Server/Spring Boot
spring boot // oauth 네이버 로그인 api
Jaybon
2020. 8. 4. 15:07
OAuth + 일반로그인
https://ondolroom.tistory.com/646
페이스북 OAuth 로그인
https://ondolroom.tistory.com/651
네이버 OAuth 로그인 참고 사이트
https://for1123.tistory.com/24
참고
구글, 페이스북은 키 - 밸류 형태
네이버는 제이슨 형식이다
application.yml
기본프로바이더에 네이버가없기 때문에 프로바이더를 추가해준다
NaverUserInfo.java
기존 OAuth2UserInfo 커스텀과 다르게 attributes안에서 response를 가져와야한다
가져오는 함수를 하나 만들어서 리턴한다
PrincipalOauth2UserService.java
네이버를 추가해준다
---
테스트
---
DB
정상적으로 입력됨
----------
코드
application.yml
더보기
server:
port: 8080
servlet:
context-path: /
encoding:
charset: utf-8
enabled: true
force: true
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
username: cos
password: cos1234
# mvc:
# view:
# prefix: /templates/
# suffix: .mustache
jpa:
hibernate:
ddl-auto: update #create update none 3개 중 하나 선택 , 개발이 끝나면 none
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
show-sql: true
security:
oauth2:
client:
registration:
google: # /oauth2/authorization/google가 이 주소를 동작하게 한다
client-id: 키
client-secret: 키
scope:
- email
- profile #스코프는 회사마다 다르기 때문에 적음
facebook:
client-id: 키
client-secret: 키
scope:
- email
- public_profile
naver:
client-id: 키
client-secret: 키
redirect-uri: http://localhost:8080/login/oauth2/code/naver
authorization-grant-type: authorization_code
scope:
- email
- profile_image
provider:
naver:
authorization-uri: https://nid.naver.com/oauth2.0/authorize
token-uri: https://nid.naver.com/oauth2.0/token
user-info-uri: https://openapi.naver.com/v1/nid/me
user-name-attribute: response
SecurityConfig.java
더보기
package com.jaybon.securityEx01.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.authentication.UserServiceBeanDefinitionParser;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.user.OAuth2User;
import com.jaybon.securityEx01.config.oauth.PrincipalOauth2UserService;
@Configuration // IoC 빈(bean, 인스턴스)을 등록
@EnableWebSecurity // 필터 체인 관리 시작 어노테이션
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) // 컨트롤러 접근 전에 낚아챔, 특정 주소 접근시 권한 및 인증 미리체크
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private PrincipalOauth2UserService principalOauth2UserService;
@Bean // IoC에 등록되어 컨피그가 호출될 때 생성, 메서드를 IoC하는 방법
public BCryptPasswordEncoder enc() { // 마땅히 둘 곳이 없어서 둔 것 Controller를 제외한 곳에 둠
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/admin/1");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // csrf 비활성화
http.authorizeRequests()
.antMatchers("/user/**").authenticated()// authenticated() 인증
// .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')") // access() 권한 .hasAnyRole()
// .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN') and hasRole('ROLE_USER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login") // 인증이 필요한 곳에 /login 으로 리다이렉트
.loginProcessingUrl("/loginProc") // 필터체인에서 인지하고 있다가 시큐리티가 낚아채서 Authentication Manager
.defaultSuccessUrl("/") // 성공하면 해당 주소로 이동 / 슬래시만 달면 이전 주소로 이동
.and()
.oauth2Login()
.loginPage("/login")
.userInfoEndpoint()
.userService(principalOauth2UserService);
}
}
NaverUserInfo.java
더보기
package com.jaybon.securityEx01.config.oauth.provider;
import java.util.Map;
public class NaverUserInfo implements OAuth2UserInfo{
private Map<String, Object> attributes;
public NaverUserInfo(Map<String, Object> attrubutes) {
this.attributes = attrubutes;
}
private Map<String, Object> ofNaver(Map<String, Object> attributes) {
Map<String, Object> response = (Map<String, Object>) attributes.get("response");
return response;
}
@Override
public String getProviderId() {
return (String) ofNaver(attributes).get("id");
}
@Override
public String getProvider() {
return "naver";
}
@Override
public String getEmail() {
return (String) ofNaver(attributes).get("email");
}
@Override
public String getName() {
return (String) ofNaver(attributes).get("name");
}
}
PrincipalOauth2UserService.java
더보기
package com.jaybon.securityEx01.config.oauth;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import com.jaybon.securityEx01.config.auth.PrincipalDetails;
import com.jaybon.securityEx01.config.oauth.provider.FacebookUserInfo;
import com.jaybon.securityEx01.config.oauth.provider.GoogleUserInfo;
import com.jaybon.securityEx01.config.oauth.provider.NaverUserInfo;
import com.jaybon.securityEx01.config.oauth.provider.OAuth2UserInfo;
import com.jaybon.securityEx01.model.User;
import com.jaybon.securityEx01.repository.UserRepository;
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService{
@Autowired
private UserRepository userRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest); // 토큰값으로 자원을 받아오는 함수
// 1번: oAuth2User의 정보를 principalDetails에 넣어주면 된다.
// 2번: PrincipalDetails를 리턴한다.
System.out.println("userRequest"+userRequest.getAccessToken().getTokenValue()); // 코드 토큰 유저정보
System.out.println("userRequest"+userRequest.getClientRegistration()); // 코드 토큰 유저정보
System.out.println(oAuth2User); // 토큰을 통해 응답받은 회원정보
try {
} catch (Exception e) {
// TODO: handle exception
}
return processOAuth2User(userRequest, oAuth2User); // 이때 세션에 등록된다 OAuth2User
}
private OAuth2User processOAuth2User(OAuth2UserRequest userRequest, OAuth2User oAuth2User) {
// 일반적으로는 로그인할 때 유저정보 User
// 1번 : OAuth2로 로그인할 때 유저정보 oAuth2User.getAttributes() <- 구성해야됨
// 2번 : DB에 이 사람있나?
// 있으면? -> 업데이트(구글에서 바뀐정보가 있을 수 있으니 확인 없이 무조건 업데이트)
// 없으면? -> 인서트(회원가입)
// 리턴을 PrincipalDetails를 리턴
OAuth2UserInfo oAuth2UserInfo = null;
if(userRequest.getClientRegistration().getRegistrationId().equals("google")) {
oAuth2UserInfo = new GoogleUserInfo(oAuth2User.getAttributes());
} else if(userRequest.getClientRegistration().getRegistrationId().equals("facebook")) {
oAuth2UserInfo = new FacebookUserInfo(oAuth2User.getAttributes());
} else if(userRequest.getClientRegistration().getRegistrationId().equals("naver")) {
oAuth2UserInfo = new NaverUserInfo(oAuth2User.getAttributes());
} else {
System.out.println("구글, 페이스북, 네이버만 가능");
}
Optional<User> userOptional
= userRepository.findByProviderAndProviderId(oAuth2UserInfo.getProvider(), oAuth2UserInfo.getProviderId());
User user;
if(userOptional.isPresent()) {
user = userOptional.get();
// 아이디가 있어도 update 해줘야 함
} else {
user = User.builder()
.username(oAuth2UserInfo.getProvider()+"_"+oAuth2UserInfo.getProviderId())
.email(oAuth2UserInfo.getEmail())
.role("ROLE_USER")
.provider(oAuth2UserInfo.getProvider())
.providerId(oAuth2UserInfo.getProviderId())
.build();
userRepository.save(user);
}
return new PrincipalDetails(user, oAuth2User.getAttributes());
}
}