我正在使用 Spring Boot 创建一个 API 网关,我使用 jwt 令牌来验证用户会话,并且我在 SpringSecurity 中遇到了这个问题,所以这是我的 SecurityFilterChain
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Value("${cognito.issuer}")
private String jwkIssuerUri;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling( exceptions -> exceptions
.authenticationEntryPoint(jwtAuthenticationEntryPoint))
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/auth/callback").permitAll()
.anyRequest().authenticated()
).oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.jwt(jwt ->
jwt.decoder(jwtDecoder())
)
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return JwtDecoders.fromOidcIssuerLocation(jwkIssuerUri);
}
}
如您所见,我正在尝试通过实现 AuthenticationEntryPoint 来处理验证异常,这是我的课程:
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException {
logger.debug("Commence method called. Handling exception.");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
if("ExpiredJwtException".equals(authException.getClass().getSimpleName())) {
logger.debug("Authentication error:\n", authException);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(String.format("{\"error\": \"%s\", \"message\": \"%s\"}",
HttpServletResponse.SC_UNAUTHORIZED, "Provided authorization token is expired"));
} else if("InsufficientAuthenticationException".equals(authException.getClass().getSimpleName())) {
logger.debug("Athentication error:\n", authException);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(String.format("{\"error\": \"%s\", \"message\": \"%s\"}",
HttpServletResponse.SC_UNAUTHORIZED, "Authorization token must be provided to reach this endpoint"));
} else {
logger.debug("Authentication error:\n", authException);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(String.format("{\"error\": \"%s\", \"message\": \"%s\"}",
HttpServletResponse.SC_UNAUTHORIZED, "Cannot validate the token"));
}
}
}
现在我能够处理
InsufficientAuthenticationException
但其他异常,比如 ExpiredJwtException
不是由此类处理的,但它们正在由 spring security 处理,这里是此案例的日志:
2024-05-09 15:12 |DEBUG| org.springframework.security.web.context.HttpSessionSecurityContextRepository.saveContext()
Did not store empty SecurityContext
2024-05-09 15:12 |DEBUG| org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter()
Cleared SecurityContextHolder to complete request
2024-05-09 15:13 |DEBUG| org.springframework.security.web.FilterChainProxy.doFilterInternal()
Securing GET /test
2024-05-09 15:13 |DEBUG| org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter()
Set SecurityContextHolder to empty SecurityContext
2024-05-09 15:13 |DEBUG| org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt()
Failed to authenticate since the JWT was invalid
2024-05-09 15:13 |DEBUG| org.springframework.security.web.context.HttpSessionSecurityContextRepository.saveContext()
Did not store empty SecurityContext
2024-05-09 15:13 |DEBUG| org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter()
Cleared SecurityContextHolder to complete request
我尝试添加 AccessDeniedHandler 甚至将其与 AuthenticationEntryPoint 结合使用,但没有成功。另外,我尝试设置一个自定义过滤器,在引发异常时抛出异常,但这也不起作用。我希望能够自定义处理这些身份验证异常,并且不让 Spring Boot 处理它们。它所做的只是更改身份验证标头,但这不是我想要的,我想从 API 获得明确的响应。
我能够解决这个问题,将 AuthenticationEntryPoint 移动到 oauth2ResourceServer 中,这是 SecurityFilterChain 的新代码:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Value("${cognito.issuer}")
private String jwkIssuerUri;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/auth/callback").permitAll()
.anyRequest().authenticated()
).oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.jwt(jwt ->
jwt.decoder(jwtDecoder())
)
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return JwtDecoders.fromOidcIssuerLocation(jwkIssuerUri);
}
}
现在所有身份验证异常都由我的 AuthenticationEntryPoint 类进行处理