userService.loadUser(OAuth2UserRequest)를 통해 customOAuth2UserService에 진입한다.
우선, 커스텀하기 위해서는 DefaultOAuth2UserService 클래스의 loadUser() 메서드에 대해 알아야한다.
1. DefaultOAuth2UserService
-DefaultOAuth2UserService의 loadUser() 메서드는 파라미터로 OAuth2UserRequest를 받는다.
-ClientRegistration( clientId, cliendSecret, redirectURI, scopes 등의 변수를 갖고 있다.), OAuth2AccessToken, additionalParameter를 변수로 갖고있는 OAuth2UserRequest
-우선, 사용자 정보 api 주소인 userNameAttributeName을 가져온다.
-request에는 scope 등의 가져와야 할 사용자 정보를 넣는다.
-그 후 getResponse(userRequest, request) 메서드로 사용자 정보 api에 접근하여, response에 저장
-등등등 해서 DefaultOAuth2User를 return한다.
2. CustomOAuth2UserService 구현
***이 과정이 필요하다.
1. CustomOAuth2UserService에는 super.loadUser(userRequest)를 통해 이 과정을 한 번 진행한 후 DefaultOAuth2User를 받는다.
2. Google, kakao, naver 등 각각의 api는 사용자 정보를 주는 형식이 다르다. 즉, 받은 DefaultOAuth2User 객체에서 registrationId를 확인하여 분기를 만들어준다.
3. 그 후 OAuth2Response 인터페이스를 만든다.
4. OAuth2Response 형식으로 다형성을 이용한 초기화한다.(attribute들을 받아준다.)
5. 만들어진 OAuth2Response 객체를 이용하여 저장하거나 가공한다.
6. DefaultOAuth2UserService에서 OAuth2User를 구현하고 있는 DefaultOAuth2User를 return했으니, OAuth2User를 구현한 CustomOAuth2User를 만들어 return 하든가, DefaultOAuth2User를 return 해준다.
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private static final Logger log = LoggerFactory.getLogger(CustomOAuth2UserService.class);
private final UserRepository userRepository;
public CustomOAuth2UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
log.info(oAuth2User.toString());
String registrationId = userRequest.getClientRegistration().getRegistrationId();
OAuth2Response oAuth2Response = null;
if(registrationId.equals("naver")){
oAuth2Response = new NaverResponse(oAuth2User.getAttributes());
} else if (registrationId.equals("Google")) {
oAuth2Response = new GoogleResponse(oAuth2User.getAttributes());
}else{
return null;
}
String username = oAuth2Response.getProvider()+" "+oAuth2Response.getProviderId();
UserEntity existData = userRepository.findByUsername(username);
if(existData == null){
System.out.println(oAuth2Response.getEmail());
UserEntity userEntity = new UserEntity();
userEntity.setUsername(username);
userEntity.setEmail(oAuth2Response.getEmail());
userEntity.setName(oAuth2Response.getName());
userEntity.setRole("ROLE_USER");
userRepository.save(userEntity);
UserDTO userDTO = new UserDTO();
userDTO.setUsername(username);
userDTO.setName(oAuth2Response.getName());
userDTO.setRole("ROLE_USER");
return new CustomOAuth2User(userDTO);
}
else{
System.out.println(oAuth2Response.getName());
existData.setEmail(oAuth2Response.getEmail());
existData.setName(oAuth2Response.getName());
userRepository.save(existData);
UserDTO userDTO = new UserDTO();
userDTO.setUsername(existData.getUsername());
userDTO.setName(oAuth2Response.getName());
userDTO.setRole(existData.getRole());
return new CustomOAuth2User(userDTO);
}
}
}
그렇게 return 해주면 로그인 성공이다.
3. OAuth2LoginAuthenticationProvider-->OAuth2LoginAuthenticationFilter로
이제, CustomSuccessHandler를 만들 차례이다.
결국, 로그인이 성공했고 이제 OAuth2LoginAuthenticationFilter는 OAuth2AuthenticationToken을 return한다.
4. return 후 AbstractAuthenticationProcessingFilter-->CustomSuccessHandler
-return 후 doFilter() 메서드에서 인증이 성공했기 때문에, CustomSuccessHandler의 successfulAuthentication() 메서드를 실행한다.
-default로는 SimpleUrlAuthenticationSuccessHandler가 실행된다. 그냥 넘어가서 CustomSuccessHandler를 구현해보자.
5. CustomSuccessHandler 구현
***이 과정이 필요하다.
1. 인증은 완료되었으니, jwt 토큰을 생성해서 클라이언트로 보내줘야 한다.
2. Authentication 객체(사실상, OAuth2AuthenticationToken이다.)에서 principal을 꺼내 따로 저장한다.
OAuth2AuthenticationToken을 보면 OAuth2User principal이 있다.
OAuth2LoginAuthenticationProvider에서 CustomOAuth2UserService에서 return 한 OAuth2User객체(사용자 정보를 담고 있음)는
OAuth2LoginAuthenticationToken 생성 시 3번째 인자로 들어간다.
그리고 생성자에서 3번째 인자는 principal임을 볼 수 있다.
OAuth2LoginAuthenticationToken-->OAuth2AuthenticationToken으로 변환하는 과정에서 그대로 principal에는 OAuth2User객체가 들어간다.
즉, CustomSuccessHandler의 Authentication의 getPrincipal을 하면 OAuth2User 객체를 얻을 수 있다.
3. OAuth2User 객체의 사용자 정보를 활용해, JWT 토큰을 만든다.
4. username, role, 만료 시간을 넣어 JWT 토큰을 만든다.
5. 그 후, 쿠키나 헤더에 실어서 main이든 어디든 redirect해준다.
@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final JWTUtil jwtUtil;
public CustomSuccessHandler(JWTUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
CustomOAuth2User customUserDetails = (CustomOAuth2User) authentication.getPrincipal();
String username = customUserDetails.getUsername();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
GrantedAuthority authority = iterator.next();
String role = authority.getAuthority();
String token = jwtUtil.createJWT(username, role, 60*60*60L);
response.addCookie(createCookie("Authorization", token));
response.sendRedirect("http://localhost:3000/");
}
private Cookie createCookie(String name, String value) {
Cookie cookie = new Cookie(name, value);
cookie.setMaxAge(60*60*60);
cookie.setPath("/");
cookie.setHttpOnly(true);
return cookie;
}
}
============================================================================================
다음 글은 jwtUtil, jwtFilter, SecurityConfig 설정을 알아봐야겠다.
중간에 글을 쓰다가 한 번 날아갔다. 젠장....시간 겁나많이 뺐었다.
'OAuth+JWT' 카테고리의 다른 글
OAuth2.0(로그인 시도부터 CustomOAuth2UserService 전까지) (1) | 2024.10.12 |
---|