Spring security 当HttpServletResponse已提交时,调用Spring安全访问拒绝处理程序

Spring security 当HttpServletResponse已提交时,调用Spring安全访问拒绝处理程序,spring-security,Spring Security,我有一个HTTP API,受Spring Security和JWT保护。 当我试图访问受保护的资源时,我会获得401退休金。 如果我经过身份验证(JWT是有效的),并且我有正确的角色,我就可以获得资源。该资源受@PreAuthorize(“hasRole('USER')”)保护 我遇到的问题是,当我没有正确的角色时,我希望返回403(在下面的代码中,为了测试起见,它是401)。 但是我知道我得到了500分,因为角色不正确时会抛出AccessDeniedException。 奇怪的是,它会转到我的

我有一个HTTP API,受Spring Security和JWT保护。
当我试图访问受保护的资源时,我会获得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上。更准确的答案如下: