@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request.getRequestURI().startsWith(NO_CHECK_URL)) {
filterChain.doFilter(request, response);
return;
}
String refreshToken = jwtService.extractRefreshToken(request)
.filter(jwtService::isTokenValid)
.orElse(null);
if (refreshToken != null) {
checkRefreshTokenAndReIssueAccessToken(response, refreshToken);
return;
}
if (refreshToken == null) {
checkAccessTokenAndAuthentication(request, response, filterChain);
}
}
먼저 NO_CHECK_URL에 필터를 거치지않을 url을 설정해야한다.
설정한 URL이 들어올 경우filterChain.doFilter(request, response);
로 다음필터를 호출한다.return
해 주는이유는 밑으로 내려가 진행되기 때문.
사용자 요청 헤더에서 RefreshToken을 추출하고 없거나 유효하지않으면 null반환.
RefreshToken이 존재한다면 DB의 RefreshToken과 일치하는지 확인 후
AccessToken
을 재 발급해 준다.
RefreshToken이 존재하지않을경우, AccessToken을 검사하고 인증처리 로직 수행.
AccessToken이 없거나 유효하지 않다면 403에러발생하고
유효하다면 인증 객체가 담긴 상태로 다음 필터로 넘어가기 때문에 인증 성공.
public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, String refreshToken) {
memberRepository.findByRefreshToken(refreshToken)
.ifPresent(member -> {
String reIssuedRefreshToken = reIssueRefreshToken(member);
jwtService.sendAccessAndRefreshToken(response, jwtService.createAccessToken(member.getEmail()),
reIssuedRefreshToken);
});
}
파라미터로 들어온 헤더에서 refreshToken으로 DB에서 회원을 찾고, 존재하면 AccessToken을 생성하고 reIssueRefreshToken()메서드로 리프레시 토큰
재발급 & 업데이트.
private String reIssueRefreshToken(Member member) {
String reIssuedRefreshToken = jwtService.createRefreshToken();
member.updateRefreshToken(reIssuedRefreshToken);
memberRepository.saveAndFlush(member);
return reIssuedRefreshToken;
}
public void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
log.info("checkAccessTokenAndAuthentication() 호출");
jwtService.extractAccessToken(request)
.filter(jwtService::isTokenValid)
.ifPresent(accessToken -> jwtService.extractEmail(accessToken)
.ifPresent(email -> memberRepository.findByEmail(email)
.ifPresent(this::saveAuthentication)));
filterChain.doFilter(request, response);
}
- request에서 AccessToken을 추출한 후, 유효성검사를 진행.
- 유효하면, Email을 추출하여 유저를 찾음.
- 그 유저를 saveAuthentication()으로 인증 처리.
- 인증 허가 처리된 객체를 SecurityContextHolder에 담기
- 그 후 다음 인증 필터로 진행
public void saveAuthentication(Member myMember) {
String password = myMember.getPassword();
if (password == null) {
password = PasswordUtil.generateRandomPassword();
}
UserDetails userDetailsUser = org.springframework.security.core.userdetails.User.builder()
.username(myMember.getEmail())
.password(password)
.roles(myMember.getRole().name())
.build();
Authentication authentication =
new UsernamePasswordAuthenticationToken(userDetailsUser, null,
authoritiesMapper.mapAuthorities(userDetailsUser.getAuthorities()));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
- 비밀번호 임의 설정하여 인증되도록 함.
- 빌더의 유저 : UserDetailsUser 객체
- credential(보통 비밀번호로, 인증 시에는 보통 null로 제거)