松哥给最近连载的 Spring Security 系列也录制了视频教程,感兴趣的小伙伴请戳这里->Spring Boot+Vue+微人事视频教程(Spring Boot 第十章就是 Spring Security)。
Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Security 提供的方式做授权,也可以自定义授权逻辑。一句话,你想怎么玩都可以!
今天松哥来和大家介绍一下 Spring Security 中四种常见的权限控制方式。
- 
表达式控制 URL 路径权限 
 - 
表达式控制方法权限 
 - 
使用过滤注解 
 - 
动态权限 
 
四种方式,我们分别来看。
本文是 Spring Security 系列第 30 篇,阅读前面文章有助于更好的理解本文:
- 
挖一个大坑,Spring Security 开搞! 
 - 
松哥手把手带你入门 Spring Security,别再问密码怎么解密了 
 - 
手把手教你定制 Spring Security 中的表单登录 
 - 
Spring Security 做前后端分离,咱就别做页面跳转了!统统 JSON 交互 
 - 
Spring Security 中的授权操作原来这么简单 
 - 
Spring Security 如何将用户数据存入数据库? 
 - 
Spring Security+Spring Data Jpa 强强联手,安全管理只有更简单! 
 - 
Spring Boot + Spring Security 实现自动登录功能 
 - 
Spring Boot 自动登录,安全风险要怎么控制? 
 - 
在微服务项目中,Spring Security 比 Shiro 强在哪? 
 - 
SpringSecurity 自定义认证逻辑的两种方式(高级玩法) 
 - 
Spring Security 中如何快速查看登录用户 IP 地址等信息? 
 - 
Spring Security 自动踢掉前一个登录用户,一个配置搞定! 
 - 
Spring Boot + Vue 前后端分离项目,如何踢掉已登录用户? 
 - 
Spring Security 自带防火墙!你都不知道自己的系统有多安全! 
 - 
什么是会话固定攻击?Spring Boot 中要如何防御会话固定攻击? 
 - 
集群化部署,Spring Security 要如何处理 session 共享? 
 - 
松哥手把手教你在 SpringBoot 中防御 CSRF 攻击!so easy! 
 - 
要学就学透彻!Spring Security 中 CSRF 防御源码解析 
 - 
Spring Boot 中密码加密的两种姿势! 
 - 
Spring Security 要怎么学?为什么一定要成体系的学习? 
 - 
Spring Security 两种资源放行策略,千万别用错了! 
 - 
松哥手把手教你入门 Spring Boot + CAS 单点登录 
 - 
Spring Boot 实现单点登录的第三种方案! 
 - 
Spring Boot+CAS 单点登录,如何对接数据库? 
 - 
Spring Boot+CAS 默认登录页面太丑了,怎么办? 
 - 
用 Swagger 测试接口,怎么在请求头中携带 Token? 
 - 
Spring Boot 中三种跨域场景总结 
 - 
Spring Boot 中如何实现 HTTP 认证? 
 
1.表达式控制 URL 路径权限
首先我们来看第一种,就是通过表达式控制 URL 路径权限,这种方式松哥在之前的文章中实际上和大家讲过,这里我们再来稍微复习一下。
Spring Security 支持在 URL 和方法权限控制时使用 SpEL 表达式,如果表达式返回值为 true 则表示需要对应的权限,否则表示不需要对应的权限。提供表达式的类是 SecurityExpressionRoot:

可以看到,SecurityExpressionRoot 有两个实现类,表示在应对 URL 权限控制和应对方法权限控制时,分别对 SpEL 所做的拓展,例如在基于 URL 路径做权限控制时,增加了 hasIpAddress 选项。
我们来看下 SecurityExpressionRoot 类中定义的最基本的 SpEL 有哪些:

可以看到,这些都是该类对应的表达式,这些表达式我来给大家稍微解释下:

这是最基本的,在它的继承类中,还有做一些拓展,我这个我就不重复介绍了。
如果是通过 URL 进行权限控制,那么我们只需要按照如下方式配置即可:
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("admin")
            .antMatchers("/user/**").hasAnyRole("admin", "user")
            .anyRequest().authenticated()
            .and()
            ...
}
这里表示访问 /admin/** 格式的路径需要 admin 角色,访问 /user/** 格式的路径需要 admin 或者 user 角色。
2.表达式控制方法权限
当然,我们也可以通过在方法上添加注解来控制权限。
在方法上添加注解控制权限,需要我们首先开启注解的使用,在 Spring Security 配置类上添加如下内容:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    ...
}
这个配置开启了三个注解,分别是:
- 
@PreAuthorize:方法执行前进行权限检查 
 - 
@PostAuthorize:方法执行后进行权限检查 
 - 
@Secured:类似于 @PreAuthorize 
 
这三个结合 SpEL 之后,用法非常灵活,这里和大家稍微分享几个 Demo。
@Service
public class HelloService {
    @PreAuthorize("principal.username.equals('javaboy')")
    public String hello() {
        return "hello";
    }
    @PreAuthorize("hasRole('admin')")
    public String admin() {
        return "admin";
    }
    @Secured({"ROLE_user"})
    public String user() {
        return "user";
    }
    @PreAuthorize("#age>98")
    public String getAge(Integer age) {
        return String.valueOf(age);
    }
}
- 
第一个 hello 方法,注解的约束是,只有当前登录用户名为 javaboy 的用户才可以访问该方法。 
 - 
第二个 admin 方法,表示访问该方法的用户必须具备 admin 角色。 
 - 
第三个 user 方法,表示方法该方法的用户必须具备 user 角色,但是注意 user 角色需要加上 
ROLE_前缀。
 - 
第四个 getAge 方法,表示访问该方法的 age 参数必须大于 98,否则请求不予通过。 
 
可以看到,这里的表达式还是非常丰富,如果想引用方法的参数,前面加上一个 # 即可,既可以引用基本类型的参数,也可以引用对象参数。
缺省对象除了 principal ,还有 authentication(参考第一小节)。
3.使用过滤注解
Spring Security 中还有两个过滤函数 @PreFilter 和 @PostFilter,可以根据给出的条件,自动移除集合中的元素。
@PostFilter("filterObject.lastIndexOf('2')!=-1")
public List<String> getAllUser() {
    List<String> users = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        users.add("javaboy:" + i);
    }
    return users;
}
@PreFilter(filterTarget = "ages",value = "filterObject%2==0")
public void getAllAge(List<Integer> ages,List<String> users) {
    System.out.println("ages = " + ages);
    System.out.println("users = " + users);
}
- 
在 getAllUser 方法中,对集合进行过滤,只返回后缀为 2 的元素,filterObject 表示要过滤的元素对象。 
 - 
在 getAllAge 方法中,由于有两个集合,因此使用 filterTarget 指定过滤对象。 
 
4.动态权限
动态权限主要通过重写拦截器和决策器来实现,这个我在 vhr 的文档中有过详细介绍,大家在公众号【江南一点雨】后台回复 888 可以获取文档,我就不再赘述了。
5.小结
好啦,今天就喝小伙伴们稍微聊了一下 Spring Security 中的授权问题,当然这里还有很多细节,后面松哥再和大家一一细聊。
如果小伙伴们觉得有收获,记得点个在看鼓励下松哥哦~
今日干货
66666
回复:666
公众号后台回复 ssm,免费获取松哥纯手敲的 SSM 框架学习干货。
本文分享自微信公众号 - 江南一点雨(a_javaboy)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

