spring-security的accessdeniedhandler无效,抛出AccessDeniedExcepthion无法访问到

上周碰到问题,通过 persionService 进行权限判断的时候,抛出的 AccesDeniedExcepthion 无法被 AccessDeniedHandler 处理到。他是直接被 globalException 拦截到。

1.Spring Security 中的异常处理 我们一般都会在 Spring Security 的 自定义配置类( WebSecurityConfigurerAdapter )中使用 HttpSecurity 提供的 exceptionHandling() 方法用来提供异常处理。该方法构造出 ExceptionHandlingConfigurer 异常处理配置类。该配置类提供了两个实用接口:

AuthenticationEntryPoint 该类用来统一处理 AuthenticationException 异常 AccessDeniedHandler 该类用来统一处理 AccessDeniedException 异常 我们只要实现并配置这两个异常处理类即可实现对 Spring Security 认证授权相关的异常进行统一的自定义处理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class SimpleAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {
        ResponseUtil.out(response, ResultJson.error(CommonEnum.NOT_FIND_LOGIN_INFORMATION));
    }
}

public class SimpleAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       AccessDeniedException accessDeniedException) throws IOException, ServletException {
        ResponseUtil.out(response, ResultJson.error(CommonEnum.NO_PERMISSION));
    }

}

配置

实现了上述两个接口后,我们只需要在 WebSecurityConfigurerAdapter 的 configure(HttpSecurity http) 方法中配置即可。相关的配置片段如下:

1
 http.exceptionHandling().accessDeniedHandler(new SimpleAccessDeniedHandler()).authenticationEntryPoint(new SimpleAuthenticationEntryPoint())

2.验证权限失败抛出 AccessDeniedException 不允许访问

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Slf4j
@Component("el")
public class ElPermissionConfig {
    /**
     * 判断接口是否有xxx:xxx权限
     *
     * @param permission 权限
     * @return {boolean}
     */
    public boolean check(String permission) {
        log.info("需要权限:{}",permission);
        if (StrUtil.isBlank(permission)) {
            return false;
        }
        SecurityUser user= (SecurityUser) SecurityUtils.getCurrentUser();
        if (user == null) {
            return false;
        }

        return user
                .getAuthorities()
                .stream()
                .map(GrantedAuthority::getAuthority)
                .filter(StringUtils::hasText)
                .anyMatch(x -> PatternMatchUtils.simpleMatch(permission, x));
    }
}

当验证权限失败时抛出 AccessDeniedException 异常 不允许访问,而我明明配置了 SimpleAccessDeniedHandler 来处理异常并返回提示信息。我仔细检查发现拦截 AccessDeniedException 异常的是全局异常处理 GlobalExceptionHandler。

1
2
3
4
5
6
    @ExceptionHandler(value =Exception.class)
    @ResponseBody
    public ResultJson exceptionHandler(HttpServletRequest req, Exception e){
        log.error("未知异常!原因是:",e);
        return ResultJson.error(CommonEnum.INTERNAL_SERVER_ERROR);
    }

是什么造成这个原因了:

问题原因 出现这种问题的原因一般都是因为项目中还配置了 GlobalExceptionHandler 。

由于 GlobalExceptionHandler 全局异常处理器会比 AccessDeniedHandler 先捕获 AccessDeniedException 异常,因此当配置了 GlobalExceptionHandler 后,会发现 AccessDeniedHandler 失效了。

3.解决方案 然后我就直接在全局异常处理 GlobalExceptionHandler 里添加

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    /**
     * 处理AccessDeineHandler无权限异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = AccessDeniedException.class)
    @ResponseBody
    public ResultJson exceptionHandler(HttpServletRequest req, AccessDeniedException e){
        log.error("不允许访问!原因是:",e.getMessage());
        return ResultJson.error(CommonEnum.NO_PERMISSION);
    }

经过过了很久的学习,已经没有单独使用 security,现在是使用 security-oauth2,但是很多 配置是类似的。 现在没有使用全局异常也能处理 Security 中的异常处理, 直接在 CustomAuthExceptionHandler 捕获打印

2023-04-13 16:53:10.935 ERROR 10932 — [nio-8111-exec-2] c.d.s.h.CustomAuthExceptionHandler : NoAuthentication :UNAUTHORIZED 2023-04-13 16:53:11.863 ERROR 10932 — [nio-8111-exec-3] c.d.s.h.CustomAuthExceptionHandler : NoAuthentication :UNAUTHORIZED

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
   <!-- 注意是starter,自动配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- 不是starter,手动配置 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.3.RELEASE</version>
        </dependency>

CustomAuthExceptionHandler 实现权限异常处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Component
@Slf4j
public class CustomAuthExceptionHandler implements AuthenticationEntryPoint, AccessDeniedHandler {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {

        Throwable cause = authException.getCause();
        if (cause instanceof InvalidTokenException) {
            log.error("InvalidTokenException : {}",cause.getMessage());
            //Token无效
            ResponseUtil.out(response,ResultJson.error(CommonEnum.ACCESS_TOKEN_INVALID));
            //response.getWriter().write(JSON.toJSONString(ResultJson.error(CommonEnum.ACCESS_TOKEN_INVALID)));
        } else {
            log.error("NoAuthentication :{} ",CommonEnum.UNAUTHORIZED);
            //资源未授权
            ResponseUtil.out(response,ResultJson.error(CommonEnum.UNAUTHORIZED));
            //response.getWriter().write(JSON.toJSONString(ResultJson.error(CommonEnum.UNAUTHORIZED)));
        }

    }

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        //访问资源的用户权限不足
        log.error("AccessDeniedException : {}",accessDeniedException.getMessage());
        ResponseUtil.out(response,ResultJson.error(CommonEnum.INSUFFICIENT_PERMISSIONS));
        //response.getWriter().write(JSON.toJSONString(ResultJson.error(CommonEnum.INSUFFICIENT_PERMISSIONS)));
    }
}

oauth2 资源服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private static final String RESOURCE_IDS = "order";
    @Autowired
    private CustomAuthExceptionHandler customAuthExceptionHandler;
    @Autowired
    UserLogoutSuccessHandler userLogoutSuccessHandler;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_IDS)
                .stateless(false)
                .accessDeniedHandler(customAuthExceptionHandler)
                .authenticationEntryPoint(customAuthExceptionHandler);
    }

    @Override
    public void configure(HttpSecurity httpSecurity) throws Exception {
        //解决springSecurty使用X-Frame-Options防止网页被Frame
        httpSecurity.headers().frameOptions().disable()
                .and()
                .logout()
                .logoutSuccessHandler(userLogoutSuccessHandler);

        httpSecurity
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .requestMatchers().anyRequest()
                .and()
                .anonymous()
                .and()
                .authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()//将PreflightRequest不做拦截。
                .and()
                .authorizeRequests()
                .antMatchers(
                        "/webjars/**",
                        "/swagger/**",
                        "/v2/api-docs",
                        "/doc.html",
                        "/swagger-ui.html",
                        "/swagger-resources/**",
                        "/druid/**",
                        "/open/**").permitAll()
                .and()
                .authorizeRequests()
                .antMatchers("/**").authenticated();//配置所有访问控制,必须认证过后才可以访问
    }
}
Licensed under CC BY-NC-SA 4.0
最后更新于 Jan 06, 2025 05:52 UTC
comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计
Caret Up