Spring 웹 Form Login과 OAuth2 Login을 위한 Spring Security 구현하기
지난 포스팅에서 Form Login Spring Security를 완료하고,
이번 포스팅에서는 Google OAuth2 Login을 사용하여 Spring Security 설정을 마무리한다.
Form Login 설정은 https://velog.io/@sorayoo/Spring-Security-Form-Login-OAuth2-Login-1 에서 확인할 수 있다.
Google API console(https://console.cloud.google.com/apis/)에서 새 프로젝트 생성
설정시 OAuth2 동의 화면(메뉴)에서 사용자 인증 정보 URI를
http://localhost:8080/login/oauth2/code/google
로 작성하여야 한다.
프로젝트 설정 내용에서 client ID와 client secret을 복사해놓는다.
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
application.yml
spring:
security:
oauth2:
client:
registration:
google:
clientId: ***
clientSecret: ***
scope:
- email
- profile
loginForm.html
<a href="/oauth2/authorization/google">구글 로그인</a>
/oauth2/authorization/google이다.SpringConfig.java
http
...
.and()
.oauth2Login()
.loginPage("/loginForm");
로그인 진행 순서는 아래와 같다.
- 구글 로그인
- 인증 코드 받기
- (권한이 담긴) 액세스 토큰을 받고
- 유저의 프로필 정보를 가져온다.
- 이후 자동 회원가입 / 로그인
후처리 함수(service) 생성 후에 이를 SecurityConfig에 추가해준다.
PrincipalOauth2UserService.java
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService{
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
System.out.println("userRequest: "+userRequest.getClientRegistration());
System.out.println("userRequest: "+userRequest.getAccessToken().getTokenValue());
System.out.println("userRequest: "+userRequest.getClientRegistration());
System.out.println("userRequest: "+super.loadUser(userRequest).getAttributes());
return super.loadUser(userRequest);
}
}
loadUser()는 구글로부터 받은 userRequest 데이터를 후처리하는 함수SecurityConfig.java
@Autowired
private PrincipalOauth2UserService principalOauth2UserService;
...
@Override
protected void configure(HttpSecurity http) throws Exception {
http
...
.and()
.oauth2Login()
.loginPage("/loginForm")
.userInfoEndpoint()
.userService(principalOauth2UserService);
}
PrincipalOauth2UserService를 autowired 해주고, userService로 지정해준다.웹에서 구글 로그인 후 userRequest 정보가 잘 받아와지는지 print하여 확인한다.
super.loadUser(userRequest).getAttribute()
{sub=***, // 구글 id(key)
name=***,
given_name=***,
family_name=***,
picture=http://******,
email=***@gmail.com,
email_verified=true,
locale=ko}
OAuth2 가입 유저의 경우, 가입 루트 표기를 위해 Member Domain에 provider와 providerId를 추가해준다.
Member.class
...
private String provider;
private String providerId;
...
구글 로그인 후 유저 정보를 잘 받아오는지 테스트
IndexController.java
@GetMapping("/test/oauth/login")
public @ResponseBody String testOauthLogin(
Authentication authentication,
@AuthenticationPrincipal OAuth2User oauth
) {
System.out.println("=================/test/oauth/login");
OAuth2User oauth2user = (OAuth2User) authentication.getPrincipal();
System.out.println("oauth2user: "+oauth2user.getAttributes());
System.out.println("oauth: "+oauth.getAttributes());
return "authentication";
}
Authentication 내의 유저 정보가 OAuth2User 객체로 담겨있다. UserDetails 객체로 유저 정보가 넘어온다.
OAuth2User와UserDetails를PrincipalDetails로 implements하여 통합해야한다. (Form Login에서 미리 작성해놓은PrincipalDetails.class를 수정하며 진행한다.)
UserDetails와 OAuth2User를 implements하는 PrincipalDetails를 만든다.
PrincipalDetails.java
public class PrincipalDetails implements UserDetails, OAuth2User {
...
@Override
public Map<String, Object> getAttributes() {
return attributes;
}
@Override
public String getName() {
return null;
}
}
OAuth2User를 추가OAuth2User의 메소드인 getAttributes()와 getName()을 추가OAuth2로 접속한 유저의 자동 회원가입 로직 추가
PrincipalOauth2UserService.java
@Service
public class PrincipalOauth2UserService extends DefaultOAuth2UserService{
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
private MemberRepository memberRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
System.out.println("userRequest: "+userRequest.getClientRegistration());
System.out.println("userRequest: "+userRequest.getAccessToken().getTokenValue());
System.out.println("userRequest: "+userRequest.getClientRegistration());
System.out.println("userRequest: "+super.loadUser(userRequest).getAttributes());
OAuth2User oAuth2User = super.loadUser(userRequest);
String provider = userRequest.getClientRegistration().getClientId();
String providerId = oAuth2User.getAttribute("sub");
String username = provider + "_" + providerId;
String password = bCryptPasswordEncoder.encode("****");
String email = oAuth2User.getAttribute("email");
Member findOne = memberRepository.findByUsername(username);
Member member = new Member();
if (findOne == null) {
System.out.println("최초 구글 로그인 / 가입");
member.setUsername(username);
member.setPassword(password);
member.setProvider(provider);
member.setProviderId(providerId);
memberRepository.save(member);
return new PrincipalDetails(member, oAuth2User.getAttributes());
} else {
System.out.println("이미 존재하는 회원입니다.");
return new PrincipalDetails(findOne, oAuth2User.getAttributes());
}
}
}
PrincipalDetails 리턴@AuthenticationPrincipal이 생성된다.