4. Handler 구현

이용준·2023년 9월 30일
0

JWT

목록 보기
4/4

1. 인증 성공 핸들러

  • 성공 이후 처리 담당

1. 개발자 되기

  • OAuth2SuccessHandler (272p)
@RequiredArgsConstructor
@Component
public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
  public static final String REFRESH_TOKEN_COOKIE_NAME = "refresh_token";
  ...
  
  private final TokenProvider tokenProvider;
  private final RefreshTokenRepository refreshTokenRepository;
  private final OAuth2AuthorizationRequestBasedOnCookieRepository authorizationRequestRepository;
  private final UserService userService;
  
  @Override
  public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException{
    OAuth2User oAuthUser = (OAuth2User) authentication.getPrincipal();
    User user = userService.findByEmail((String) oAuth2User.getAttributes().get("email"));
    
    // 1. 리프레시 토큰 생성 -> 저장 -> 쿠키에 저장
    String refreshToken = tokenProvider.generateToken(user, REFRESH_TOKEN_DURATION);
    saveRefreshToken(user.getId(), refreshToken); // DB에 유저 아이디와 토큰 저장
    
    // 2. 액레스 토큰 생성 -> 패스에 액세스 토큰 추가
    String accessToken = tokenProvider.generateToken(user, ACCESS_TOKEN_DURATION);
    String targetUrl = getTargetUrl(accessToken);
    
    // 3. 인증 관련 설정값, 쿠키 제거
    clearAuthenticationAttributes(request, response); // 임시 저장된 인증 데이터 제거
    
    // 4. 리다이렉트
    getRedirectStrategy().sendRedirect(request, response, targetUrl); // 2로 리다이렉트
    
  }
  
  // 생성된 리프레시 토큰 전달받아 DB 저장
  private void savedRefreshToken(Long userId, String newRefreshToken){
    RefreshToken refreshToken = refreshTokenRepository.findByUserId(userId)
      .map(entity -> entity.update(newRefreshToken))
      .orElse(new RefreshToken(userId, newRefreshToken));
      
    refreshTokenRepository.save(refreshToken);
  }
  
  // 생성된 리프레시 토큰을 쿠키에 저장
  private void addRefreshTokenToCookie(HttpServletRequst request, HttpServletResponse response, String refreshToken){
    int cookieMaxAge = (int) REFRESH_TOKEN_DURATION.toSeconds();
    
    CookieUtil.deleteCookie(request, response, REFRESH_TOKEN_COOKIE_NAME);
    CookieUtil.addCookie(response, REFRESH_TOKEN_COOKIE_NAME, refreshToken, cookieMaxAge);
  }
  
  // 인증 관련 설정값, 쿠키 제거
  private void clearAuthenticationAttributes(HttpServletRequest request, HttpServletResponse response){
    super.clearAuthenticationAttributes(request);
    authorizationRequestRepository.removeAuthorizationRequestCookies(request, response); //OAuth 인증 위해 저장된 정보 삭제
  }
  
}
  • WebOAuth2SecurityConfig
@RequiredArgsConstrucotr
@Configuration
public class WebOAuth2SecurityConfig{
  private final OAuth2UserCustomerService oAuth2UserCustomService;
  private final TokenProvider tokenProvider;
  private final RefreshTokenRepository refreshTokenRepository;
  private final UserService userService;
  
  ...
  
  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
    ...
    http.oauth2Login()
        .login("/login")
        .authorizationEndpoint()
        .authorizationRequestRepository(oAuth2AuthorizationRequestBasedOnCookieRepository())
        .and()
        .successHandler(oAuth2SuccessHandler())
        .userInfoEndPoint()
        .userService(oAuth2UserCustomService);
	
	http.logout()
	    .logoutSuccessful("/login");
	...
  }
  
  @Bean
  public OAuth2SuccessHandler oAuth2SuccessHandler(){
    return new OAuth2SuccessHandler(tokenProvider, refreshTokenRepository, oAuth2AuthorizationRequestBasedOnCookieRepository(), userService);
  }
}

2. 코드로 배우는 스프링부트

  • ClubLoginSuccessHandler(551p)
@Log4j2
public class ClubLoginSuccessHandler implements AuthenticationSuccessHandler{
  private RedirectStategy redirectStrategy = new DefaultRedirectStrategy();
  
  public ClubLoginSuccessHandler(PasswordEncoder passwordEncoder){
    this.passwordEncoder = passwordEncoder;
  }
  
  @Overrid
  public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException{
  
    ClubAuthMenberDTO authMember = (ClubAuthMemberDTO) authentication.getPrincipla();
    boolean fromSocial = authMember.isFromSocial();
    log.inco(" 회원 정보 수정 필요 여부 : "+fromSocial);
    
    boolean passwordResult = passwordEncoder.matches("1111", authMember.getPassword());
    
    if(fromSocial && passwordResult){
      redirectyStrategy.sendRedirect(request, response, "/member/modify?from=social");
    }
  
  }

}

대상 URL을 다르게 지정하는 용도

  • (적용) SecurityConfig
public class SecurityConfig{
  ...
  
  @Bean
  public ClubLoginSuccessHandler successHandler(){
    return new ClubLoginSuccessHandler(passwordEncoder);
  }
}

2. 인증 실패 핸들러

  • ApiLoginFailHandler
public class ApiLoginFailHandler implements AuthenticationFailureHandler{

  @Override
  public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException{
  log.info(exception.getMessage());
  
  response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
  
  // json 리턴
  response.setContentType("application/json; charset=utf-8");
  JSONObject json = new JSONObject();
  String message = exception.getMessage();
  json.put("code", "401");
  json.put("message", message);
  
  PrintWriter out = response.getWriter();
  out.print(json);
  }
}
  • 인증 실패시 '401' 상태 코드 반환
  • SecurityConfig 적용
  • SecurityConfig
public class SecurityConfig{

  ... 
  
  ApiLoginFilter apiLoginFilter = new ApiLoginFilter("/api/login");
  apiLoginFilter.setAuthenticationManager(authenticationManager());
  
  apiLoginFilter.setAuthenticationFailureHandler(new ApiLoginFailHnadler());
  
  return apiLoginFilter
}
profile
뚝딱뚝딱

0개의 댓글