UsernamePasswordAuthenticationFilter/**
* URL 이 /login 으로 넘어올 경우 spring security 에서 자동으로 attemptAuthentication() 으로 보내줌
* attemptAuthentication() 에서 유저 정보를 확인 후 성공하면 successfulAuthentication()
* 실패할 경우 unsuccessfulAuthentication() 으로 자동 이동 시켜줌
*/
@Slf4j
@Setter
@Component
public class UserAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final String METHOD_NAME = "UserAuthenticationFilter";
private UserAuthenticationManager userAuthenticationManager;
private JwtTokenProvider jwtTokenProvider;
private TokenRepository tokenRepository;
private String headerKeyAccess;
private String headerKeyRefresh;
private String typeAccess;
private String typeRefresh;
@Autowired
public UserAuthenticationFilter(UserAuthenticationManager userAuthenticationManager, JwtTokenProvider jwtTokenProvider, TokenRepository tokenRepository) {
super(userAuthenticationManager);
this.userAuthenticationManager = userAuthenticationManager;
this.jwtTokenProvider = jwtTokenProvider;
this.tokenRepository = tokenRepository;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
log.info(METHOD_NAME + "- attemptAuthentication() ...");
try {
Employee employee = new ObjectMapper().readValue(request.getInputStream(), Employee.class);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(employee.getEmpNo(), employee.getPassword());
return userAuthenticationManager.authenticate(authenticationToken);
} catch (IOException ie) {
log.error("유저 정보를 읽지 못했습니다. " + METHOD_NAME, ie);
unsuccessfulAuthentication(request, response, ie);
} catch (NullPointerException ne) {
log.error("받은 유저 정보가 비어 있습니다. " + METHOD_NAME, ne);
unsuccessfulAuthentication(request, response, ne);
} catch (Exception e) {
log.error("SERVER ERROR " + METHOD_NAME, e);
unsuccessfulAuthentication(request, response, e);
}
log.error("자격 증명에 실패하였습니다. " + METHOD_NAME);
return null;
}
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain, Authentication authResult) throws ServletException {
log.info(METHOD_NAME + "- successfulAuthentication() ...");
try {
String principal = String.valueOf(authResult.getPrincipal());
Token token = tokenRepository.findByEmpNo(principal);
if (token != null) {
log.info("Token Set Existed - Token issuance");
CommonTokenDTO commonTokenDTO = jwtTokenProvider.generateToken(principal);
if (!jwtTokenProvider.updateRefresh(commonTokenDTO.getReIssuanceTokenDTO()))
log.warn("Token Set Update to Token Repository - Fail");
response.addHeader(headerKeyAccess, typeAccess + commonTokenDTO.getAccessToken());
response.addHeader(headerKeyRefresh, typeRefresh + commonTokenDTO.getReIssuanceTokenDTO().getRefreshToken());
} else {
log.info("First Login User - Token issuance");
CommonTokenDTO commonTokenDTO = jwtTokenProvider.generateToken(principal);
if (!jwtTokenProvider.saveRefresh(commonTokenDTO.getReIssuanceTokenDTO()))
log.warn("Token Set Save to Token Repository - Fail");
response.addHeader(headerKeyAccess, typeAccess + commonTokenDTO.getAccessToken());
response.addHeader(headerKeyRefresh, typeRefresh + commonTokenDTO.getReIssuanceTokenDTO().getRefreshToken());
}
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(new ResponseHandler().convertResult(HttpStatus.OK, Payload.SIGN_IN_OK));
} catch (IOException ie) {
log.error("유저 정보를 읽지 못했습니다. " + METHOD_NAME, ie);
} catch (NullPointerException ne) {
log.error("받은 유저 정보가 비어 있습니다. " + METHOD_NAME, ne);
} catch (Exception e) {
log.error("SERVER ERROR " + METHOD_NAME, e);
}
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException failed) throws ServletException {
log.info(METHOD_NAME + "- unsuccessfulAuthentication() ...");
try {
String message = new UserLoginFailureHandler().onAuthenticationFailure(failed);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(new ResponseHandler().convertResult(HttpStatus.BAD_REQUEST, Payload.SIGN_IN_FAIL + message));
} catch (IOException ie) {
log.error("전달받은 정보를 읽지 못했습니다. " + METHOD_NAME, ie);
} catch (Exception e) {
log.error("SERVER ERROR " + METHOD_NAME, e);
}
}
public void unsuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
Exception exception) {
log.info(METHOD_NAME + "- unsuccessfulAuthentication() ...");
try {
SecurityContextHolder.clearContext();
String message = new UserLoginFailureHandler().onAuthenticationFailure(exception);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(new ResponseHandler().convertResult(HttpStatus.BAD_REQUEST, Payload.SIGN_IN_FAIL + message));
} catch (IOException ie) {
log.error("전달받은 정보를 읽지 못했습니다. " + METHOD_NAME, ie);
} catch (Exception e) {
log.error("SERVER ERROR " + METHOD_NAME, e);
}
}
}
BasicAuthenticationFilter/**
* 제외 지정한 URL 이 아닌 모든 URL 이 인가된 Token 을 보유하고 있는지 검증하는 클래스
* 액세스 토큰 보유를 확인해서 올바른 토큰을 보유하고 있는 경우 doFilter 를 사용하여 다음 필터로 패스
* 토큰이 올바르지 않을 경우 Fail Response 를 보냄
* 리프레쉬 토큰을 보유한 경우 리프레쉬 토큰이 올바르면 액세스 토큰을 다시 리턴해 줌
*/
@Slf4j
@Setter
@Component
public class JwtTokenAuthorizationFilter extends BasicAuthenticationFilter {
private static final String METHOD_NAME = "JwtTokenAuthorizationFilter";
private final JwtTokenProvider jwtTokenProvider;
private final PrincipalDetailService principalDetailService;
private String headerKeyAccess;
private String typeAccess;
@Autowired
public JwtTokenAuthorizationFilter(UserAuthenticationManager userAuthenticationManager, JwtTokenProvider jwtTokenProvider, PrincipalDetailService principalDetailService) {
super(userAuthenticationManager);
this.jwtTokenProvider = jwtTokenProvider;
this.principalDetailService = principalDetailService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.info(METHOD_NAME + "- doFilterInternal() ...");
try {
TokenResDTO tokenResDTO = jwtTokenProvider.requestCheckToken(request);
String token = tokenResDTO.getToken();
switch (tokenResDTO.getCode()) {
case 0:
if (jwtTokenProvider.validateToken(token)) {
log.info("Access Token Validation - Success");
String userPk = jwtTokenProvider.getUserPk(token);
UserDetails userDetails = principalDetailService.loadUserByUsername(userPk);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
} else {
log.info("Access Token Validation - Fail");
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(new ResponseHandler().convertResult(HttpStatus.BAD_REQUEST, Payload.ACCESS_FAIL + Payload.TOKEN_FAIL));
}
return;
case 1:
if (jwtTokenProvider.validateRefreshToken(token)) {
log.info("Refresh Token Validation - Success");
String accessToken = jwtTokenProvider.generateAccessToken(jwtTokenProvider.getUserPk(token));
response.addHeader(headerKeyAccess, typeAccess + accessToken);
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(new ResponseHandler().convertResult(HttpStatus.OK, Payload.TOKEN_OK));
} else {
log.info("Refresh Token Validation - Fail");
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(new ResponseHandler().convertResult(HttpStatus.BAD_REQUEST, Payload.ACCESS_FAIL + Payload.TOKEN_FAIL));
}
return;
case 2:
default:
log.warn("Access/Refresh Token Validation - Fail");
}
} catch (NullPointerException ne) {
log.error("토큰 값이 비어있습니다. " + METHOD_NAME);
} catch (Exception e) {
log.error("사용자 인증을 확인하지 못해 인가할 수 없습니다. " + METHOD_NAME, e);
}
filterChain.doFilter(request, response);
}
}