Spring Security TokenStore实现3+1详解
TokenStore:Persistence interface for OAuth2 tokens.(对于OAuth2令牌持久化接口)
官方文档
TokenStore 的默认实现有三种:
- InMemoryTokenStore
- JdbcTokenStore
- JwtTokenStore
此外,将会根据TokenStor的特性多自定义一种实现——RedisTokenStore
一、InMemoryTokenStore
1.1.概要
这个是OAuth2默认采用的实现方式。在单服务上可以体现出很好特效(即并发量不大,并且它在失败的时候不会进行备份),大多项目都可以采用此方法。根据名字就知道了,是存储在内存中,毕竟存在内存,而不是磁盘中,调试简易。
1.2.实现
既然InMemoryTokenStore是OAuth2默认实现,那么就不需要我们再去配置,直接调用即可。
1.3.代码调用
@Autowired(required = false) private TokenStore inMemoryTokenStore; /** * 端点(处理入口) */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(inMemoryTokenStore); .... }
1.4.测试调用访问获取Token
此处基于SpringBoot+Security的小demo,相关配置就不在本文太多出现,着重讲解TokenStore
- spring security 默认授权认证端点:oauth/token
- 此处使用:grant_type—>password模式
二、JdbcTokenStore
2.1.概要
这个是基于JDBC的实现,令牌(Access Token)会保存到数据库。这个方式,可以在多个服务之间实现令牌共享。
2.2.实现
1).既然是JDBC,那么肯定得需要一个数据源。此处使用的是SpringBoot,因此配置了一个数据源。所需jar依赖就不多说了。
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/security?useUnicode=yes&characterEncoding=UTF-8 username: catalpaFlat password: catalpaFlat
2).除了数据源,那么jdbc肯定得有库表,因此OAuth2默认给出了表结构
Drop table if exists oauth_access_token; create table oauth_access_token ( create_time timestamp default now(), token_id VARCHAR(255), token BLOB, authentication_id VARCHAR(255), user_name VARCHAR(255), client_id VARCHAR(255), authentication BLOB, refresh_token VARCHAR(255) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; Drop table if exists oauth_refresh_token; create table oauth_refresh_token ( create_time timestamp default now(), token_id VARCHAR(255), token BLOB, authentication BLOB ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
而且JdbcTokenStore源码中也有很多关于表的操作:
3).配置JdbcTokenStore
@Autowired private DataSource dataSource; /** * jdbc token 配置 */ @Bean public TokenStore jdbcTokenStore() { Assert.state(dataSource != null, "DataSource must be provided"); return new JdbcTokenStore(dataSource); }
2.3.代码调用
@Autowired(required = false) private TokenStore jdbcTokenStore; /** * 端点(处理入口) */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jdbcTokenStore); .... }
2.4.测试调用访问获取Token
三、JwtTokenStore
3.1.概要
jwt全称 JSON Web Token。这个实现方式不用管如何进行存储(内存或磁盘),因为它可以把相关信息数据编码存放在令牌里。JwtTokenStore 不会保存任何数据,但是它在转换令牌值以及授权信息方面与 DefaultTokenServices 所扮演的角色是一样的。
3.2.实现
既然jwt是将信息存放在令牌中,那么就得考虑其安全性,因此,OAuth2提供了JwtAccessTokenConverter实现,添加jwtSigningKey,以此生成秘钥,以此进行签名,只有jwtSigningKey才能获取信息。
/** * jwt Token 配置, matchIfMissing = true * * @author : CatalpaFlat */ @Configuration public class JwtTokenConfig { private final Logger logger = LoggerFactory.getLogger(JwtTokenConfig.class); @Value("${default.jwt.signing.key}") private String defaultJwtSigningKey; @Autowired private CustomYmlConfig customYmlConfig; public JwtTokenConfig() {logger.info("Loading JwtTokenConfig ...");} @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); String jwtSigningKey = customYmlConfig.getSecurity().getOauth2s().getOuter().getJwtSigningKey(); Assert.state(StringUtils.isBlank(jwtSigningKey), "jwtSigningKey is not configured"); //秘签 jwtAccessTokenConverter.setSigningKey(StringUtils.isBlank(jwtSigningKey) ? defaultJwtSigningKey : jwtSigningKey); return jwtAccessTokenConverter; } }
3.3.代码调用
@Autowired(required = false) private TokenStore jwtTokenStore; @Autowired(required = false) private JwtAccessTokenConverter jwtAccessTokenConverter; /** * 端点(处理入口) */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore) .accessTokenConverter(jwtAccessTokenConverter); .... }
3.4.测试调用访问获取Token
四、RedisTokenStore
4.1.概要
由于TokenStore作用就是对于OAuth2令牌持久化接口,而我们在实际开发中,对于内存的使用是慎之又慎,而对于存储到数据库也是根据项目需求进行调配。因此就想,可不可以用redis来进行存储持久化我们的OAuth2令牌。偷偷瞄了一眼OAuth2还有那些实现了TokenStore的,找到了一个RedisTokenStore。
4.2.实现
记得配置redis
@Autowired private RedisConnectionFactory redisConnectionFactory; /** * redis token 配置 */ @Bean public TokenStore redisTokenStore() { return new RedisTokenStore(redisConnectionFactory); }
4.3.代码调用
@Autowired(required = false) private TokenStore redisTokenStore; /** * 端点(处理入口) */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(redisTokenStore); .... }