Spring Security-28-Token配置

前面的文章中,实现了我们三种自定义登录方式的 token 返回,做的也全是一些资源服务器的配置,本文再来看一些关于认证服务器的配置, 比如 Token 的过期时间,存储方式等等

认证服务器配置

在之前的配置类 MyAuthorizationServerConfig 做修改,首先继承 AuthorizationServerConfigurerAdapter

client自定义配置

之前我们配置 client_idclient_serect 都是在应用的 application.yml 中去配置的,这种方式是基于内存的配置. 而且token的过期时间等等配置都是用的默认的配置, 在实际情况中, 每个应用的 client_idclient_serect 都应该放到数据库中去存储,就比如我们之前去QQ互联平台去创建应用一样, 下面看一下如何使用数据库存储client信息
首先需要修改我们认证服务器的配置类 MyAuthorizationServerConfig ,然后继承 AuthorizationServerConfigurerAdapter, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

@Autowired
private AuthenticationManager authenticationManager;

@Autowired
private UserDetailsService userDetailsService;

@Autowired
private DataSource dataSource;

@Autowired
private TokenStore tokenStore;

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.userDetailsService(userDetailsService);
endpoints.tokenStore(tokenStore);
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 这个地方指的是从jdbc查出数据来存储
clients.withClientDetails(clientDetails());
}

private ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}

}

这里重写了两个方法,上面的这个是 endpoints 的一些配置, 只要重写了这个方法,就必须我们自己去指定 authenticationManageruserDetailsService

然后下面这个就是对 client 的一些配置,只要重写了这个方法,之前我们在 application.yml 中的配置就失效了

client的信息默认是在内存中的,这种方式对于单个服务器是完全正常的(即,在发生故障的情况下,低流量和热备份备份服务器).大多数项目可以从这里开始,也可以在开发模式下运行,以便轻松启动没有依赖关系的服务器.

我们这里只介绍用数据库存储的方式, 就是设置一下数据源就可以了

然后这里在数据库里面需要用到一张表,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE TABLE `oauth_client_details` (
`client_id` varchar(48) NOT NULL,
`resource_ids` varchar(256) DEFAULT NULL,
`client_secret` varchar(256) DEFAULT NULL,
`scope` varchar(256) DEFAULT NULL,
`authorized_grant_types` varchar(256) DEFAULT NULL,
`web_server_redirect_uri` varchar(256) DEFAULT NULL,
`authorities` varchar(256) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(4096) DEFAULT NULL,
`autoapprove` varchar(256) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

添一条测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
INSERT INTO oauth_client_details (
client_id,
resource_ids,
client_secret,
scope,
authorized_grant_types,
web_server_redirect_uri,
authorities,
access_token_validity,
refresh_token_validity,
additional_information,
autoapprove
)
VALUES
(
'testAppid',
NULL,
'testSecret',
'read,write,all',
'authorization_code,refresh_token,password',
'http://www.pinzhi365.com',
'ROLE_USER',
86400,
86400,
NULL,
FALSE
);

具体的每列的作用,说明在这里

配置 Token 的存储方式

在之前的配置中,Token都是存放在内存中的,只要重启应用,token就都失效了,通常情况下都是要放到redis里面的,下面看一下如何使用 redis 存储 Token

新建一个 TokenStoreConfig 类,如下:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class TokenStoreConfig {

@Autowired
private RedisConnectionFactory connectionFactory;

@Bean
public TokenStore redisTokenStore(){
return new RedisTokenStore(connectionFactory);
}
}

然后在上面的配置类中的第一个 configure 方法中注入进去就ok了

踩坑

这里遇到一个问题,我们项目目前的依赖 spring-boot-starter-data-redis 的默认实现是 Jedis, RedisTokenStore 中会使用 Pipeline 的功能,Jedis对于单节点可以支持 Pipeline,但是集群则没有支持 Pipeline, 因为我这里配置的是集群,所以这里就不能正常运行,错误如下:

1
UnsupportedOperationException, Pipeline is currently not supported for JedisClusterConnection.

要解决这个问题,要么自己实现Jedis对Pipeline的支持,这比较复杂.另一种就是使用lettuce代替Jedis,luttuce对于集群使用Pipeline有很好的支持.

我们只需要引入luttuce的依赖:

1
2
3
4
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
</dependency>

然后在 redis 的配置类中,加入:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Primary
@Bean
public LettuceConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {
RedisProperties.Cluster cluster = redisProperties.getCluster();
if (cluster != null) {
RedisClusterConfiguration configuration = new RedisClusterConfiguration(cluster.getNodes());
return new LettuceConnectionFactory(configuration);
} else {
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort());
connectionFactory.setPassword(redisProperties.getPassword());
return connectionFactory;
}
}

这样就ok了

测试

如下:
image

接口可以正常访问,就说明数据库配置的client信息已经生效了, 然后再看一下redis中
image

上面生成的token也正常的放到了redis中

本文代码传送门 (忘记创建分支了,跟前面一章写在了同一个分支上)