我有 JwtTokenProvider:
@Component
@PropertySource("classpath:jwt.properties")
@RequiredArgsConstructor
public class JwtTokenProvider {
//some code ...
public boolean validateToken(String token) {
try {
Jws<Claims> claimsJws = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token);
return !claimsJws.getBody().getExpiration().before(new Date());
} catch (JwtException | IllegalArgumentException e) {
throw new JwtAuthenticationException("JWT token expired or invalid!");
}
}
}
这是一个正常组件,如果令牌“腐烂”,结果会引发异常
很明显,如果您使用损坏的令牌联系服务器,它将抛出异常 - 但会出现 500 错误。如何捕获它以便返回 401?而且只有这个例外,403我需要保存
以下是一些相关的类:
- 筛选
@RequiredArgsConstructor
public class JwtTokenFilter extends GenericFilterBean {
private final JwtTokenProvider jwtTokenProvider;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String token = jwtTokenProvider.retrieveToken((HttpServletRequest) servletRequest);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
2.配置
@RequiredArgsConstructor
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final JwtTokenProvider provider;
@Override
public void configure(HttpSecurity builder) throws Exception {
JwtTokenFilter filter = new JwtTokenFilter(provider);
builder.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}
}
==========================
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = {SecurityPackageMarker.class})
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtTokenProvider provider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.addFilterBefore(new CorsFilter(corsConfigurationSource()), SessionManagementFilter.class)
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/hello", "/login", "/register").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(provider));
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config.applyPermitDefaultValues());
return source;
}
}
- 例外:
public class JwtAuthenticationException extends AuthenticationException {
public JwtAuthenticationException(String explanation) {
super(explanation);
}
}
4.控制器:
@PostMapping("/login")
@ResponseStatus(HttpStatus.OK)
public Properties login(@RequestBody AuthenticationInDto dto) {
try {
String username = dto.getUsername();
String password= dto.getPassword();
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
Optional<User> user = userService.findByUsername(username);
if (!user.isPresent()) {
throw new UsernameNotFoundException("User with username: " + username + " not found");
}
String token = provider.createToken(username, user.get().getRole());
Properties response = new Properties();
response.setProperty("jwtToken", token);
return response;
} catch (AuthenticationException e) {
throw new BadCredentialsException(e.getMessage());
}
}
但是,尽管如此,它并没有捕获我的异常(不转发它BadCredentialsException) - 服务器崩溃并出现错误 500,即使我有一个处理程序:
@RestControllerAdvice
public class ControllersExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(BadCredentialsException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ErrorDto handleBadCredentials(BadCredentialsException ex) {
return new ErrorDto(HttpStatus.UNAUTHORIZED.value(),
ex.getMessage());
}
@ExceptionHandler(RegistrationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorDto handleRegistrationException(RegistrationException ex) {
return new ErrorDto(HttpStatus.BAD_REQUEST.value(),
ex.getMessage());
}
}
我已经简化为过滤器中的任何错误:
在您的情况下,捕获过滤器中的错误并发送
sendError(). 在sendError()第二个参数中,您可以添加错误描述。validateToken()如果在方法中处理了异常,并且接口GenericFilterBean将方法设置为仅 2 个可能的异常,则仍然不清楚您是如何设法编译的:IOException和ServletException.从理论上讲,
@RestControllerAdvice它不会通过捕获错误来工作,因为。servlet -> 过滤器前 -> spring mvc -> 过滤器后。顺便说一句,
@Aspect它工作得很好: