当 JWT 令牌无效时,我尝试返回自定义响应。
我知道 Spring Security 异常是在控制器开始工作之前抛出的。因此,我尝试编写自己的“CustomBearerAuthenticationEntryPoint”类来实现“AuthenticationEntryPoint”。但在这个课程中,我得到 *Cannot find bean with qualifier 'handlerExceptionResolver' *.
package ila.ila_backend.security;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import java.io.IOException;
@Component
public class CustomBearerAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final HandlerExceptionResolver resolver;
public CustomBearerAuthenticationEntryPoint(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) {
this.resolver = resolver;
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
this.resolver.resolveException(request, response, null, authException);
}
}
这是我的 *CustomBearerTokenAccesDeniedHandler * 类:
package ila.ila_backend.security;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import java.io.IOException;
@Component
public class CustomBearerTokenAccesDeniedHandler implements AccessDeniedHandler {
private final HandlerExceptionResolver resolver;
public CustomBearerTokenAccesDeniedHandler(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) {
this.resolver = resolver;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
this.resolver.resolveException(request,response,null,accessDeniedException);
}
}
我的SecurityConfig类:
package ila.ila_backend.security;
import jakarta.servlet.DispatcherType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private JwtAuthenticationEntryPoint handler;
private final CustomBearerAuthenticationEntryPoint customBearerTokenAccessEntryPoint;
private final CustomBearerTokenAccesDeniedHandler customBearerTokenAccesDeniedHandler;
public SecurityConfig(UserDetailsServiceImpl userDetailsService, JwtAuthenticationEntryPoint handler, CustomBearerAuthenticationEntryPoint customBearerTokenAccessEntryPoint, CustomBearerTokenAccesDeniedHandler customBearerTokenAccesDeniedHandler) {
this.handler = handler;
this.customBearerTokenAccessEntryPoint = customBearerTokenAccessEntryPoint;
this.customBearerTokenAccesDeniedHandler = customBearerTokenAccesDeniedHandler;
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
//.formLogin().failureHandler()
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorize -> {
authorize.requestMatchers("/api/projects/getProjects").authenticated();
authorize.requestMatchers("/api/activities/getActivities").authenticated();
authorize.requestMatchers("/api/participants/getSelectedParticipants").authenticated();
authorize.dispatcherTypeMatchers(DispatcherType.ERROR).permitAll();
authorize.anyRequest().permitAll();
})
.exceptionHandling(exceptionHandling ->
exceptionHandling.
authenticationEntryPoint(this.customBearerTokenAccessEntryPoint)
.accessDeniedHandler(this.customBearerTokenAccesDeniedHandler))
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
我的控制器:
@GetMapping("/getProjects")
public ResponseEntity<ProjectsApiResponse<ProjectsResponse>> getProjects(@RequestHeader("Authorization") String token) {
String tokenWithoutPrefix = token.substring("Bearer ".length());
// Validate the token before proceeding
if (!jwtTokenManager.validateToken(tokenWithoutPrefix)) {
throw new UnauthorizedUserException("Invalid JWT");
}
Long userId = jwtTokenManager.getUserIdFromToken(tokenWithoutPrefix);
System.out.println("Extracted User ID: " + userId);
List<Projects> projects = projectsService.getProjectsByUserId(userId);
List<ProjectsResponse> projectsResponseList = new ArrayList<>();
for (Projects project : projects) {
ProjectsResponse projectResponse = new ProjectsResponse(project.getId(), project.getName());
projectsResponseList.add(projectResponse);
}
ProjectsApiResponse<ProjectsResponse> response = new ProjectsApiResponse<>("success", projectsResponseList);
return new ResponseEntity<>(response, HttpStatus.OK);
}
验证令牌:
@Override
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);
return true;
} catch (SignatureException e) {
LOGGER.error("Invalid JWT signature: " + e.getMessage());
} catch (MalformedJwtException e) {
LOGGER.error("Malformed JWT: " + e.getMessage());
} catch (ExpiredJwtException e) {
LOGGER.error("Expired JWT: " + e.getMessage());
} catch (UnsupportedJwtException e) {
LOGGER.error("Unsupported JWT: " + e.getMessage());
} catch (IllegalArgumentException e) {
LOGGER.error("JWT claims string is empty: " + e.getMessage());
}
return false;
}
我的异常处理程序(在 GlobalExceptionHandler 类中):
@ExceptionHandler(InvalidBearerTokenException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ErrorMessage handleInvalidBearerTokenException (InvalidBearerTokenException ex) {
return new ErrorMessage("error", "The access token provided is expired, invalid or malformed.");
}
在这种情况下,当我写入无效的 JWT 令牌时,我收到“500 内部服务器错误”且正文为空。我期待看到我的自定义回复。
我已经附上了我所有的相关课程(如果你想看,我可以分享其他代码)。我真的需要你的帮助来解决这个问题。预先感谢。
首先创建一个自定义异常处理类并编写自定义异常方法。 (你已经有了)
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(value = {ExpiredJwtException.class})
public ResponseEntity<Object> handleExpiredJwtException(ExpiredJwtException ex, WebRequest request) {
var errMsg = new ErrorMessage("error", "Token provided is expired");
return new ResponseEntity<Object>(errMsg, HttpStatus.UNAUTHORIZED);
}
@ExceptionHandler(value = {InvalidBearerTokenException.class})
public ResponseEntity<Object> invalidBearerTokenException(ExpiredJwtException ex, WebRequest request) {
var errMsg = new ErrorMessage("error", "The access token provided is expired, invalid or malformed.");
return new ResponseEntity<Object>(errMsg, HttpStatus.UNAUTHORIZED);
}
}
// Add logic under this filter
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
// @Autowired others Bean If required
@Autowired
private JwtService jwtService;
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
try {
// Put your JWT filter codes under try catch block
// Provide an example
final var jwt = jwtService.resolveToken(request);
} catch (ExpiredJwtException ex) {
// Catch exception as you like
resolver.resolveException(request, response, null, ex);
}
catch (InvalidBearerTokenException ex) {
// Catch exception as you like
resolver.resolveException(request, response, null, ex);
}
}
}
尝试使用此方法,如果您有任何问题请告诉我。因为它正在处理我的代码。