Я реализую аутентификацию с помощью Spring Security с JWT.
Мои конечные точки входа и регистрации работали правильно до добавления JWT. Однако после реализации JWT и добавления фильтра безопасности я начал получать следующую ошибку при попытке войти:
java.lang.NullPointerException: Cannot invoke "org.springframework.security.core.userdetails.UserDetails.getAuthorities()" because "user" is null
at br.com.gustavo.numa.infra.security.SecurityFilter.doFilterInternal(SecurityFilter.java:33) ~[classes/:na]
Мой вопрос
Почему пользователь становится нулевым после проверки токена JWT?
Код
Ниже приведена моя реализация:
SecurityConfiguration
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Autowired
SecurityFilter securityFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
return httpSecurity
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(HttpMethod.POST, "/auth/login").permitAll()
.requestMatchers(HttpMethod.POST, "/auth/register").permitAll()
.requestMatchers(HttpMethod.GET, "/test").hasRole("USER")
.anyRequest().authenticated()
)
.addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception{
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Фильтр безопасности
@Component
public class SecurityFilter extends OncePerRequestFilter {
@Autowired
TokenService tokenService;
@Autowired
UserRepository userRepository;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
var token = this.recoverToken(request);
if (token != null) {
var login = tokenService.validateToken(token);
UserDetails user = userRepository.findByEmail(login);
var authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String recoverToken(HttpServletRequest request) {
var authHeader = request.getHeader("Authorization");
if (authHeader == null) return null;
return authHeader.replace("Bearer ", "");
}
}
TokenService
@Component
public class TokenService {
@Value("${api.security.token.secret}")
private String secret;
public String generateToken(User user) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.create()
.withIssuer("auth-api")
.withSubject(user.getEmail())
.withExpiresAt(genExpirationDate())
.sign(algorithm);
} catch (JWTCreationException exception) {
throw new RuntimeException("Error while generating token");
}
}
public String validateToken(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.require(algorithm)
.withIssuer("auth-api")
.build()
.verify(token)
.getSubject();
} catch (JWTVerificationException exception) {
return "";
}
}
private Instant genExpirationDate() {
return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00"));
}
}