SpringSecurityOAuth2配置认证服务器策略

一、刷新令牌

如果客服端的令牌过期,可以使用刷新令牌更新令牌。这就就可以避免再次通过用户名和密码登录,重新获取令牌这些麻烦的操作了。刷新令牌只能是授权码和密码模式下有效,在认证服务器AuthorizationServerConfigauthorizedGrantTypes中配置刷新令牌的参数refresh_token

springsecurity 如何解析token spring security refresh token_spring

更新令牌的HTTP请求参数如下:

grant_type(必须):表示使用的授权模式,此处的值固定为 refresh_token

refresh_token(必须):表示早前收到的更新令牌

scope:表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致。

1.1、创建UserDetailsService实现类

创建CustomUserDetailsService类实现UserDetailsService接口,并覆写 loadUserByUsername(String username)这个方法。

springsecurity 如何解析token spring security refresh token_数据库_02

修改安全配置类SpringSecurityConfig中用户名和密码的设置方式。

springsecurity 如何解析token spring security refresh token_spring_03

在认证服务器AuthorizationServerConfig中更改认证服务器端点配置 configure(AuthorizationServerEndpointsConfigurer endpoints)

springsecurity 如何解析token spring security refresh token_bc_04

1.2、测试刷新令牌

重启程序,访问令牌端点都是 /auth/oauth/token,先获取令牌之后,就可以拿到刷新令牌,在根据刷新令牌去获取令牌。

springsecurity 如何解析token spring security refresh token_spring_05

springsecurity 如何解析token spring security refresh token_数据库_06

二、令牌管理策略

默认情况下,令牌是通过 randomUUID 产生32位随机数的来进行填充的,而产生的令牌默认是存储在内存中。

  • 内存采用TokenStore 接口的默认实现类 InMemoryTokenStore , 开发时方便调试,适用单机版。
  • RedisTokenStore 将令牌存储到 Redis 非关系型数据库中,适用于并发高的服务。
  • JdbcTokenStore 基于 JDBC 将令牌存储到 关系型数据库中,可以在不同的服务器之间共享令牌。
  • JwtTokenStore (JSON Web Token)将用户信息直接编码到令牌中,这样后端可以不用存储它,前端拿到令牌可以直接解析出用户信息。

2.1、Redis管理令牌

引入redis依赖并床架配置类

sse-cloud-oauth2-base工程中添加Redis依赖,版本号就不要单行了,spring-boot-dependencies帮我们决绝了。

<!-- 加入redis的处理 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

这里的依赖我放到sse-cloud-oauth2-base工程下,而没有放在sse-cloud-oauth2-auth-server工程,因为引用了sse-cloud-oauth2-base工程的。

springsecurity 如何解析token spring security refresh token_bc_07

创建TokenConfig类指定Redis存储Token添加 redis 依赖后, 容器自动就会有 RedisConnectionFactory 实例。

springsecurity 如何解析token spring security refresh token_数据库_08

将令牌管理策略添加到端点上

将上面令牌管理策略作用到认证服务器AuthorizationServerConfig端点上configure(AuthorizationServerEndpointsConfigurer endpoints)

springsecurity 如何解析token spring security refresh token_bc_09

配置redis相关信息

这里默认是要有一个redis服务器器咯,我采用的是docker的形式。下载安装包编译的形式可以参考:第三章redis简述及安装。注意需要开放6379端口

springsecurity 如何解析token spring security refresh token_bc_10

测试

springsecurity 如何解析token spring security refresh token_数据库_11

springsecurity 如何解析token spring security refresh token_数据库_12

当前版本号:56de860491eefb1327fd32ef52d8c71852aa151c

2.2、JDBC管理令牌

当前使用了 MySQL 数据库,要修改下数据类型:

  • 官方提供的表结构主键类型为 VARCHAR(256) ,超过了MySQL限制的长度 128 ,需要修改为 VARCHAR(128)
  • LONGVARBINARY 类型修改为 BLOB 类型。

将这些表copy下来,根据上面的要求更改后,在数据库中执行。注意:表明和字段名不要随意更改,这是默认的。

引入依赖

<!--mybatis-plus启动器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--druid连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
</dependency>
<!--mysql驱动包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

数据源配置application.yml

springsecurity 如何解析token spring security refresh token_spring_13

配置JDBC管理令牌

springsecurity 如何解析token spring security refresh token_spring_14

测试了哈

重启程序,在postman中获取token,获取到token之后,查看数据库中oauth_access_token表是否保存token相关信息。

springsecurity 如何解析token spring security refresh token_bc_15

注意:在postman上看到的令牌和刷新令牌与数据库中保存的是不一样的,但是没有错,这个不用去关心。

当前版本号:

三、将授权码保存到数据库中

可以关注 JdbcAuthorizationCodeServices类。

授权码主要操作oauth_code表的,只有当 grant_typeauthorization_code时才会产生授权码,也就是说授权类型必须为authorization_code授权码模式才会产生授权码,配置将授权码保存到数据库了之后,oauth_code这样表才会保存授权码。其他授权模式oauth_code这样表示没有数据的,因为压根就没有授权码的产生。但是这个授权码使用过一次之后就会自动是删除,所以将授权码放到数据库中是没有太大的必要。

3.1、配置授权码保存到数据库

创建JDBC管理授权码的实例,并注入容器中。jdbcAuthorizationCodeServices()方法上注意不要少了 @Bean注解注入容器。

/**
 * 获取数据源
 */
@Autowired
private DataSource dataSource;

/**
 * 授权码管理策略
 * @return
 */
@Bean
public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
    // 注入数据源
    return new JdbcAuthorizationCodeServices(dataSource);
}

将授权码管理策略添加到令牌端点中。

springsecurity 如何解析token spring security refresh token_bc_16

3.2、测试

springsecurity 如何解析token spring security refresh token_数据库_17

当前版本号:a03d0494e197cdf8df85d2944dc44ef4fcf0c798

四、将客服端信息保存到数据库中

4.1、数据表介绍

之前使用的客服端信息都是在代码中直接配置的,采用内存的形式配置。但是这样特别不方便,如果需要添加一个客服端呢?我们还得去更改代码。涉及到客服端表 oauth_client_details,不要问为什么?就是这样表,里面的字段也不要去动,因为 org.springframework.security.oauth2.provider.client.JdbcClientDetailsService类中全部封装好了。这张表在上面JDBC管理令牌说过了,直接根据那个地址去copy。

drop table if exists `oauth_client_details`;
create table `oauth_client_details` (
  `client_id` varchar(128) not null comment '客户端id',
  `resource_ids` varchar(128) default null comment '资源id(每个微服务的名称)',
  `client_secret` varchar(128) default null comment '客户端密码(要加密后存储)',
  `scope` varchar(128) default null comment '客户端授权范all,write,read)',
  `authorized_grant_types` varchar(128) default null comment '4种授权类型(多个授权类型,用英文逗号分隔',
  `web_server_redirect_uri` varchar(128) default null comment '获取授权码后的回调地址',
  `authorities` varchar(128) default null comment '授权标识',
  `access_token_validity` int(11) default null comment '令牌有效时长',
  `refresh_token_validity` int(11) default null comment '刷新令牌的有效时长',
  `additional_information` varchar(4096) default null comment '扩展字段',
  `autoapprove` varchar(128) default null comment '是否显示,true或者false',
  primary key (`client_id`)
) engine=innodb default charset=utf8mb4 comment='客户端(第三方应用)基本信息';
client_id资源id表示每个服务器的名称,配置这个资源id之后,只有这个这个资源可以访问,其他的资源就不能访问,但是这里可以设置多个。

springsecurity 如何解析token spring security refresh token_bc_18

scope:客服端授权范围,在可以访问的服务器中有添加一层限制,可以控制读或者写权限,可以配置多个。

  • authorized_grant_types:授权类型,其实是有5中授权类型,刷新令牌也属于。
  • authorization_code:授权码模式
  • password:密码模式
  • implicit:简化模式
  • client_credentials:客服端模式
  • refresh_token:刷新令牌模式

authorities授权标识,是API级别的,就是可以访问那些接口。控制层不是定义很多接口吗?就是那些接口的访问URL地址。但是授权码模式和密码模式这个字段是不生效的,因为授权码和密码模式会通过用户名来查询权限资源。这里字段主要是针对简化模式和客服端模式。

autoapprove是否自动授权, false 跳转到授权页面手动点击授权,true 不用手动授权。

4.2、springboot创建测试类

当测试的时候,功能不是很完善,需要在数据库中手动添加一些数据做测试,那么上面有一个客服端密码是需要加密的。下面就介绍怎么创建springboot程序的测试类,创建这个类应该注意什么。

test/java目录下创建 com.sse.oauth2.server.config.TestAuthApplication类测试密码加密。

package com.sse.oauth2.server.config;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author: GL
 * @program: springSecurity-example
 * @create: 2020年 06月 06日 18:26
 **/
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestAuthApplication {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Test
    public void testPwd() {
        // 指定数据获取加密后的结果
        System.out.println(passwordEncoder.encode("123456"));
    }
}

注意:创建的这个测试类类一定要在SpringBoot启动工程扫描范围内,也就是说要在@SpringBootApplication注解标识的这个类扫描范围内。创建的测试包要与正式代码的包名要一致,有在@SpringBootApplication注解的扫描范围内才可以测试。

springsecurity 如何解析token spring security refresh token_数据库_19

启动后,控制台就会打出加密后的结果了。

4.3、配置客服端信息由数据库管理

在认证服务器AuthorizationServerConfigconfigure(ClientDetailsServiceConfigurer clients)方法中,更改为使用JDBC方式管理客服端信息。将之前的内存方式 clients.inMemory()更改为JDBC管理方式 clients.withClientDetails(ClientDetailsService clientDetailsService);

springsecurity 如何解析token spring security refresh token_数据库_20

创建JdbcClientDetailsService实例,并注入spring容器中。

/**
 *  创建jdbcClientDetailsService实例,并注入spring容器中,不要少了@Bean
 *  注意:访问修饰符不要写错了。
 * @return
 */
@Bean
public ClientDetailsService jdbcClientDetailsService(){
    return new JdbcClientDetailsService(dataSource);
}

withClientDetails方法中把JdbcClientDetailsService实例注入。

// 使用JDBC方式管理客服端
clients.withClientDetails(jdbcClientDetailsService());

这样就完成了。


分析:withClientDetails(ClientDetailsService clientDetailsService)方法需要使用ClientDetailsService接口为参数,但是这个接口有两个实现类,InMemoryClientDetailsService类和JdbcClientDetailsService类,这里需要使用到JdbcClientDetailsService这个类。之前说表的字段和表明不要乱改的原因就是JdbcClientDetailsService类已经封装好了,不能更改了。

springsecurity 如何解析token spring security refresh token_spring_21

springsecurity 如何解析token spring security refresh token_spring_22

springsecurity 如何解析token spring security refresh token_数据库_23

4.4、测试

重启程序,注意客服端id和客服端密码不要写错,客服端密码是加密后存储的,写错了就获取不到token了。

springsecurity 如何解析token spring security refresh token_数据库_24

当前版本号:1721713ee5c8c67c7e86b7df4a1904a300331b0a

五、配置令牌端点安全策略

  • 令牌端点如下:

/oauth/authorize :申请授权码 code, 涉及的类 AuthorizationEndpoint

/oauth/token :获取令牌 token, 涉及的类 TokenEndpoint

/oauth/check_token :用于资源服务器请求端点来检查令牌是否有效, 涉及的类 CheckTokenEndpoint

/oauth/confifirm_access :用户确认授权提交, 涉及的类 WhitelabelApprovalEndpoint

/oauth/error :授权服务错误信息, 涉及的类 WhitelabelErrorEndpoint

/oauth/token_key :提供公有密匙的端点,使用 JWT 令牌时会使用 , 涉及的类 TokenKeyEndpoint

这些令牌端点并不是所有用户都可以访问的,默认情况下 /oauth/check_token/oauth/token_key 端点默认是 denyAll() 拒绝访问的权限,所以要设置他的权限。要将这两个端点认证或授权后可以访问,因为后面资源服务器,要通过此端点检验令牌是否有效。

springsecurity 如何解析token spring security refresh token_bc_25

5.1、配置端点权限

在认证服务器AuthorizationServerConfig中覆写 configure(AuthorizationServerSecurityConfigurer security)方法,配置/oauth/check_token 端点和/oauth/confifirm_access的权限。

/**
 * 令牌端点的安全配置
 * @param security
 * @throws Exception
 */
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    // 所有人可访问 /oauth/token_key 后面要获取公钥, 默认拒绝访问
    security.tokenKeyAccess("permitAll()");
    // 认证后可访问 /oauth/check_token , 默认拒绝访问
    security.checkTokenAccess("isAuthenticated()");
}

5.2、测试环节

springsecurity 如何解析token spring security refresh token_数据库_26

springsecurity 如何解析token spring security refresh token_spring_27

springsecurity 如何解析token spring security refresh token_数据库_28