개발/Spring

Spring boot,nuxt(vue) oauth2 security token기반 소셜 로그인(카카오,네이버,구글,페이스북,로컬)

JangHC 2022. 3. 13. 10:53

참조 : https://deeplify.dev/back-end/spring/oauth2-social-login

 

 

[Spring Boot] OAuth2 소셜 로그인 가이드 (구글, 페이스북, 네이버, 카카오)

스프링부트를 이용하여 구글, 페이스북, 네이버, 카카오 OAuth2 로그인 구현하는 방법에 대해서 소개합니다.

deeplify.dev

 

Spring boot로 소셜 로그인을 구현하면서 했던 내용들을 정리하겠습니다.

너무 잘 정리해주셔서 참고하였고, 이를 기반으로 어떤식으로 token을 주고 받으며 API를 사용할 수 있을지 나름대로 

구현하면서 정리했습니다.

 

GIT :

https://github.com/wkdgudcjf/social_example.git

 

GitHub - wkdgudcjf/social_example: 소셜 로그인 example

소셜 로그인 example. Contribute to wkdgudcjf/social_example development by creating an account on GitHub.

github.com

 

백엔드에서 신경 쓸 부분은 api 패키지 내부만 본인이 구현하면 됩니다.

Token 유지와, Refresh만 제대로 한다면 인증을 신경쓰지 않고 구현하므로 매우 편합니다.

 

본 예제에서는 Token을 따로 저장하지 않고, RefreshToken만 httpOnly로 쿠키에 저장합니다.

만약 클라이언트에서 새로고침이나 주소 변경시에도 token을 유지하고 싶다면, 로컬에 저장하거나 쿠키에 저장하여 사용하시길 바랍니다.

 

User의 정보를 커스터마이징 하고, User 파라미터를 수정하기 위한 부분만 다뤄보려고 합니다.

 

1. 소셜 로그인

 

소셜 로그인 부분은 oauth 패키지에서 모두 처리합니다.

Spring boot oauth2는 자동으로 Controller를 만들어줍니다.

 

properties에 설정한 redirect-uri가 자동으로 생성된 Controller로 맵핑됩니다.

 

결국 모든 것이 처리되고 우리가 봐야할 곳은,

 

oauth 패키지의 service 패키지와 handler 패키지입니다.

 

 

 

CustomOAuth2UserService에서는 카카오 구글 등에서 받아온 데이터를 저장하는 곳입니다.

OAuth2AuthenticationSuccessHandler 에서는 유저의 토큰을 생성하여 등록된 front(http://localhost:3000/oauth/redirect)로 redirect합니다.

 

 

2. 일반 로그인

 

일반 로그인은 User 패키지에 AuthController 에서 합니다.

 

회원가입이 되었다고 가정하고, login시 인증 코드를 거치게 됩니다.

 

Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(
                    authReqModel.getEmail(),
                    authReqModel.getPassword()
            )
);

 

위 코드는 내부적으로 여러 경로(비밀번호 암호화 및 oauth 패키지의 CustomUserDetailsService에서 가져온 Object와 비교)를 거쳐 로그인을 하게 됩니다.

 

3. 로그인 후 Filter를 통해 인증된 유저 얻기

 

oauth에 TokenAuthenticationFilter에서는 프론트에서 보내준 Token을 가지고 user를 불러옵니다.

 

소셜로그인,일반로그인에서 만든 토큰은 이메일과 권한이 들어있습니다.

 

AuthToken accessToken = tokenProvider.createAuthToken(
         userInfo.getEmail(),
         roleType.getCode(),
         new Date(now.getTime() + appProperties.getAuth().getTokenExpiry())
 );

 

그래서 Filter에서 Token을 꺼내 유효한지 확인하고 이메일을 꺼냅니다. 

 

Filter 코드

if (token.validate()) {
     Authentication authentication = tokenProvider.getAuthentication(token);
     SecurityContextHolder.getContext().setAuthentication(authentication);
 }

 

AuthTokenProvider 코드

public Authentication getAuthentication(AuthToken authToken) {
     if(authToken.validate()) {
        Claims claims = authToken.getTokenClaims(); // 이메일이 담겨있음.
        Collection<? extends GrantedAuthority> authorities =
                Arrays.stream(new String[]{claims.get(AUTHORITIES_KEY).toString()})
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());

         log.debug("claims subject := [{}]", claims.getSubject());
         MyUserDetails myUserDetails = new MyUserDetails(userRepository.findByEmail(claims.getSubject()));
         return new UsernamePasswordAuthenticationToken(myUserDetails, authToken, authorities);
     } else {
         throw new TokenValidFailedException();
     }

}

 

위 코드에서 userRepository로 DB 접근을 하게 되는데, 만약 매번 db접근을 하지 않으려면,

MyUserDetails myUserDetails = new MyUserDetails(userRepository.findByEmail(claims.getSubject())); 이 코드를 

User principal = new User(claims.getSubject(), "", authorities);

이렇게 수정하고 MyUserArgumentResolver 에서 User로 Casting해서 return하고,

파라미터에 MyUser myUser를 User로 사용해야 합니다. 참고로 여기서 User는 Srping Security에서 제공하는 User클래스입니다.

 

Email 정보만 가지고 있기 때문에 다른 User 정보들이 필요하면 DB접근을 해야합니다.

 

모든 url 접근에 MyUser 정보(DB접근)를 모두 셋팅하는것이 비효율이라고 생각한다면, 수정하여 사용하시길 바랍니다.

 

 

 

궁금한게 있으시면 댓글 남겨주세요~