想要控制好权限,这八个注解你必须知道
小伙伴们知道松哥最近在做 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
盾牌座uy能容纳6500兆个地球,如果地球也有这么大,那地球两边的人会否因为相距太远而永远无法来往了?地球如同宇宙中的一颗尘埃,这并非夸大其词,宇宙中存在着远大于地球的星球。在太阳系中,木星和太阳是体积最大的两个天体,前者是地球的1320倍,后者是地球的130万倍。然而,放眼太阳系
电商对中国经济有多大的影响?以我个人己见,电商是一种代表着,时代进步的产物,但是同时电商的存在,其实是不利于国家发展,和安全问题的,因为电商的便利,和优惠正迎合了不愿劳动的国人,让国人变得更懒惰,而电商的优惠
新能源汽车未来15年怎么发展?国家最新规划来源莲都发布国务院办公厅近日印发新能源汽车产业发展规划(20212035年),要求深入实施发展新能源汽车国家战略,推动我国新能源汽车产业高质量可持续发展,加快建设汽车强国。你关注新
高通公司的新型5G调制解调器得益于高通公司的新型无线处理器,5G可能很快就会出现在机器人,拖拉机和工厂,自动驾驶中。星期四,这家芯片制造商推出了3155GIOT调制解调器,这是它的第一款将互联网连接设备连接到
今年最能打的机子,都在这了哈喽黑粉们,欢迎来到黑马公社。在后台,询问黑马最多的产品,除了手机就是电脑。手机,黑马已经介绍过多次了,今天咱就先不说手机,我们来说说电脑。多余的话,黑马也不多说了,直接进入主题,
华为智慧屏SE系列怎么样?把摄像头变成标配,65英寸仅4299元最近华为发布了智慧屏SE系列新品电视,有55英寸和65英寸,售价3299元和4299元,引起电视潜在用户的关注。那么,这一系列新品有何特点呢,我们不妨来看看。首先,就是影音有所保证
欧易OKEx上线的NFT的市场参与度怎样?对于区块链行业外的人士而言,NFT还是一个新鲜事物。但实际上,NFT很早就已经出现了即2017年末ETH上的加密猫游戏。在这款游戏中,每个加密猫都是独一无二的品种,不存在一模一样的
谷歌公布名单,华为不在其中2019年,在特朗普的干预下,华为和谷歌这对最亲密的商业合作伙伴关系开始恶化,谷歌不但不再授权华为手机搭载谷歌GMS服务,还利用对安卓系统的主导权,要求各大安卓手机厂商海外版手机的
在线教育或迎最严监管近年来,在线教育方兴未艾,在经过了2020年的疫情催化后,在线教育更是遍地开花。在繁荣的背后,也存在着获客成本提升抢占更大市场的现象,违规超前培训超期收取费用虚假广告宣传等一系列在
7月发布?华为P50Pro再次被确认,鸿蒙OS超级镜头虽然华为受到严重打击,但是余承东坚持推新的决心还是非常强,这一点值得点赞,外界也同样期盼新旗舰能够早日到来。无独有偶,随着时间的推移,目前关于新品的爆料也逐步深入,按照外媒Phon
没有别墅不配买?100全屋覆盖的华为路由H6有机测评,快点进来!感谢华为路由器部门的支持,华为路由H6已经安装使用一个星期了,全文都是基于实际的体验得出,不是无机测评呦!一一款好的路由器是提升全家幸福指数的关键。这是阿sir