专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员的那些事  ·  清华大学:DeepSeek + ... ·  昨天  
程序员的那些事  ·  OpenAI ... ·  14 小时前  
OSC开源社区  ·  宇树王兴兴早年创业分享引围观 ·  2 天前  
OSC开源社区  ·  升级到Svelte ... ·  3 天前  
程序员小灰  ·  DeepSeek做AI代写,彻底爆了! ·  4 天前  
51好读  ›  专栏  ›  SegmentFault思否

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

SegmentFault思否  · 公众号  · 程序员  · 2019-11-26 12:02

正文

本文转载自 SegmentFault 社区

作者:码农小胖哥






前言



欢迎阅读 Spring Security 实战干货系列文章。 对于受限的访问资源,并不是对所有认证通过的用户开放的。 比如 A 用户的角色是会计,那么他就可以访问财务相关的资源。 B 用户是人事,那么他只能访问人事相关的资源。 我们在 一文中也对基于角色的访问控制的相关概念进行了探讨。

在实际开发中我们如何对资源进行角色粒度的管控呢? 今天我来告诉你 Spring Security 是如何来解决这个问题的。




将角色写入 UserDetails



我们使用 UserDetailsService 的 加载 UserDetails 时也会把用户的 GrantedAuthority  权限集写入其中。 你可以将角色持久化并在这个点进行注入然后配置访问策略,后续的问题交给 Spring Security 。



在 HttpSecurity 中进行配置角色



我们可以通过配置 WebSecurityConfigurerAdapter 中的 HttpSecurity 来控制接口的角色访问。

1. 通过判断用户是否持有角色来进行访问控制

httpSecurity.authorizeRequests().antMatchers("/foo/test").hasRole("ADMIN")

表示持有 ROLE_ADMIN 角色的用户才能访问 /foo/test 接口。 注意: hasRole(String role) 方法入参不能携带前缀 ROLE_ 。 我们来查看 SecurityExpressionRoot 中相关源码:

 public final boolean hasRole(String role) {
       return hasAnyRole(role);
   }

很明显 hasRole 方法源于 hasAnyRole (持有任何其中角色之一,就能满足访问条件,用于一个接口开放给多个角色访问时) :

public final boolean hasAnyRole(String... roles) {
       return hasAnyAuthorityName(defaultRolePrefix, roles);
   }
 
如果某接口开放给多个角色,比如 /foo/test 开放了 ROLE_APP 和 ROLE_ADMIN 可以这么写:
httpSecurity.authorizeRequests().antMatchers("/foo/test").hasAnyRole("APP","ADMIN")

hasAnyRole 方法最终的实现为 hasAnyAuthorityName(String prefix, String... roles):

private boolean hasAnyAuthorityName(String prefix, String... roles) {
       Set roleSet = getAuthoritySet();

       for (String role : roles) {
           String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
           if (roleSet.contains(defaultedRole)) {
               return true;
           }
       }

       return false;
   }
 
上面才是根本的实现, 需要一个 prefix 和每一个 role 进行拼接,然后用户的角色集合 roleSet 中包含了就返回true 放行,否则就 false 拒绝。 默认的 prefix 为 defaultRolePrefix= ROLE_ 。

2. 通过判断用户的 GrantedAuthority 来进行访问控制


我们也可以通过 hasAuthority 和 hasAnyAuthority 来判定。 其实底层实现和 hasAnyRole 方法一样,只不过 prefix 为 null 。 也就是你写入的 GrantedAuthority 是什么样子的,这里传入参数的就是什么样子的,不再受 ROLE_ 前缀的制约。

2.1 章节的写法等同如下的写法:
httpSecurity.authorizeRequests().antMatchers("/foo/test").hasAuthority("ROLE_ADMIN")
httpSecurity.authorizeRequests().antMatchers("/foo/test").hasAnyAuthority("ROLE_APP","ROLE_ADMIN")




匿名访问



匿名身份验证的用户和未经身份验证的用户之间没有真正的概念差异。 Spring Security 的匿名身份验证只是为您提供了一种更方便的方式来配置访问控制属性。 所有的匿名用户都持有角色 ROLE_ANONYMOUS 。 所以你可以使用 2.1 章节的方法来配置匿名访问:
httpSecurity.authorizeRequests().antMatchers("/foo/test").hasAuthority("ROLE_ANONYMOUS")

你也可以通过以下方式进行配置:
httpSecurity.authorizeRequests().antMatchers("/foo/test").anonymous()




开放请求



开放请求可以这么配置:
httpSecurity.authorizeRequests().antMatchers("/foo/test").permitAll()









请到「今天看啥」查看全文