Spring security 配置的AccessDeniedHandler无效,抛出AccessDeniedException 不允许访问

1.Spring Security 中的异常处理

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

AuthenticationEntryPoint 该类用来统一处理 AuthenticationException 异常
AccessDeniedHandler 该类用来统一处理 AccessDeniedException 异常

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

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));
    }

}

配置

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

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

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

@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

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

3.解决方案

然后我就直接在全局异常处理GlobalExceptionHandler里添加

    /**
     * 处理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);
    }

==============={2023/4/13} ==========================
经过过了很久的学习,已经没有单独使用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

   <!-- 注意是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 实现权限异常处理

@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资源服务器

@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();//配置所有访问控制,必须认证过后才可以访问
    }
}

你可能感兴趣的:(java,spring,boot)