Spring security 当HttpServletResponse已提交时,调用Spring安全访问拒绝处理程序
我有一个HTTP API,受Spring Security和JWT保护。Spring security 当HttpServletResponse已提交时,调用Spring安全访问拒绝处理程序,spring-security,Spring Security,我有一个HTTP API,受Spring Security和JWT保护。 当我试图访问受保护的资源时,我会获得401退休金。 如果我经过身份验证(JWT是有效的),并且我有正确的角色,我就可以获得资源。该资源受@PreAuthorize(“hasRole('USER')”)保护 我遇到的问题是,当我没有正确的角色时,我希望返回403(在下面的代码中,为了测试起见,它是401)。 但是我知道我得到了500分,因为角色不正确时会抛出AccessDeniedException。 奇怪的是,它会转到我的
当我试图访问受保护的资源时,我会获得401退休金。
如果我经过身份验证(JWT是有效的),并且我有正确的角色,我就可以获得资源。该资源受
@PreAuthorize(“hasRole('USER')”)
保护
我遇到的问题是,当我没有正确的角色时,我希望返回403(在下面的代码中,为了测试起见,它是401)。
但是我知道我得到了500分,因为角色不正确时会抛出AccessDeniedException。奇怪的是,它会转到我的JwtAccessDeniedHandler自定义代码,但响应已经提交(isCommitted()==true),因此每当我尝试设置状态等时,它什么也不做。
您是否对可能出现的错误配置或遗漏有任何想法 配置:
@Slf4j
@EnableWebSecurity
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true
)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ObjectMapper objectMapper;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(
jwtAuthenticationFilter(joseHelper(jsonWebKey())),
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
.accessDeniedHandler(new JwtAccessDeniedHandler());
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter(JoseHelper joseHelper) {
return new JwtAuthenticationFilter(joseHelper);
}
@Bean
public JoseHelper joseHelper(PublicJsonWebKey key) {
return new JoseHelper(key);
}
@Bean
public PublicJsonWebKey jsonWebKey() throws IOException, JoseException {
return RsaJwkGenerator.generateJwk(2048);
}
private void sendUnauthorized(HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.setContentType("application/json");
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ApiError apiError = ApiError.builder()
.code(HttpStatus.UNAUTHORIZED.name())
.message(HttpStatus.UNAUTHORIZED.getReasonPhrase())
.httpStatus(HttpStatus.UNAUTHORIZED)
.build();
httpServletResponse.getWriter().print(objectMapper.writeValueAsString(apiError));
}
private class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
log.info("accessDeniedHandler", e);
sendUnauthorized(httpServletResponse);
}
}
private class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
sendUnauthorized(httpServletResponse);
}
}
}
过滤器:
@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final String BEARER = "Bearer ";
private JoseHelper joseHelper;
@Autowired
public JwtAuthenticationFilter(JoseHelper joseHelper) {
this.joseHelper = joseHelper;
}
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
String header = httpServletRequest.getHeader("Authorization");
if (header == null || !header.startsWith(BEARER)) {
log.error("JWT token is not valid");
filterChain.doFilter(httpServletRequest, httpServletResponse);
return;
}
final String encryptedToken = header.substring(BEARER.length());
try {
final String decryptedJwt = joseHelper.decryptJwt(encryptedToken);
final String verifiedJwt = joseHelper.verifyJwt(decryptedJwt);
final JwtClaims jwtClaims = joseHelper.parse(verifiedJwt);
List<SimpleGrantedAuthority> authorities = jwtClaims.getStringListClaimValue("userRoles")
.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(jwtClaims, null, authorities);
SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken);
filterChain.doFilter(httpServletRequest, httpServletResponse);
} catch (JoseException | InvalidJwtException | MalformedClaimException e) {
log.error("JWT token is not valid", e);
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
}
@Slf4j
@组成部分
公共类JwtAuthenticationFilter扩展了OncePerRequestFilter{
私有静态最终字符串BEARER=“BEARER”;
私人约瑟夫·约瑟夫·约瑟夫;
@自动连线
公共JwtAuthenticationFilter(JoseHelper-JoseHelper){
this.joseHelper=joseHelper;
}
@凌驾
受保护的void doFilterInternal(HttpServletRequest HttpServletRequest,HttpServletResponse HttpServletResponse,FilterChain FilterChain)抛出ServletException,IOException{
String header=httpServletRequest.getHeader(“授权”);
if(header==null | |!header.startsWith(BEARER)){
log.error(“JWT令牌无效”);
doFilter(httpServletRequest,httpServletResponse);
返回;
}
最终字符串encryptedToken=header.substring(BEARER.length());
试一试{
最终字符串decryptedJwt=joseHelper.decryptJwt(encryptedToken);
最终字符串verifiedJwt=joseHelper.verifyJwt(decryptedJwt);
final JwtClaims JwtClaims=joseHelper.parse(verifiedJwt);
List authorities=jwtClaims.getStringListClaimValue(“用户角色”)
.stream().map(SimpleGrantedAuthority::new).collect(collector.toList());
JwtAuthenticationToken JwtAuthenticationToken=新的JwtAuthenticationToken(jwtClaims,null,authorities);
SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken);
doFilter(httpServletRequest,httpServletResponse);
}catch(JoseException | InvalidJwtException |格式错误的LaimException e){
log.error(“JWT令牌无效”,e);
doFilter(httpServletRequest,httpServletResponse);
}
}
}
问题在于我显然使用了球衣。我现在真的没有时间调查原因。一旦我在JerseyConfig中注册了异常映射器,我就能够正确捕获和处理AccessDeniedException。
从那时起,拒绝访问处理程序不再被调用,变得无用。
有点奇怪,但可能有一个很好的理由。万一有人来到这里想知道根本原因,那是因为jersey将其包装在ServletException上。更准确的答案如下: