I am trying to use jwt for my spring boot project and have Spring Security setup. When I use the Internal filter which is this
@Slf4j
@RequiredArgsConstructor
public class CustomAuthorizationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
private final String tokenPrefix = "Bearer ";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
log.info("INITIALIZE | CustomAuthorizationFilter | doFilterInternal | " request.getRequestURI());
LocalDateTime initTime = LocalDateTime.now();
String token = request.getHeader(AUTHORIZATION);
if (token != null && token.startsWith(tokenPrefix)) {
jwtProvider.verifyJwt(token);
} else {
filterChain.doFilter(request, response);
}
log.info("COMPLETE | CustomAuthorizationFilter | doFilterInternal | "
Duration.between(initTime, LocalDateTime.now()) " | " request.getRequestURI());
}
}
Problem: In my verifyJwt, it reads the secret which is declared as below as null
for some reason.
@Value("${jwt.secret}")
private String secret;
And this is my verifyJwt method
public User verifyJwt(String token) {
log.info("INITIALIZE | JwtProvider | verifyJwt | " token);
LocalDateTime initTime = LocalDateTime.now();
if (token == null || !token.startsWith(tokenPrefix)) {
throw new UnauthorizedException();
}
token = token.substring(tokenPrefix.length());
Algorithm algorithm = Algorithm.HMAC256(secret.getBytes(StandardCharsets.UTF_8));
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(token);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
});
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
log.info("COMPLETE | JwtProvider | verifyJwt | " Duration.between(initTime, LocalDateTime.now()) " | "
username);
return (User) userDetailsService.loadUserByUsername(username);
}
The @Value is not working obviously, but it is working fine for other methods.
Anyone has a solution to fix this problem except for declaring it like this private String secret = "secret"
This is my SpringSecurityConfiguration
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
JwtProvider jwtProvider = new JwtProvider(userDetailsService);
http
.csrf().disable()
.cors()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/contact/**",
"/user/duplicate/**",
"/user/new/**",
"/user/login/**",
"/user/auth/**",
"/user/findId/**",
"/user/findPw/**/**",
"/swagger-ui/index.html/**")
.permitAll()
.antMatchers("/user", "/user/**",
"/profile/**")
.authenticated()
.and()
.addFilterBefore(new CustomAuthorizationFilter(jwtProvider),
UsernamePasswordAuthenticationFilter.class);
}
}
CodePudding user response:
Register your CustomAuthorizationFilter
as a spring bean component.
@Slf4j
@RequiredArgsConstructor
@Component // <-----------------
public class CustomAuthorizationFilter extends OncePerRequestFilter {
....
}
Then register your custom filter to be used just before UsernamePasswordAuthenticationFilter
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Override
protected void configure(HttpSecurity httpSecurity) {
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
This way your filter will be invoked just in the right order and also would be registered as a spring bean so that it has access to application context where @Value
will be able to function.