13518219792

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

SpringSecurity原理剖析与权限系统设计

 Spring Secutity和Apache Shiro是Java领域的两大主流开源安全框架,也是权限系统设计的主要技术选型。本文主要介绍Spring Secutity的实现原理,并基于Spring Secutity设计基于RBAC的权限系统。

创新互联技术团队十载来致力于为客户提供成都网站建设、成都做网站、品牌网站设计成都全网营销推广、搜索引擎SEO优化等服务。经过多年发展,公司拥有经验丰富的技术团队,先后服务、推广了上千多家网站,包括各类中小企业、企事单位、高校等机构单位。

一、技术选型

  1. 为何把Spring Secutity作为权限系统的技术选型,主要考虑了以下几个方面:
  2. 数据鉴权的能力:Spring Secutity支持数据鉴权,即细粒度权限控制。
  3. Spring生态基础:Spring Secutity可以和Spring生态无缝集成。

多样认证能力:Spring Secutity支持多样认证方式,如预认证方式可以与第三方认证系统集成。

二、核心架构

权限系统一般包含两大核心模块:认证(Authentication)和鉴权(Authorization)。

官方给出的Spring Security的核心架构图如下:

核心架构解读:

三、设计原理

通过对源码的分析,我把Spring Security的核心领域模型设计整理如下:

全局抽象模型解读:

四、应用集成

理清Spring Security的定制点后,就可以在系统内部集成Spring Security了。

这里使用预认证的方式,以适配第三方认证系统。AbstractPreAuthenticatedProcessingFilter提供了预认证的扩展点,基于该抽象类实现一个自定义认证过滤器。

 
 
 
 
  1. public class MyPreAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
  2.  @Override
  3.  protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
  4.  // 从第三方系统获取用户ID
  5.  return userId;
  6.  }
  7.  @Override
  8.  protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
  9.  return "";
  10.  }
  11. }

Spring Security会根据预认证过滤器getPreAuthenticatedPrincipal返回的用户ID信息,加载用户角色等初始信息。这里需要实现UserDetailsManager接口,提供用户信息管理器。

 
 
 
 
  1. @Service
  2. public class MyUserManager implements UserDetailsManager {
  3.  @Override
  4.  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  5.  // 从数据库加载用户信息
  6.  return user;
  7.  }
  8.  
  9.  // 其他管理接口
  10. }

UserDetails内包含了GrantedAuthority接口类型的权限信息抽象,一般可以基于它自定义角色和权限。Spring Security使用一种接口形式表达角色和权限,角色和权限的差别是角色的ID是以"ROLE_"为前缀。

 
 
 
 
  1. public class MyRole implements GrantedAuthority {
  2.  private final String role;
  3.  @Override
  4.  public String getAuthority() {
  5.  return "ROLE_" + role;
  6.  }
  7. }
  8. public class MyAuthority implements GrantedAuthority {
  9.  private final String authority;
  10.  @Override
  11.  public String getAuthority() {
  12.  return authority;
  13.  }
  14. }

接下来注册自定义认证过滤器和用户管理器,这里需要实现WebSecurityConfigurerAdapter进行Web安全配置。

 
 
 
 
  1. @EnableWebSecurity
  2. @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.PROXY)
  3. public class MySecurityConfig extends WebSecurityConfigurerAdapter {
  4.  @Autowired
  5.  UserDetailsManager userDetailsManager;
  6.  @Bean
  7.  protected AuthenticationProvider createPreAuthProvider() {
  8.  // 注册用户管理器
  9.  PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
  10.  provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsManager));
  11.  return provider;
  12.  }
  13.  @Override
  14.  protected void configure(HttpSecurity http) throws Exception {
  15.  // 注册预认证过滤器
  16.  http.addFilter(new MyPreAuthFilter(authenticationManager()));
  17.  }
  18. }

这样,最简单的Spring Security框架集成内系统内部已经完成了。在系统的任意服务接口上可以使用如下方式进行鉴权。

 
 
 
 
  1. public interface MyService {
  2.  @PreAuthorize("hasAuthority('QUERY')")
  3.  Object getById(String id);
  4.  
  5.  @PreAuthorize("hasRole('ADMIN')")
  6.  void deleteById(String id);
  7. }

PreAuthorize注解表示调用前鉴权,Spring使用默认使用动态代理技术生成鉴权逻辑。注解内配置了SpringEL表达式来定制鉴权方式。上述代码中,hasAuthority会检查用户是否有QUERY权限,hasRole会检查用户是否有ADMIN角色。

使用动态代理的方式进行AOP,只允许在接口层面进行权限拦截,如果想在任意的方法上进行权限拦截,那么就需要借助于AspectJ的方式进行AOP。首先将注解EnableGlobalMethodSecurity的mode设置为AdviceMode.ASPECTJ,然后添加JVM启动参数,这样就可以在任意方法上使用Spring Security的注解了。

 
 
 
 
  1. javaagent:/path/to/org/aspectj/aspectjweaver/1.9.4/aspectjweaver-1.9.4.jar

以上还是只是以用户的身份信息(角色/权限)进行权限,灵活度有限,也发挥不了Spring Security的数据鉴权的能力。要使用数据鉴权,需要实现一个Spring Bean。

 
 
 
 
  1. @Component
  2. public class MyPermissionEvaluator implements PermissionEvaluator {
  3.  @Override
  4.  public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
  5.  // 自定义数据鉴权
  6.  return false;
  7.  }
  8.  @Override
  9.  public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
  10.  // 自定义数据鉴权
  11.  return false;
  12.  }
  13. }

PermissionEvaluator会被自动注册到Spring Security框架,并允许在注解内使用如下方式进行鉴权。

 
 
 
 
  1. @PreAuthorize("hasPermission(#id, 'QUERY')")
  2. Object func1(String id) {
  3. }
  4. @PreAuthorize("hasPermission(#id, 'TABLE', 'QUERY')")
  5. Object func2(String id) {
  6. }

其中,func1的注解表示校验用户是否对id有QUERY权限,代码逻辑路由到MyPermissionEvaluator的第一个接口。func2的注解表示校验用户是否对TABLE类型的id有QUERY权限,代码逻辑路由到MyPermissionEvaluator的第二个接口。PermissionEvaluator提供了权限系统中数据鉴权的扩展点,稍后会描述如何利用该扩展点定制基于RBAC的权限系统。

五、权限系统

构建基于RBAC(Role Based Access Control)的权限系统,需要明确用户、角色、权限、资源这几个核心的概念类的含义和它们之间的关系。

资源:权限系统内需要安全控制的客体,一般是系统内的数据或功能。

权限:描述了资源上的操作抽象,一般是一种动作。

授权:是权限和资源的组合,表示对资源的某一个操作。

角色:描述了一组授权的集合,表示一类特殊概念的功能集。

用户:权限系统的主体,一般是当前系统的访问用户,用户可以拥有多种角色。

以下是我们设计的基于RABC的权限核心领域模型:

一般情况下,系统内需要权限管控的资源是无法用户自定义的,因为资源会耦合大量的业务逻辑,所以我们提供了自 资源工厂,通过配置化的方式构建业务模块所需的资源。而用户、角色、权限,以及授权记录都是可以通过相应的管理器进行查询更新。

另外,资源抽象允许表达资源的继承和组合关系,继而表达更复杂的资源模型,资源统一鉴权的流程为:

六、总结回顾

本文从Spring Security的架构和原理出发,描述了开源安全框架对于认证和鉴权模块的设计思路和细节。并提供了系统内集成Spring Security的方法,结合RBAC通用权限系统模型,讨论了统一资源构建和统一鉴权的设计和实现。如果你也需要设计一个新的权限系统,希望本文对你有所帮助。


标题名称:SpringSecurity原理剖析与权限系统设计
标题链接:http://cdbrznjsb.com/article/cdoggpp.html

其他资讯

让你的专属顾问为你服务