IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Security自定义AccessDeniedHandler配置后不生效 -> 正文阅读

[Java知识库]Spring Security自定义AccessDeniedHandler配置后不生效

问题遇到的现象和发生背景

我在Spring Security中配置了两个异常处理,一个是自定AuthenticationEntryPoint,一个是自定义AccessDeniedHandler。但发现无论抛什么异常都进入了AuthenticationEntryPoint。怎么样才能进入自定义AccessDeniedHandler呢?往下看

问题相关代码

认证代码

/**
 * 权限控制
 * 判断用户角色
 * @author 刘昌兴
 * 
 */
@Component
public class RoleOfAdminFilter implements AccessDecisionManager {
    /** 
     * @author 刘昌兴
     * @param authentication 调用方法的调用者(非空)
     * @param o 被调用的受保护对象
     * @param collection 与被调用的受保护对象关联的配置属性
     */
    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        //collection即是在UrlOfMenuJudgeRoleFilter中getAttributes返回的由角色组成的List<ConfigAttribute>
        for(ConfigAttribute configAttribute:collection){
            //当前url所需要的角色
            String urlNeedRole=configAttribute.getAttribute();
            //如果匿名可访问就不用匹配角色
            if("ROLE_anonymous".equals(urlNeedRole)){
                //如果未登录,提示登陆
                if(authentication instanceof AnonymousAuthenticationToken){
                    throw new AccessDeniedException("尚未登陆,请登录");
                }else{
                    return;//终止继续匹配角色
                }
            }
            //获得用户所授予的角色
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            //判断用户的角色是否满足访问该url的角色
            for(GrantedAuthority grantedAuthority:authorities){
                if(grantedAuthority.getAuthority().equals(urlNeedRole)){
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足!");
    }
 
    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return false;
    }
 
    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}
 
 
 

自定义AuthenticationEntryPoint

/**
 * 用户未登录或token失效时的返回结果
 * @author 刘昌兴
 */
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{
 
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter printWriter=response.getWriter();
        ResultBean resultBean=ResultBean.error(authException.getMessage(), null);
        resultBean.setCode(401);
        printWriter.write(new ObjectMapper().writeValueAsString(resultBean));
        printWriter.flush();
        printWriter.close();
    }
    
}
 

自定义AccessDeniedHandler

/**
 * 没有权限访问时返回的结果
 * @author 刘昌兴
 * 
 */
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler{
 
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter printWriter=response.getWriter();
        ResultBean resultBean=ResultBean.error("权限不足,请联系管理员!", null);
        resultBean.setCode(403);
        printWriter.write(new ObjectMapper().writeValueAsString(resultBean));
        printWriter.flush();
        printWriter.close();
        
    }
 
}
 
 

Spring Security配置

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
/*            .antMatchers("/login","/doc.html","/swagger-resources/**",
                    "/v2/api-docs/**","/webjars/**","/capture","/test/**","/ws/**","/logOut",
                    "/admins/userFaces","/index.html","/css/**","/js/**","/fonts/**").permitAll()//放行相关请求和资源*/
            .anyRequest().authenticated()//除了上面的其他都需要认证
            .withObjectPostProcessor(getObjectPostProcessor())//动态权限配置
            .and()
            .addFilterBefore(getJwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)//添加登陆过滤器
            .exceptionHandling()//添加异常处理过滤器
            .accessDeniedHandler(restfulAccessDeniedHandler)//访问拒绝处理器
            .authenticationEntryPoint(restAuthenticationEntryPoint)//权限异常过滤器
            .and()
            .csrf().disable()//使用jwt,不需要使用csrf拦截器
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//不需要使用session
            .and()
            .headers().cacheControl();//禁用缓存
    }
 

原因分析

在ExceptionTranslationFilter源码中有如下代码

    private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,
            FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication);
        if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {
            if (logger.isTraceEnabled()) {
                logger.trace(LogMessage.format("Sending %s to authentication entry point since access is denied",
                        authentication), exception);
            }
            sendStartAuthentication(request, response, chain,
                    new InsufficientAuthenticationException(
                            this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication",
                                    "Full authentication is required to access this resource")));
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        LogMessage.format("Sending %s to access denied handler since access is denied", authentication),
                        exception);
            }
            this.accessDeniedHandler.handle(request, response, exception);
        }
    }
 

如果程序抛出了AccessDeniedException但是当前认证状态是匿名的(未认证),那么会ExceptionTranslationFilter会抛出InsufficientAuthenticationException。而所有的AuthenticationException会被配置的AuthenticationEntryPoint实现类(RestAuthenticationEntryPoint)捕获。
所以通过抛出AccessDeniedException进入自定义AccessDeniedHandler(RestfulAccessDeniedHandler)的前提是当前已完成身份认证。
修改后的认证代码

/**
 * 权限控制
 * 判断用户角色
 * @author 刘昌兴
 * 
 */
@Component
public class RoleOfAdminFilter implements AccessDecisionManager {
    /** 
     * @author 刘昌兴
     * @param authentication 调用方法的调用者(非空)
     * @param o 被调用的受保护对象
     * @param collection 与被调用的受保护对象关联的配置属性
     */
    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        //collection即是在UrlOfMenuJudgeRoleFilter中getAttributes返回的由角色组成的List<ConfigAttribute>
        //如果未登录,提示登陆
        if(authentication instanceof AnonymousAuthenticationToken){
            throw new BadCredentialsException("尚未登陆");
        }
        for(ConfigAttribute configAttribute:collection){
            //当前url所需要的角色
            String urlNeedRole=configAttribute.getAttribute();
            //如果URL登录即可访问就不用匹配角色
            if("ROLE_login".equals(urlNeedRole)){
                return;
            }
            //获得用户所授予的角色
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            //判断用户的角色是否满足访问该url的角色
            for(GrantedAuthority grantedAuthority:authorities){
                if(grantedAuthority.getAuthority().equals(urlNeedRole)){
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足");
    }
 
    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return false;
    }
 
    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}
 

总结:如果想通过抛出AccessDeniedException异常进入自定义AccessDeniedHandler那么当前认证状态不应该是匿名的。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-11 16:17:11  更:2022-05-11 16:19:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:04:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码