Spring Security JWT的用户变成了null
我正在使用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令牌后,user 会变成 null?
代码
下面是我的实现:
安全配置
@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 ", "");
}
}
令牌服务
@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"));
}
}
解决方案
我通过调试应用程序解决了这个问题。在调试过程中,我检查了SecurityFilter接收到的令牌并分析了其内容。我发现登录时发送的令牌并非应用程序生成的预期令牌,而是一个不同的令牌。这导致校验失败,用户对象为空。在发现这一点后,我查找系统中错误令牌被设置的位置,并将其从请求中移除。一旦移除错误的令牌,问题就得到解决。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。