spring boot security自定义权限检查

news2024/11/25 14:38:20

前言

鉴权主要分为身份认证和权限控制两部分:
身份认证:检查当前用户是否合法(比如已登录)
权限控制:检查当前用户是否有访问该资源的权限

本文主要给出一个示例,说明如何自定义权限控制。
因为一个完整的示例代码,比较多,我在前面的几篇文章都有相关示例:
spring boot security快速使用示例
spring boot security之前后端分离配置
spring boot security自定义认证
spring boot security验证码登录示例
spring boot security使用jwt认证
所以本文主要给出权限检查相关的示例代码。

源码分析

在前面几篇文章里,都给了完整示例,照搬就能用,这里只粘贴出部分代码,可能让人迷糊,所以给出一些源码说明。
在启用spring security后,我们启动应用应该能看到下面这行日志:

2023-07-14 13:36:41.306 INFO 9584 — [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@6af5bbd0, org.springframework.security.web.context.SecurityContextPersistenceFilter@3204e238, org.springframework.security.web.header.HeaderWriterFilter@20b9d5d5, org.springframework.web.filter.CorsFilter@76464795, org.springframework.security.web.authentication.logout.LogoutFilter@4821aa9f, com.xxd.security.demo.security.JwtRequestFilter@129bd55d, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@35a0e495, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@a5272be, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@53cf9c99, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@b34832b, org.springframework.security.web.session.SessionManagementFilter@291373d3, org.springframework.security.web.access.ExceptionTranslationFilter@4228bf58, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@10f7918f]

注意最后一个过滤器:org.springframework.security.web.access.intercept.FilterSecurityInterceptor

	public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
		...
		InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
		...
	}

如上:在doFilter方法调用的invoke方法内调用了super.beforeInvocation()方法。
super.beforeInvocation()方法内调用认证检查和权限检查的方法,可以自己找到这个方法里去看,我们重点看权限检查:

	private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
			Authentication authenticated) {
		try {
			this.accessDecisionManager.decide(authenticated, object, attributes);
		}
		catch (AccessDeniedException ex) {
			...
			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));
			throw ex;
		}
	}

主要就是:this.accessDecisionManager.decide(authenticated, object, attributes);这个方法。
accessDecisionManager,顾名思义是访问决策管理,构造方法需要传递一些投票器,在决策的时候根据投票器的投票结果来返回最终结果,默认主要有3个实现:
在这里插入图片描述
投票器的结果有3种:同意、拒绝、弃权。

  • AffirmativeBased 有一个投票器投同意票,最终结果就是同意
  • ConsensusBased 投票结果,如果同意票数大于拒绝的,就是同意,小于是拒绝,如果票数相等且不为0,有一个属性来决定:allowIfEqualGrantedDeniedDecisions, 默认是true,同意。
  • Unanimous 有一票拒绝,最终就拒绝

不是说授权么,怎么突然引入投票的概念,是不是很懵???这里的实现就是这样。
所以我们自定义授权,就是来定义投票器的实现。

示例

定义投票器

@Slf4j
@Component
public class AuthorityDecisionVoter implements AccessDecisionVoter<FilterInvocation> {

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    private final Set<String> ANON_URL_SET = new HashSet<>();

    public AuthorityDecisionVoter() {
        ANON_URL_SET.add("/login/**");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    public int vote(Authentication authentication, FilterInvocation object, Collection<ConfigAttribute> attributes) {
        // 拿到当前请求uri
        String requestUrl = object.getRequestUrl();
        String method = object.getRequest().getMethod();


        for (String anonUrl : ANON_URL_SET) {
            if (pathMatcher.match(anonUrl, requestUrl)) {
                return ACCESS_ABSTAIN;
            }
        }

        List<String> permList = Arrays.asList(requestUrl, method + ":" + requestUrl);

        // 拿到当前用户所具有的权限.不处理了,这里是做个示例,因为每个人的场景或处理不一样,有的可能这里返回的是角色信息
        Collection<? extends GrantedAuthority> permissions = ((UserDetails) authentication.getPrincipal()).getAuthorities();

        // 示例代码,写死了,/hello/world 请求通过,其它都拒绝
        for (String perm : permList) {
            if (perm.equalsIgnoreCase("get:/hello/world")) {
                return ACCESS_GRANTED;
            }
        }

        return ACCESS_DENIED;
    }
}

定义访问决策管理

前面说过了,accessDecisionManager是决策投票器的投票结果的。

@Configuration
public class WebConfiguration {
    @Bean
    public AccessDecisionManager accessDecisionManager(ApplicationContext applicationContext,
                                                       AccessDecisionVoter authorityDecisionVoter) {
        DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
        // spel 表达式中解析bean使用的,如果用到了必须设置这个
        expressionHandler.setApplicationContext(applicationContext);
        WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
        webExpressionVoter.setExpressionHandler(expressionHandler);
        // 默认是只有webExpressionVoter这个投票器的,解析spel表达式,authorityDecisionVoter是我们自定义的,注入进来,解析的时候,两个投票器都会投票的
        List<AccessDecisionVoter<?>> decisionVoters = Arrays.asList(webExpressionVoter, authorityDecisionVoter);
        // 这个决策器表示,上面的这两个投票器如果有一个反对票,就抛出访问拒绝的异常
        UnanimousBased unanimousBased = new UnanimousBased(decisionVoters);
        return unanimousBased;
    }
}

注意下,我上面针对spel注释的说明。如果没有用到spel表达式,WebExpressionVoter (这个是默认的投票器)可以直接new一个出来。

spel的使用

演示一个spel的使用,这个不是必须的啊,这里演示下,用户认证的时候,想要打印一条日志:

@Slf4j
@Component
public class AuthenticationHandler {

    public boolean hasAuthenticated(HttpServletRequest request, Authentication authentication) {
        // 获取主体
        Object principal = authentication.getPrincipal();
        if (principal instanceof String) {
            log.error("匿名用户{}访问", principal);
            return false;
        }

        // 判断主体是否属于 UserDetails
        if (principal instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) principal;
            return userDetails.isEnabled() && userDetails.isCredentialsNonExpired();
        }
        return false;
    }
}

配置权限检查

@Component
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    private final AccessDecisionManager accessDecisionManager;

    public WebSecurityConfigurer(AccessDecisionManager accessDecisionManager) {
        this.accessDecisionManager = accessDecisionManager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 在这里自定义配置
        http.authorizeRequests()
                // 登录相关接口都允许访问
                .antMatchers("/login/**").permitAll()
                .anyRequest()
                // spel的使用
                .access("@authenticationHandler.hasAuthenticated(request, authentication)")
//                .authenticated()
                // 自定义权限检查主要是一行代码
                .accessDecisionManager(accessDecisionManager)
                .and()
				...
    }
}

基本使用示例就是这样,可以根据需要调整。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/760531.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

增长能力模型:最大限度地发挥增长团队的潜力

作为增长领导者&#xff0c;准确评估你的组织和团队成员是一项非常重要且耗时的工作。但是如果我们计划为未来的增长领导者创造一条职业道路&#xff0c;我们就需要一个流程来为整个团队提供客观的反馈&#xff0c;这就是增长能力模型的用武之地。 增长能力模型提供了一个框架&…

argc,argv

文章目录 argc 是argument count的缩写表示传入main函数中的参数个数&#xff0c;包括这个程序本身 argv 是 argument vector的缩写表示传入main函数中的参数列表&#xff0c;其中argv[0]:程序的名字(.exe) int main(int argc, char** argv) {std::cout << "argc:…

1、环境搭建

下载visual studio,社区版基本够用了,其他版本是收费的。 下载之后安装即可。 安装完成之后要求我们选择要使用的模块。这里我们勾选如图中圈起来的模块,并选择安装。因为我这边已经安装过,所以按钮变成了“关闭” 安装完毕后,重启。 重启完成之后,打开Visual studio。…

配置JDK_IDEA

配置JDK_IDEA 1.安装JDK配置环境变量1.1.安装JDK1.2.配置JDK环境变量 2.IDEA 1.安装JDK配置环境变量 1.1.安装JDK 链接: 下载JDK 1.2.配置JDK环境变量 2.IDEA 链接: IDEA官网 自行激活

分布式运用——ELK 企业级日志分析系统

分布式运用——ELK 企业级日志分析系统 一、ELK 简介1.ELK的主要组件2.可以添加的其它组件3.为什么要使用 ELK4.完整日志系统基本特征5.ELK 的工作原理&#xff1a; 二、ELK集群部署三、ELK Elasticsearch 集群部署&#xff08;在Node1、Node2节点上操作&#xff09;1&#xff…

扩散模型学习笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 前言 &#xff08;注&#xff1a;高斯分布前加乘以项改变其方差&#xff09; 深入浅出扩散模型系列&#xff1a;DDPM架构图解&#xff08;模型架构篇&#x…

【C/C++】类成员进阶——类中静态成员static

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

暑假第十一天打卡

离散&#xff1a; 今天主要学的是推理&#xff0c;感觉推理定理那一大堆公式就是假设箭头前是true&#xff0c;然后将双箭头改为单箭头&#xff0c;然后由此得到对应推理规则&#xff0c;横线上是“因为”&#xff0c;横线下是所以 推理的形式结构: (p → q) ∧ q → p 科研 …

Java编译运行程序(Windows环境)

1.运行条件&#xff1a;我们已经配好环境变量&#xff0c;且在需要编译的文件的目录下打开cmd&#xff1b;还没配好环境变量的同学可以参考&#xff08;链接最上面的推荐文章链接&#xff09;&#xff1a;http://t.csdn.cn/HPWWr 2.首先需编译目标编译的Java文件&#xff0c;…

【Linux】udp服务器实现英汉互译以及远程操作

全是好玩的小功能~ 文章目录 前言一、udp服务器实现网络在线英汉互译二、udp服务器实现网络远程操作总结 前言 在上一篇文章中我们详细的讲解了udp服务器的实现步骤&#xff0c;把用到的每一个接口都进行了详细的讲解&#xff0c;而我们在上一篇只是简单的网络通信功能&#x…

Spring中必备的自定义扩展点,结合工作中的案例,你一定用得到

文章目录 简单介绍Spring流程从问题入手介绍扩展点如何在static方法中从Spring容器中获取bean对象如何在bean实例化前后做一些自定义操作如何移除&#xff0c;修改一些BeanDefination怎样用相同的线程从Spring容器中获取同一个bean对象如何在Spring容器初始化或销毁时做一些自定…

2023年软件测试方向大厂招人和面试要求趋势

当前软件测试的行业现状是什么&#xff1f;2022年后半年经常能够在知乎、小红书、抖音等互联网平台上看到有人在抱怨软件测试行业就业困难。抱怨的一个主要内容是相比2021年测试岗位数下跌&#xff0c;几个月都找不到工作。另外一个测试岗位是有限的&#xff0c;但是应聘者在变…

Python读取excle文件,插入到数据库

一、需求背景 最近项目实践过程中遇到了一个问题&#xff1a;在使用Navicat将数据导入到PostgreSQL数据库时&#xff0c;发现时间格式的字段中的时间数值发生了变化&#xff0c;导致部分数据的时间不正确&#xff0c;故数据手动导入数据库报错。为了解决这个问题&#xff0c;决…

集群基础6——keepalived+lvs+apache

文章目录 一、环境说明二、安装apache三、配置keepalivedlvs3.1 配置lvs规则3.2 配置keepalived规则&#xff08;主&#xff09;3.3 配置keepalived规则&#xff08;备&#xff09; 四、验证 一、环境说明 先对两台后端服务器的httpd服务进行负载均衡&#xff0c;再对负载均衡服…

如何二次封装一个el-table组件并二次复用

*注:示例使用的是vue3和element进行二次封装的 首先我们来看效果图&#xff08;总共可以分为以下几个模块&#xff09;&#xff1a; 表格数据操作按钮区域表格信息提示区域表格主体内容展示区域表格分页区域 表单搜索没有封装在这里是为了降低代码的耦合性(有兴趣的可以查看我…

ChatGPT开发【一】:打造与ChatGPT默契互动的绝佳输入格式

点击加入->【OpenAI-API开发】技术交流群 文章目录 1. 导入openai库2.示例聊天API调用3.GPT-3.5-Turbo-0301的使用技巧系统消息Few-show prompt 4.计数Token数 Chatgpt由Openai最先进的型号 gpt-3.5-Turbo和 gpt-4提供支持。我们可以使用OpenAI API使用 GPT-3.5-Turbo或…

谈谈在Bitcask中用读写锁实现并发控制的性能表现

背景 最近被问了几次nutsdb事务是怎么实现的&#xff0c;也就是并发控制是怎么做的。我说&#xff0c;用一把大的读写锁&#xff0c;写事务拿到写锁&#xff0c;读事务拿读锁&#xff0c;这样子做的。提问者先是震惊&#xff0c;接着说是有一点鄙夷&#xff0c;我感觉大概心里…

【踩坑指南】Django+channels WebSocket配置

目前我搜到网上所有配置Djangochannels的教程/博客中&#xff0c;都没有提及这一点。希望能帮助你 踩的坑必须写在最前面&#xff1a; 根据文档的步骤去配置&#xff0c;每次到执行python manage.py 的时,使用的是默认的development server&#xff0c;而不是我们想要的Star…

解决:.prettierrc 配置完后,自动保存并没有格式化代码

如果你也碰到了同样的问题&#xff0c;请先确保&#xff1a; .prettierrc 文件已正确配置&#xff0c;例如我的&#xff1a; {"semi": false,"singleQuote": true,"arrowParens": "always","trailingComma": "all&qu…

卷积神经网络参数量和计算量的计算方法

本文总结了标准卷积、分组卷积和全连接层参数量和计算量的计算方法&#xff0c;如有错误&#xff0c;麻烦大家指正 一、标准卷积 假设输入特征的shape为[, , ]&#xff0c;卷积核的shape为[, , , ]&#xff0c;输出特征的shape为[, , ]&#xff0c;则&#xff0c; 标准卷积运…