Spring Security 实战干货:基于注解的接口角色访问控制

1. 前言

欢迎阅读 Spring Security 实战干货 系列文章 。在上一篇 基于配置的接口角色访问控制 我们讲解了如何通过 javaConfig 的方式配置接口的角色访问控制。其实还有一种更加灵活的配置方式 基于注解 。今天我们就来探讨一下。DEMO 获取方式在文末。

2. Spring Security 方法安全

Spring Security 基于注解的安全认证是通过在相关的方法上进行安全注解标记来实现的。

2.1 开启全局方法安全

我们可以在任何 @Configuration实例上使用 @EnableGlobalMethodSecurity 注解来启用全局方法安全注解功能。该注解提供了三种不同的机制来实现同一种功能,所以我们单独开一章进行探讨。

3. @EnableGlobalMethodSecurity 注解

 @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
 @Target(value = { java.lang.annotation.ElementType.TYPE })
 @Documented
 @Import({ GlobalMethodSecuritySelector.class })
 @EnableGlobalAuthentication
 @Configuration
 public @interface EnableGlobalMethodSecurity {
 
    /**
     * 基于表达式进行方法访问控制
     */
    boolean prePostEnabled() default false;
 
    /**
     * 基于 @Secured 注解
     */
    boolean securedEnabled() default false;
 
    /**
     * 基于 JSR-250 注解
     */
    boolean jsr250Enabled() default false;

    boolean proxyTargetClass() default false;

    int order() default Ordered.LOWEST_PRECEDENCE;
 }

@EnableGlobalMethodSecurity 源码中提供了 prePostEnabledsecuredEnabledjsr250Enabled 三种方式。当你开启全局基于注解的方法安全功能时,也就是使用 @EnableGlobalMethodSecurity 注解时我们需要选择使用这三种的一种或者其中几种。我们接下来将分别介绍它们。

4. 使用 prePostEnabled

如果你在 @EnableGlobalMethodSecurity 设置 prePostEnabledtrue ,则开启了基于表达式的方法安全控制。通过表达式运算结果的布尔值来决定是否可以访问(true 开放, false 拒绝 )。
有时您可能需要执行开启 prePostEnabled 复杂的操作。对于这些实例,您可以扩展 GlobalMethodSecurityConfiguration,确保子类上存在@EnableGlobalMethodSecurity(prePostEnabled = true) 。例如,如果要提供自定义 MethodSecurityExpressionHandler :

 @EnableGlobalMethodSecurity(prePostEnabled = true)
 public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
     @Override
     protected MethodSecurityExpressionHandler createExpressionHandler() {
         // ... create and return custom MethodSecurityExpressionHandler ...
         return expressionHandler;
     }
 }

上面示例属于高级操作,一般没有必要。无论是否继承GlobalMethodSecurityConfiguration 都将会开启四个注解。 @PreAuthorize@PostAuthorize 侧重于方法调用的控制;而 @PreFilter@PostFilter 侧重于数据的控制。

4.1 @PreAuthorize

在标记的方法调用之前,通过表达式来计算是否可以授权访问。接下来我来总结以下常用的表达式。

  • 基于 SecurityExpressionOperations 接口的表达式,也就是我们在上一文javaConfig 配置。示例: @PreAuthorize("hasRole('ADMIN')") 必须拥有 ROLE_ADMIN 角色。
  • 基于 UserDetails 的表达式,此表达式用以对当前用户的一些额外的限定操作。示例:@PreAuthorize("principal.username.startsWith('Felordcn')") 用户名开头为 Felordcn 的用户才能访问。
  • 基于对入参的 SpEL表达式处理。关于 SpEL 表达式可参考官方文档。或者通过关注公众号:Felordcn 来获取相关资料。 示例: @PreAuthorize("#id.equals(principal.username)") 入参 id 必须同当前的用户名相同。

4.2 @PostAuthorize

在标记的方法调用之后,通过表达式来计算是否可以授权访问。该注解是针对 @PreAuthorize 。区别在于先执行方法。而后进行表达式判断。如果方法没有返回值实际上等于开放权限控制;如果有返回值实际的结果是用户操作成功但是得不到响应。

4.3 @PreFilter

基于方法入参相关的表达式,对入参进行过滤。分页慎用!该过程发生在接口接收参数之前。 入参必须为 java.util.Collection 且支持 remove(Object) 的参数。如果有多个集合需要通过 filterTarget=<参数名> 来指定过滤的集合。内置保留名称 filterObject 作为集合元素的操作名来进行评估过滤。

样例:

// 入参为Collection<String> ids   测试数据 ["Felordcn","felord","jetty"]

// 过滤掉  felord jetty  为  Felordcn
@PreFilter(value = "filterObject.startsWith('F')",filterTarget = "ids")
// 如果 当前用户持有 ROLE_AD 角色  参数都符合  否则 过滤掉不是 f 开头的   
// DEMO 用户不持有 ROLE_AD 角色  故而 集合只剩下 felord
@PreFilter("hasRole('AD') or filterObject.startsWith('f')")

4.4 @PostFilter

@PreFilter 不同的是, 基于返回值相关的表达式,对返回值进行过滤。分页慎用!该过程发生接口进行数据返回之前。 相关测试与 @PreFilter 相似,参见文末提供的 DEMO。

5. 使用 securedEnabled

如果你在 @EnableGlobalMethodSecurity 设置 securedEnabledtrue ,就开启了角色注解 @Secured ,该注解功能要简单的多,默认情况下只能基于角色(默认需要带前缀 ROLE_)集合来进行访问控制决策。

该注解的机制是只要其声明的角色集合(value)中包含当前用户持有的任一角色就可以访问。也就是 用户的角色集合和 @Secured 注解的角色集合要存在非空的交集。 不支持使用 SpEL 表达式进行决策。

6. 使用 jsr250Enabled

启用 JSR-250 安全控制注解,这属于 JavaEE 的安全规范(现为 jakarta 项目)。一共有五个安全注解。如果你在 @EnableGlobalMethodSecurity 设置 jsr250Enabledtrue ,就开启了 JavaEE 安全注解中的以下三个:

  • @DenyAll 拒绝所有的访问
  • @PermitAll 同意所有的访问
  • @RolesAllowed 用法和 5. 中的 @Secured 一样。

7. 总结

今天讲解了 Spring Security 另一种基于注解的静态配置。相比较基于 javaConfig 的方式要灵活一些、粒度更细、基于 SpEL 表达式可以实现更加强大的功能。但是这两种的方式还是基于编程的静态方式,具有一定的局限性。更加灵活的方式应该是动态来处理用户的角色和资源的映射关系,这是以后我们将要解决的问题。
本次的 DEMO 可通过 关注微信公众号:Felordcn 回复 ss09 获取。

关注公众号:码农小胖哥,获取更多资讯

个人博客:https://felord.cn

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,137评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,824评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,465评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,131评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,140评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,895评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,535评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,435评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,952评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,081评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,210评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,896评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,552评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,089评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,198评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,531评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,209评论 2 357

推荐阅读更多精彩内容