Spring Security 3
in Spring on Spring Security
추가 내용 및 수정 사항
원래 jwt 유효성 검사를 인터셉터에서 진행했었는데, 권한 검사랑 코드가
꼬여서 필터로 재구성하였다.
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {
// 제외할 경로 추가.
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return (
new AntPathMatcher().match("/user/login", request.getServletPath()) ||
new AntPathMatcher().match("/user/join", request.getServletPath())
);
}
// Token Check
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("찍혀1?");
try {
log.info("찍혀2?");
// Access Token
String header = request.getHeader(AuthConstants.AUTH_HEADER);
String token = JwtUtils.getTokenFromHeader(header);
// Refresh Token
String reHeader = request.getHeader(AuthConstants.RE_AUTH_HEADER);
String reToken = JwtUtils.getTokenFromHeader(reHeader);
if (JwtUtils.isValidToken(token)) {
// 권한 검사.
checkAuthorization(token);
chain.doFilter(request, response);
} else if (!JwtUtils.isValidToken(token) && JwtUtils.isValidRefreshToken(reToken)) {
// Access Token 재발급
UserVo userVo = UserVo.builder()
.type(JwtUtils.getClaimsCodeFromToken(reToken))
.id(JwtUtils.getIdFromToken(reToken))
.email(JwtUtils.getUserEmailFromToken(reToken))
.userSeq(JwtUtils.getClaimsSeqFromToken(reToken))
.build();
String newToken = JwtUtils.generateJwtToken(userVo);
String newAccessToken = AuthConstants.TOKEN_TYPE + " " + newToken;
response.setHeader(AuthConstants.AUTH_HEADER, newAccessToken);
response.setHeader(AuthConstants.RE_AUTH_HEADER, AuthConstants.TOKEN_TYPE + " " + reToken);
// 권한 검사.
checkAuthorization(newToken);
chain.doFilter(request, response);
} else {
chain.doFilter(request, response);
}
} catch (NullPointerException e) {
chain.doFilter(request, response);
}
}
// 권한 검사 로직
public void checkAuthorization(String token) {
String authCode = JwtUtils.getClaimsCodeFromToken(token);
switch (authCode) {
case "1":
authCode = "ROLE_ADMIN";
break;
case "2":
authCode = "ROLE_USER";
break;
}
CustomUserDetail userDetail = new CustomUserDetail(UserVo.builder().type(authCode).build());
Authentication authentication =
new UsernamePasswordAuthenticationToken(null, null, userDetail.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
OncePerRequestFilter
특정 필터가 요청당 한 번만 호출되도록 할 수 있다. Spring Security로 작업할 때 일반적으로 사용한다.
요청이 Filter Chain을 통과할 때 일부 인증작업을 요청에 대해 한 번만 발생하기를 원할 경우,
OncePerRequestFilter를 상속받아서 커스텀하면 된다. Spring은 주어진 요청에 대해 OncePerRequestFilter가
한 번만 실행되도록 보장해준다. 나의 경우에도 토큰에 대한 유효성 검사와 요청에 대한 권한 검사는
클라이언트로 부터 요청이 들어올 때 딱 한번만 실행하면 된다고 판단했다.
shouldNotFilter 메서드
특정 요청에 대해서, 해당 필터를 적용하지 않고 싶을 때 사용하는 메서드.
OncePerRequestFilter를 상속받아 정의하면 된다.
제외해야 할 경로를 추가하는 방법은 아래와 같다.
// 하나의 경우
return new AntPathMatcher().match("/test", request.getServletPath());
// 하나 이상일 경우
return (
new AntPathMatcher().match("/test1", request.getServletPath()) ||
new AntPathMatcher().match("/test2", request.getServletPath());
)
아래는 match()
메서드이다. 해당 메서드는 내부적으로 doMatch()
메서드를 호출한다.
match()
@Override
public boolean match(String pattern, String path) {
return doMatch(pattern, path, true, null);
}
doMatch()
protected boolean doMatch(String pattern, @Nullable String path, boolean fullMatch,
@Nullable Map<String, String> uriTemplateVariables) {...}