想要控制好权限,这八个注解你必须知道
小伙伴们知道松哥最近在做 TienChin 项目,项目里涉及到一个问题,那就是数据权限过滤,有不少小伙伴对这个问题觉得特别迷,希望松哥松哥能整一篇文章讲讲,好吧,安排。
在讲数据权限之前,我们有必要先和大家介绍一下 Spring Security 中的权限注解,把这个捋清楚了,再去看 TienChin 项目的权限注解,你就会发现非常容易了。 1. Spring Security 中的权限注解
Spring Security 中支持多种权限注解,首先我们需要通过 @EnableGlobalMethodSecurity 注解开启权限注解的使用,方式如下: @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true, jsr250Enabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { }
在这个注解中我们一共设置了三个属性: prePostEnabled :这个表示开启 Spring Security 提供的四个权限注解,@PostAuthorize 、@PostFilter 、@PreAuthorize 以及 @PreFilter ,这四个注解支持权限表达式,支持 SpEL,功能比较丰富。 securedEnabled :开启 Spring Security 提供的 @Secured 注解,该注解不支持权限表达式。 jsr250Enabled :开启 JSR-250 提供的注解,主要包括@DenyAll 、@PermitAll 以及 @RolesAllowed 三个注解,这些注解也不支持权限表达式。
以上这些注解的含义分别如下: @PostAuthorize :在目标方法执行之后进行权限校验。 @PostFilter :在目标方法执行之后对方法的返回结果进行过滤。 @PreAuthorize :在目标方法执行之前进行权限校验。 @PreFilter :在目标方法执行之前对方法参数进行过滤。 @Secured :访问目标方法必须具备相应的角色。 @DenyAll :拒绝所有访问。 @PermitAll :允许所有访问。 @RolesAllowed :访问目标方法必须具备相应的角色。
这是所有的,当然一般来说我们只要设置 prePostEnabled=true 就够用了,也就是前四个注解基本上就能满足大部分需求了。
另外还有一种比较"古老"的方法配置基于方法的权限管理,那就是通过 XML 文件配置方法拦截规则,目前已经很少有用 XML 文件来配置 Spring Security 了,所以对于这种方式我们不做过多介绍。感兴趣的小伙伴可以查看官网的相关介绍: https://docs.spring.io/spring-security/ site/docs/5.4.0/reference/html5/#secure-object-impls 。 2. 权限注解实践
接下来我们通过几个简单的案例来学习一下上面几种不同注解的用法。
首先创建一个 Spring Boot 项目,引入 Web 和 Spring Security 依赖,项目创建完成后,添加如下配置文件: @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class SecurityConfig{ }
为了方便起见,我们将使用单元测试进行验证,所以这里就不进行额外的配置了,通过 @EnableGlobalMethodSecurity 注解开启其他权限注解的使用即可。
接下来创建一个 User 类以备后续使用: public class User { private Integer id; private String username; //省略getter/setter }
准备工作完成后,我们来逐个讲解一下前面注解的用法。 2.1 @PreAuthorize
@PreAuthorize 注解可以在目标方法执行之前对其进行安全校验,在安全校验时,可以直接使用权限表达式。例如可以定义如下方法: @Service public class HelloService { @PreAuthorize("hasRole("ADMIN")") public String hello() { return "hello"; } }
这里使用了权限表达式 hasRole,表示执行该方法必须具备 ADMIN 角色才可以访问,否则不可以访问。接下来我们在单元测试中来测试该方法: @SpringBootTest class BasedOnMethodApplicationTests { @Autowired HelloService helloService; @Test @WithMockUser(roles = "ADMIN") void preauthorizeTest01() { String hello = helloService.hello(); assertNotNull(hello); assertEquals("hello", hello); } }
通过 @WithMockUser(roles = "ADMIN") 注解设定当前执行的用户角色是 ADMIN,然后调用 helloService 中的方法进行测试即可。如果将用户角色设置为其他字符,那单元测试就不会通过。
当然,这里除了 hasRole 表达式之外,也可以使用其他权限表达式,甚至也可以同时使用多个权限表达式,如下所示: @Service public class HelloService { @PreAuthorize("hasRole("ADMIN") and authentication.name=="javaboy"") public String hello() { return "hello"; } }
表示访问者名称必须是 javaboy,而且还需要同时具备 ADMIN 角色,才可以访问该方法。此时通过如下代码对其进行测试: @SpringBootTest class BasedOnMethodApplicationTests { @Autowired HelloService helloService; @Test @WithMockUser(roles = "ADMIN",username = "javaboy") void preauthorizeTest01() { String hello = helloService.hello(); assertNotNull(hello); assertEquals("hello", hello); } }
在 @PreAuthorize 注解中,还可以通过 # 引用方法的参数,并对其进行校验,例如如下方法表示请求者的用户名必须等于方法参数 name 的值,方法才可以被执行: @PreAuthorize("authentication.name==#name") public String hello(String name) { return "hello:" + name; }
测试方法如下: @Test @WithMockUser(username = "javaboy") void preauthorizeTest02() { String hello = helloService.hello("javaboy"); assertNotNull(hello); assertEquals("hello:javaboy", hello); }
当模拟的用户名和方法参数相等时,单元测试就可以通过。 2.2 @PreFilter
@PreFilter 主要是对方法的请求参数进行过滤,它里边包含了一个内置对象 filterObject 表示要过滤的参数,如果方法只有一个参数,则内置的 filterObject 对象就代表该参数;如果方法有多个参数,则需要通过 filterTarget 来指定 filterObject 到底代表哪个对象: @PreFilter(value = "filterObject.id%2!=0",filterTarget = "users") public void addUsers(List users, Integer other) { System.out.println("users = " + users); }
上面代码表示对方法参数 users 进行过滤,将 id 为奇数的 user 保留。
然后通过单元测试对该方法进行测试: @Test @WithMockUser(username = "javaboy") void preFilterTest01() { List users = new ArrayList<>(); for (int i = 0; i < 10; i++) { users.add(new User(i, "javaboy:" + i)); } helloService.addUsers(users, 99); }
执行单元测试方法,addUsers 方法中只会打印出 id 为奇数的 user 对象。 2.3 @PostAuthorize
@PostAuthorize 是在目标方法执行之后进行权限校验。可能有小伙伴会觉得奇怪,目标方法都执行完了才去做权限校验意义何在?其实这个主要是在 ACL 权限模型中会用到,目标方法执行完毕后,通过 @PostAuthorize 注解去校验目标方法的返回值是否满足相应的权限要求。
不过呢,即使你的权限模型不是 ACL,也没关系,也有可能用到这个注解,反正记得它的作用:方法执行完成后,根据用户的权限信息过滤出需要返回给用户的数据。
从技术角度来讲, @PostAuthorize 注解中也可以使用权限表达式,但是在实际开发中权限表达式一般都是结合 @PreAuthorize 注解一起使用的。@PostAuthorize 包含一个内置对象 returnObject,表示方法的返回值,开发者可以对返回值进行校验: @PostAuthorize("returnObject.id==1") public User getUserById(Integer id) { return new User(id, "javaboy"); }
这个表示方法返回的 user 对象的 id 必须为 1,调用才会顺利通过,否则就会抛出异常。
然后通过单元测试对该方法进行测试: @Test @WithMockUser(username = "javaboy") void postAuthorizeTest01() { User user = helloService.getUserById(1); assertNotNull(user); assertEquals(1,user.getId()); assertEquals("javaboy",user.getUsername()); }
如果调用时传入的参数为 1,单元测试就会顺利通过。 2.4 @PostFilter
@PostFilter 注解是在目标方法执行之后,对目标方法的返回结果进行过滤,该注解中包含了一个内置对象 filterObject,表示目标方法返回的集合/数组中的具体元素: @PostFilter("filterObject.id%2==0") public List getAll() { List users = new ArrayList<>(); for (int i = 0; i < 10; i++) { users.add(new User(i, "javaboy:" + i)); } return users; }
这段代码表示 getAll 方法的返回值 users 集合中 user 对象的 id 必须为偶数。
然后我们通过单元测试对其进行测试,代码如下: @Test @WithMockUser(roles = "ADMIN") void postFilterTest01() { List all = helloService.getAll(); assertNotNull(all); assertEquals(5, all.size()); assertEquals(2,all.get(1).getId()); } 2.5 @Secured
@Secured 注解也是 Spring Security 提供的权限注解,不同于前面四个注解,该注解不支持权限表达式,只能做一些简单的权限描述。 @Secured({"ROLE_ADMIN","ROLE_USER"}) public User getUserByUsername(String username) { return new User(99, username); }
这段代码表示用户需要具备 ROLE_ADMIN 或者 ROLE_USER 角色,才能访问 getUserByUsername 方法。
然后我们通过单元测试对其进行测试,代码如下: @Test @WithMockUser(roles = "ADMIN") void securedTest01() { User user = helloService.getUserByUsername("javaboy"); assertNotNull(user); assertEquals(99,user.getId()); assertEquals("javaboy", user.getUsername()); }
注意,这里不需要给角色添加 ROLE_ 前缀,系统会自动添加。 2.6 @DenyAll
@DenyAll 是 JSR-250 提供的方法注解,看名字就知道这是拒绝所有访问: @DenyAll public String denyAll() { return "DenyAll"; }
然后我们通过单元测试对其进行测试,代码如下: @Test @WithMockUser(username = "javaboy") void denyAllTest01() { helloService.denyAll(); }
在单元测试过程中,就会抛出异常。 2.7 @PermitAll
@PermitAll 也是 JSR-250 提供的方法注解,看名字就知道这是允许所有访问: @PermitAll public String permitAll() { return "PermitAll"; }
然后我们通过单元测试对其进行测试,代码如下: @Test @WithMockUser(username = "javaboy") void permitAllTest01() { String s = helloService.permitAll(); assertNotNull(s); assertEquals("PermitAll", s); } 2.8 @RolesAllowed
@RolesAllowed 也是 JSR-250 提供的注解,可以添加在方法上或者类上,当添加在类上时,表示该注解对类中的所有方法生效;如果类上和方法上都有该注解,并且起冲突,则以方法上的注解为准。我们来看一个简单的案例: @RolesAllowed({"ADMIN","USER"}) public String rolesAllowed() { return "RolesAllowed"; }
这个表示访问 rolesAllowed 方法需要具备 ADMIN 或者 USER 角色,然后我们通过单元测试对其进行测试,代码如下: @Test @WithMockUser(roles = "ADMIN") void rolesAllowedTest01() { String s = helloService.rolesAllowed(); assertNotNull(s); assertEquals("RolesAllowed", s); }
这就是常见的方法权限注解。
好啦,今天就先和小伙伴们介绍这么多,下面文章我再和小伙伴们介绍这些注解在 TienChin 项目中的应用。
原文链接:https://mp.weixin.qq.com/s/1NlWRwiBs8dl3Lu40haz5Q
五金小程序在线搭建,简单的微信商场搭建方法分享科技水平的提高,人们在生活方面对五金建材的质量要求也日渐有改变。在五金门店中虽然可以选到便宜的五金建材,但种类参差不齐,容易出现鱼龙混珠,消费者极易被误导消费,所以我们需要给消费者
2008年中国平安暴跌74。56的原因分析2008年,中国平安曾经股价暴跌了74。56因为经过了多年的分红送股除权后,现在我们只能通过不复权的K线走势去看看当年的股价趋势。2008年中国平安最高价为112。9元,最低价为1
格力电器股价明天很可能跌停板今天格力电器公布3季度业绩今天格力电器披露第三季度报,公司前三季度营业收入1381。35亿元,同比增9。73净利润156。45亿元,同比增长14。21。其中第三季度净利61。88亿
招商银行零售业务风险点正在增加今天招商银行公告了三季度报告2021年19月,本集团实现营业收入2,514。10亿元,同比增长13。54实现归属于本行股东的净利润936。15亿元,同比增长22。21实现净利息收入
油价与股市的走势关系原油是大部分工业的基础原油是现代工业的基础,原油的作用非常大首先,原油炼制生产的汽油煤油柴油燃料油以及液化气是能源的主要供应者,是主要机动车飞机轮船等主要的动力来源,而且这也是原油
怎么做一个吸引人的抽奖营销活动?对于商家来说,一场抽奖活动不仅可以提升门店品牌知名度,吸引客户,还能刺激消费,让销量激增。今天小编就以幸运大转盘抽奖活动为例,给大家演示一下如何制作一个页面美观且吸引人的抽奖营销活
华为商城商品图片怎么保存作为十大智能手机品牌之一,华为市场越来越强大,不少商家都会对华为进行宣传,那么在收集宣传素材的时候我们要怎么样以最快的方法去采集呢,今天就给大家分享一个方法,能够快速采集华为商城的
社区团购小程序如何开发,快速搭建的方法分享随着互联网快速发展,现在越来越多人选择网上购物,实体店的客流量也开始日渐消退。随着网购的趋势日渐强大,也衍生了各种不一样的网购方式,其中社区团购发展是最为迅速。随着越来越多的商家加
如何开小程序店铺,分享怎么在微信开店铺小程序商家店铺小程序小店铺做微信小程序,花几万几十万请外包小程序公司开发,显然不是很划算,之前我发布的一个关于制作小程序各种方法的视频,评论区的朋友就跟我说,他是找的公司专属定制的,接连找了两家开发公
米拍摄影高清相册图集怎么批量下载到本地电脑上米拍摄影社区有着海量优质图片素材,有没有什么方法可以批量下载这里的高清图片呢?今天,小编在这里跟大家分享一个实用的小工具,帮你快速采集米拍摄影相册的优质图片素材,再也不用担心图片素
微信调查问卷如何制作?看这里你就知道了微信调查问卷如何制作?这是网络上关注人数比较多的一个问题,今天小编就这个问题给大家讲解一下。一起来看看微信调查问卷是怎么制作的吧!其实方法还是很简单的,找一个操作简单且专业靠谱的第