SpringSecurityOAuth2配置认证服务器策略
一、刷新令牌
如果客服端的令牌过期,可以使用刷新令牌更新令牌。这就就可以避免再次通过用户名和密码登录,重新获取令牌这些麻烦的操作了。刷新令牌只能是授权码和密码模式下有效,在认证服务器AuthorizationServerConfig
的authorizedGrantTypes
中配置刷新令牌的参数refresh_token
。
更新令牌的HTTP请求参数如下:
grant_type(必须):表示使用的授权模式,此处的值固定为
refresh_token
refresh_token(必须):表示早前收到的更新令牌
scope:表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致。
1.1、创建UserDetailsService实现类
创建CustomUserDetailsService
类实现UserDetailsService
接口,并覆写 loadUserByUsername(String username)
这个方法。
修改安全配置类SpringSecurityConfig
中用户名和密码的设置方式。
在认证服务器AuthorizationServerConfig
中更改认证服务器端点配置 configure(AuthorizationServerEndpointsConfigurer endpoints)
。
1.2、测试刷新令牌
重启程序,访问令牌端点都是 /auth/oauth/token
,先获取令牌之后,就可以拿到刷新令牌,在根据刷新令牌去获取令牌。
二、令牌管理策略
默认情况下,令牌是通过 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
工程的。
创建TokenConfig
类指定Redis
存储Token
添加 redis
依赖后, 容器自动就会有 RedisConnectionFactory
实例。
将令牌管理策略添加到端点上
将上面令牌管理策略作用到认证服务器AuthorizationServerConfig
端点上configure(AuthorizationServerEndpointsConfigurer endpoints)
。
配置redis相关信息
这里默认是要有一个redis服务器器咯,我采用的是docker的形式。下载安装包编译的形式可以参考:第三章redis简述及安装。注意需要开放6379端口
测试
当前版本号: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
配置JDBC
管理令牌
测试了哈
重启程序,在postman
中获取token
,获取到token
之后,查看数据库中oauth_access_token
表是否保存token
相关信息。
注意:在postman上看到的令牌和刷新令牌与数据库中保存的是不一样的,但是没有错,这个不用去关心。
当前版本号:
三、将授权码保存到数据库中
可以关注 JdbcAuthorizationCodeServices
类。
授权码主要操作oauth_code
表的,只有当 grant_type
为authorization_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);
}
将授权码管理策略添加到令牌端点中。
3.2、测试
当前版本号: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之后,只有这个这个资源可以访问,其他的资源就不能访问,但是这里可以设置多个。
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
注解的扫描范围内才可以测试。
启动后,控制台就会打出加密后的结果了。
4.3、配置客服端信息由数据库管理
在认证服务器AuthorizationServerConfig
的 configure(ClientDetailsServiceConfigurer clients)
方法中,更改为使用JDBC方式管理客服端信息。将之前的内存方式 clients.inMemory()
更改为JDBC管理方式 clients.withClientDetails(ClientDetailsService clientDetailsService);
创建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
类已经封装好了,不能更改了。
4.4、测试
重启程序,注意客服端id和客服端密码不要写错,客服端密码是加密后存储的,写错了就获取不到token了。
当前版本号: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()
拒绝访问的权限,所以要设置他的权限。要将这两个端点认证或授权后可以访问,因为后面资源服务器,要通过此端点检验令牌是否有效。
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、测试环节