呓语 | 杨英明的个人博客

专注于c++、Python,欢迎交流

By

Spring Security 调研

前言

Spring Security 是 Spring 提供的用于用户权限认证授权的框架。

系统设计要求

要实现用户权限认证授权,主流的设计要求系统中有四个部分:用户、角色、权限、资源,即:

  • 用户:要求访问资源的对象,比如张三;
  • 角色:具有某些权限的对象,比如普通用户、管理员、订单管理员等;
  • 权限:或者说是授权,每条授权记录某个角色可以访问某个资源;
  • 资源:代表系统中某个资源,通常用 url 代表,比如 https://xxx.com/api/v1/order;

它们的关系如下:屏幕快照 2019-12-20 下午2.45.05.png

如图所示,要实现用户权限认证和授权通常要求数据库中要有五张表,分别是:用户表、用户角色表、角色表、资源表、角色资源表(角色授权表)。Spring Security 框架也以以上设计为基础。

RBAC

业界通常基于 RBAC 实现授权。RBAC 可以理解为两种,或者分为两种:

  • 基于角色的访问控制(Role-Based Access Control):根据角色进行权限判断,比如用户要查询员工的工资,就判断用户是否具有总经理的角色,如果有就允许查询;

流程如下:

屏幕快照 2019-12-20 下午5.03.04.png

优点:查询快,查一遍用户角色表即可;缺点:需要什么权限就加具有这个权限的角色,代码写起来会比较繁琐,不够灵活;

  • 基于资源的访问控制(Resource-Based Access Control):根据资源进行权限判断,比如用户要查询员工的工资,就判断用户是否具有访问工资这个资源的权限,比如有,就允许查询;

流程如下:

屏幕快照 2019-12-20 下午5.09.22.png

优点:直接根据资源进行权限判断,粒度小,足够灵活;缺点:查询慢,需要查两遍表; 总的来说,以上两种方式,基于资源的访问控制,粒度小,足够灵活,推荐采用。一般来说,角色授权表和用户角色表不会很大,查询表的那些时间消耗可以接受。

Spring Security 快速开始

首先为 maven 项目添加 Spring Security 的依赖:pom.xml

<dependencies>
    ...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    ...
</dependencies>

使用 Spring Security 实现用户角色定义,即规定用户具有哪些角色

UserBuilder users = User.withDefaultPasswordEncoder();  // 密码编码器
User user = users
  .username("user")
  .password("password")
  .roles("USER")            // 当前用户具有 USER 角色
  .build();
User admin = users
  .username("admin")
  .password("password")
  .roles("USER","ADMIN")    // 当前用户具有 USER 和 ADMIN 两种角色
  .build();

使用 Spring Security 实现权限拦截,即规定资源需要哪些角色才能访问

protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests(authorizeRequests ->                                        1
            authorizeRequests
                .antMatchers("/resources/**", "/signup", "/about").permitAll() //这些 url 不需要        2
                .antMatchers("/admin/**").hasRole("ADMIN")                             3
                .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")   4
                .anyRequest().authenticated()                                          5
        )
        .formLogin(withDefaults());
}

通过以上几步,可以搭建一个简单的用户权限认证和授权系统,除此之外,Spring Security 还具有一些高级功能:

  • Password Encoding:密码编码器,上例中使用的是简单的编码器(不编码,直接比较),你还可以选择其他方式对密码编码,比如 md5、BCrypt 等,可供选择编码器有:BCryptPasswordEncoder、Argon2PasswordEncoder、Pbkdf2PasswordEncoder 等;
  • 从数据库中对用户授权角色:上例中对用户授权角色写在了代码中,但 Spring Security 也支持从数据库中对用户授权角色;
  • 从数据库中对角色授权:同上;

总的来说,Spring Security 实现用户权限认证和授权是比较成熟的,提供的功能比较丰富,初期接入和后期扩展都能应付的来。

Spring Security OAuth2

OAuth2 是第二代的开放认证协议(Open Authorization 2.0),Spring Security OAuth2 是对 OAuth2 协议的一种实现。一般来说,OAuth2 提供两种服务,即授权服务(Authorization Server,也叫做认证服务)和资源服务(Resource Server),授权服务接受一组用户名和密钥,返回一个授权码(token),客户端可以通过授权码(token)访问资源服务。

OAuth2 认证逻辑如下:

屏幕快照 2019-12-20 下午3.59.11.png举个例子说得更清楚些:oauth 例子.png

总的来说,如果我们的系统打算提供给第三方登录认证的功能,那么可以通过 Spring Security OAuth2 实现。

Spring Security CAS

CAS 是 Central Authentication Service 的缩写,中央认证服务,一种独立开放指令协议。CAS 适用于分布式和微服务情况,设想这种场景,当系统服务多起来之后,比如订单系统、问题系统等,是否每个服务都要设计一个认证和授权模块进行用户的访问控制,那么不如将认证授权模块提取出来,做一个统一的认证授权中心(CAS)。用户要访问某种资源需要先到 CAS 中统一认证,通过后获取一个授权码,用户拿着授权码通过网关访问对应服务中的资源。

具体实现可参考如下架构:

屏幕快照 2019-12-20 下午4.10.18.png通过 Spring Security 也可以实现 CAS,参考文档:https://docs.spring.io/spring-security/site/docs/5.2.2.BUILD-SNAPSHOT/reference/htmlsingle/#cas

总的来说,当体量大的时候,可以考虑将认证服务拆出来,站内认证和第三方认证都通过统一认证服务(CAS)进行认证。

参考资料

原创声明

转载请注明:呓语 » Spring Security 调研