在此之前,我们实现了基于内存模型、默认数据库模型和自定义数据库模型的认证和授权功能。然而,无论如何,我们对接口的拦截限制都是通过编写SecurityConfig配置类来实现的 (Http Security http)方法中,通过http. authorize Requests ( ). antMatchers ("/admin/**")...这样的代码进行权限控制。
虽然这种权限控制方法也可以拦截或释放某些接口,但它不够灵活。事实上,Spring Security拦截或放行接口的写作方法还有其他方法。接下来请向我学习!
一. 权限控制模式Spring Security 我们可以使用它 Spring Security 提供的默认方式进行授权,也可以进行自定义授权,总之Spring Security中权限控制的实现方式是比较灵活多样的。Spring Security 在中间,有四种常见的权限控制方法可以拦截或释放接口:
- 权限控制采用Ant表达式实现;
- 结合Spel表达式,使用授权注释实现权限控制;
- 使用过滤器注释实现权限控制;
- 利用动态权限实现权限控制。
我们将分别解释上述四种权限控制方法。
二. 使用Ant表达式实现权限控制Ant表达式权限控制的使用是我们以前使用的权限控制方法。在实现代码之前,我将简要分析这种方法的底层实现。
1. Spring Security中的权限控制方法Spring Security中有一个Security Expression Operations接口定义了用户权限设置的一系列方法,如下图所示:
Securityexpresionoperations接口中的Security
这些方法如下图所示:
2. Spring Security中的权限控制粒度这个接口有一个SecurityExpressionrot子类,它提供了基于表达式的权限控制实现方法。这个SecurityExpresionroot 实现URLL有两个子类 Web接口粒度的权限控制和 方法粒度的权限控制如下图所示:
3. 代码实现从上面的小节中,我们知道在Spring 在Security中,支持两种粒度的权限控制,即URL Web接口粒度 以及方法粒度,这里所谓的Ant表达式授权控制方法是通过Ant表达式进行控制 URL 访问界面的权限。
因此,如果需要对URL接口粒度进行权限控制,则可以按照以下代码实现:
@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").antMatchers("/user/**").hasRole("USER").antMatchers("/visitor/**").permitAll().anyRequest().authenticated().and().formLogin().permitAll().and()///伪造跨域请求进行保护---->csrf:用户带登录状态的cookie攻击手段.csrf().disable();}
在上述代码中,/admin/ 需要格式路径 admin 角色可以访问,/user/需要格式路径 user 角色可以访问,/visitor/** 格式路径可直接访问,其他接口路径需登录后方可访问。
三. 结合Spel表达式,使用授权注释实现权限控制1. 授权注解除了使用上述Ant表达式进行授权外,我们还可以在方法中添加授权注释进行权限控制,常用的授权注释有三种:
- @PreAuthorize:执行方法前进行权限检查;
- @PostAuthorize:执行方法后进行权限检查;
- @Secured:类似于 @PreAuthorize。
为了使用上述三个授权注释进行权限控制,我们首先需要使用@enableglobalmethodsecurity注释来打开授权注释功能,代码如下:
@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter {...}
然后在具体的接口方法中使用授权注释进行权限控制,代码如下:
@RestControllerpublic class UserController {@Secured({"ROLE_USER"})//@PreAuthorize("principal.username.equals('user')")@GetMapping("/user/hello")public String helloUser() {return "hello, user";}@PreAuthorize("hasRole('ADMIN')")@GetMapping("/admin/hello")public String helloAdmin() {return "hello, admin";}@PreAuthorize("#age>100")@GetMapping("/age")public String getAge(@RequestParam("age") Integer age) {return String.valueOf(age);}@GetMapping("/visitor/hello")public String helloVisitor() {return "hello, visitor";}}
可以看出,这种写法显然比使用Ant表达式控制权限更灵活、更方便,因此这种写法在开发过程中非常常用。
四. 使用过滤器注解实现权限控制1. 过滤器注释简介Spring Security还提供了另外两个注释,即@PreFilter和@PostFilter,这两个注释可以过滤集合类型的参数或返回值。使用@PreFilter和@PostFilter时,Spring Security将相应的表达式结果移除为false的元素。
2. @PostFilter的用法@Postfilter注释主要用于过滤集合类型的返回值。filterobject是@postfilter中的内置表达式,表示集合中的元素对象。
@Slf4j@RestControllerpublic class FilterController {/*** 在结果中只返回id为偶数的user元素。* filterobject是@prefilter和@postfilter中的内置表达式,表示当前对象的集合。*/@PostFilter("filterObject.id%2==0")@GetMapping("/users")public List getAllUser() {List users = new ArrayList<>();for (int i = 0; i < 10; i++) {users.add(new User(i, "yyg-" + i));}return users;}}
当我们启动浏览器进行测试时,我们可以看到测试接口中只返回id作为偶数的元素。
3. @PreFilter的用法
使用@Prefilter也可以过滤集合类型的参数。当@Prefilter标记的方法中有多个集合类型的参数时,可以通过@Prefilter的filtertarget属性来指定过滤哪个参数;filterbject是@Prefilter中的内置表达式,表示集合中的元素对象。
为便于测试,我们在Service层中进行过滤操作,然后在Controller层中调用。
FilterService类中的方法定义:
@Slf4j@Servicepublic class FilterService {/*** 当@PreFilter标记的方法中有多个集合类型的参数时,* 通过@Prefilter的filtertarget属性,可以指定当前过滤的参数。*/@PreFilter(filterTarget = "ids", value = "filterObject%2==0")public List doFilter(List ids, List users) {log.warn("ids=" + ids.toString());log.warn("users=" + users.toString());return ids;}}
在Controller中定义一个测试接口:
@Slf4j@RestControllerpublic class FilterController {/*** 在结果中只返回id为偶数的user元素。* filterobject是@prefilter和@postfilter中的内置表达式,表示当前对象的集合。*/@PostFilter("filterObject.id%2==0")@GetMapping("/users")public List getAllUser() {List users = new ArrayList<>();for (int i = 0; i < 10; i++) {users.add(new User(i, "yyg-" + i));}return users;}@Autowiredprivate FilterService filterService;@GetMapping(/users2)public List getUserInfos() {List ids = new ArrayList<>();for (int i = 0; i < 10; i++) {ids.add(i);}List users = new ArrayList<>();for (int i = 0; i < 10; i++) {users.add(new User(i, "yyg-" + i));}return filterService.doFilter(ids, users);}}
当我们启动浏览器进行测试时,我们可以看到测试界面中只返回id作为偶数的元素。
4. 代码结构下图为上述案例的代码结构,请参考:
五. 利用动态权限实现权限控制我们知道RABC的标准, 权限系统需要支持动态配置,Spring 默认情况下,Security在代码中约定了权限。在真实的业务场景中,通常需要动态配置角色访问权限,即在运行过程中配置url对应的访问角色。
而Spring Security中的动态权限主要是通过重写拦截器和决策者来实现的。最简单的方法是定制Filter来完成权限判断。事实上,这里涉及的代码基本上与Spring有关 Security关系不大,主要是在传统的Filter中实现的,这里就不描述了,感兴趣的同学可以自己实现!
到目前为止,我将向您介绍Spring Security中有四种控制权限的方法,你可以根据自己的项目需要进行选择。