스프링 boot 6에서 직접 jwt token filter를 만들때의 주의점
-
정체도 알 수 없는 패키지를 임포트하고 싶지 않아 JwtTokenFilter를 직접 구현했다
-
org.springframework.web.filter.OncePerRequestFilter
를 상속받아,doFilterInternal
을 구현하고, 인증이 되었다면SecurityContextHolder
에 authentication을 추가해주면 되는 간단한 작업이었다.-
authentication은
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
을 사용했다UsernamePasswordAuthenticationToken(userDetails, null)`
-
-
그러나 403 forbidden만 뜨면서 진행이 안된다
-
수많은 페이지를 찾아보고, 오랜시간 step into를 눌러가면서 확인한 결과, authentication이 granted false 되는것을 확인했다.
org/springframework/security/web/access/intercept/AuthorizationFilter.java:95 try { AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request); ... --- inspector decision = AuthorizationDecision [granted=false]
-
원인을 찾아보니, UsernamePasswordAuthenticationToken의 생성자에 숨어 있었다.
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) { super(null); this.principal = principal; this.credentials = credentials; setAuthenticated(false); } public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; this.credentials = credentials; super.setAuthenticated(true); // must use super, as we override } public static UsernamePasswordAuthenticationToken authenticated(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { return new UsernamePasswordAuthenticationToken(principal, credentials, authorities); }
- 마지막의 authorities가 없이 호출한다면
isAuthenticated = false
로 설정되어서 진행이 안된 것이었다.- 참고로 외부에서 함부로 setAuthenticated를 호출한다면 아래와 같은 로그 메시지를 받는다
Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead org/springframework/security/authentication/UsernamePasswordAuthenticationToken.java:112 @Override public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); super.setAuthenticated(false); }
- 마지막의 authorities가 없이 호출한다면
-
-
UsernamePasswordAuthenticationToken은 authorities를 함께 넣어주어야 하는구나. 스프링의 세계는 참 신묘하고 깊다는 것을 알게되었다…. 가 아니지. 이 무슨 개판인 구현인지 모르겠다.
- authenticationToken이 사실은 authenticated되지 않았다니!
- 일반적으로 authenticationToken을 만들었다면, authenticated된 것으로 간주할 것이라고 생각하지 않을까?
- 물론 UsernamePasswordAuthenticationToken의 주석에 명시되어 있긴 하지만, 모든 함수 하나하나 주석을 읽어볼 수는 없는 노릇 아닌가
- 이름이라도 특별하게 만들어 놓던지
- 차라리 생성자 없이 static 함수로만 구현되어 있었다면, 오히려 괜찮았을 것이다.
- 회사에서 자주 “이거 이름이랑 동작이 조금 달라요. 놀람 최소화 원칙에 따라 이름이나 구현을 조금 바꿔주세요"라고 했는데, UsernamePasswordAuthenticationToken에 비하면 애교 수준이었던 것 같다.
- authenticationToken이 사실은 authenticated되지 않았다니!