我有下一个Spring Security配置:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
private final JwtAuthenticationEntryPoint jwtFilterAuthenticationEntryPoint;
private final AuthzAccessDeniedHandler authzAccessDeniedHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(USER_PATH + USER_ID_PATH_VAR).authenticated()
.requestMatchers(HttpMethod.POST, EVENT_PATH).hasRole("ORGANIZER")
.requestMatchers(HttpMethod.GET, TICKET_PATH).authenticated()
.requestMatchers(AUTH_PATH + "/logout/{userUuid}").authenticated()
.anyRequest().permitAll()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
.cors(Customizer.withDefaults())
.exceptionHandling(handler -> handler
.authenticationEntryPoint(jwtFilterAuthenticationEntryPoint)
.accessDeniedHandler(authzAccessDeniedHandler)
)
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.logout(AbstractHttpConfigurer::disable)
;
return http.build();
}
//other omited code...
字符串
以下是JWT filter代码:
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final String AUTHZ_HEADER_PREFIX = "Bearer ";
private final JwtService jwtService;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
String authzHeader = request.getHeader("Authorization");
if (authzHeader == null || !authzHeader.startsWith(AUTHZ_HEADER_PREFIX)) {
filterChain.doFilter(request, response);
return;
}
AccessToken accessToken = jwtService.parseAccessToken(
authzHeader.substring(AUTHZ_HEADER_PREFIX.length())
);
authenticateIfCurrentlyNot(accessToken, request);
filterChain.doFilter(request, response);
}
private void authenticateIfCurrentlyNot(AccessToken accessToken, HttpServletRequest request) {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
accessToken.claims().sub(),
null,
List.of(convertRoleToSimpleGrantedAuthority(
accessToken.claims().role()
))
);
token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(token);
}
}
private SimpleGrantedAuthority convertRoleToSimpleGrantedAuthority(UserRole role) {
return new SimpleGrantedAuthority("ROLE_" + role.name());
}
}
型
如果由于各种原因,给定的编码JWT无效,则方法parseToken()抛出自定义InvalidTokenException。我想处理此异常,正如您在上面的配置中看到的那样,我已经添加了名为jwtFilterAuthenticationEntryPoint的自定义AuthenticationEntryPoint。现在这里是InvalidTokenException和JwtFilterAuthenticationEntryPoint的代码:
public class InvalidTokenException extends AuthenticationException {
public InvalidTokenException(String message) {
super(message);
}
public static String errorCode() {
return "invalid.token";
}
}
@Component
@Slf4j
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException
) {
log.error(authException.getMessage(), authException);
resolver.resolveException(request, response, null, authException);
}
}
型
正如你所看到的ExceptionTranslationFilter捕捉我的异常,我已经从AuthenticationException扩展了它,所以在官方文档中写的(ExceptionTranslationFilter伪代码),它将能够处理它。但是当我尝试测试JwtAuthenticationEntryPoint的commence()方法的异常处理代码时,没有执行,因为没有提供日志:
所以我期待着任何帮助解决这个问题)
2条答案
按热度按时间5uzkadbs1#
在您的情况下,由于
SecurityFilterChain
中的语句addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
,在调用ExceptionTranslationFilter
之前抛出异常。因此,没有调用ExceptionTranslationFilter
。为了解释为什么
JwtAuthenticationEntryPoint
没有被触发,当AuthenticationException
发生时,注册的AuthenticationEntryPoint
的commence
方法通常由ExceptionTranslationFilter
调用。需要注意的是,过滤器可能有自己的
AuthenticationEntryPoint
。例如,在httpBasic
的情况下,身份验证由BasicAuthenticationFilter
处理,任何问题都会导致调用BasicAuthenticationEntryPoint
。我可以看到3个选项来处理
JwtAuthenticationFilter
中的错误:选项一:
在
SecurityFilterChain
中的AuthorizationFilter
之前或ExceptionTranslationFilter
之后执行JwtAuthenticationFilter
。例如:
字符串
选项二:
包括在
JwtAuthenticationFilter
中捕获AuthenticationException
并调用JwtAuthenticationEntryPoint
的commence
方法。为了说明这一点,让我们看看Spring
BasicAuthenticationFilter
如何管理AuthenticationException
。BasicAuthenticationEntryPoint
的commence
方法如下所示:型
在检查
BasicAuthenticationEntryPoint
的源代码时,很明显它试图处理AuthenticationException
并随后调用authenticationEntryPoint.commence(request, response, ex)
。应用类似的方法,可以对
JwtAuthenticationEntryPoint
和JwtAuthenticationFilter
进行以下调整:型
更新
SecurityFilterChain
配置如下:型
需要注意的是,不需要显式注册
JwtAuthenticationEntryPoint
。选项三:
另一种方法是直接在
JwtAuthenticationFilter
中处理AuthenticationException
并发送错误响应。这消除了对JwtAuthenticationEntryPoint
的需要。下面是修改后的JwtAuthenticationFilter
:型
更新
SecurityFilterChain
配置:型
选择最适合您的要求并与您的身份验证流程无缝集成的选项。
6uxekuva2#
问题出在过滤器的顺序上。我的自定义过滤器在ExceptionTranslationFilter之前,所以当抛出异常时,ExceptionTranslationFilter无法到达。
这行代码修复了它:
字符串