PS:此文章爲系列文章,建議從第一篇開始閱讀。java
在咱們以前的文章中,咱們當時獲取到Token令牌時,此時的令牌時存儲在內存中的,這樣顯然不利於咱們程序的擴展,因此爲了解決這個問題,官方給咱們還提供了其它的方式來存儲令牌,存儲到數據庫或者Redis中,下面咱們就來看一看怎麼實現。git
使用數據庫存儲方式以前,咱們須要先準備好對應的表。Spring Security OAuth倉庫能夠找到相應的腳本:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql。該腳本爲HSQL,因此須要根據具體使用的數據庫進行相應的修改,以MySQL爲例,而且當前項目中,只須要使用到oauth_access_token和oauth_refresh_token數據表,因此將建立這兩個庫表的語句改成MySQL語句:github
CREATE TABLE oauth_access_token ( token_id VARCHAR ( 256 ), token BLOB, authentication_id VARCHAR ( 256 ), user_name VARCHAR ( 256 ), client_id VARCHAR ( 256 ), authentication BLOB, refresh_token VARCHAR ( 256 ) ); CREATE TABLE oauth_refresh_token ( token_id VARCHAR ( 256 ), token BLOB, authentication BLOB );
而後咱們須要去配置對應的認證服務器,主要就是修改以前文章中TokenConfigure類中的tokenStore()方法:redis
// 同時須要注入數據源 @Autowired private DataSource dataSource; @Bean public TokenStore tokenStore() { return new JdbcTokenStore(dataSource); }
同時須要添加jdbc的依賴:spring
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
認證服務器中的配置保持本系列第一章的配置不變,此處再也不贅述。sql
其中如有不理解的地方,請參考該系列的第一篇文章數據庫
關於數據源的補充:apache
在咱們項目中配置的數據源,可能不必定是使用的官方提供的格式,好比咱們自定義的格式,或者使用第三方的數據源,那麼咱們如何去配置呢?這裏以mybatis-plus的多數據源爲例:服務器
@Configuration @EnableAuthorizationServer public class FebsAuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter { ...... @Autowired private DynamicRoutingDataSource dynamicRoutingDataSource; @Bean public TokenStore tokenStore() { DataSource dimplesCloudBase = dynamicRoutingDataSource.getDataSource("dimples_cloud_base"); return new JdbcTokenStore(febsCloudBase); } ...... }
而後啓動項目,從新獲取Token進行測試,能正確的獲取到Token:mybatis
咱們查看數據中,看是否已經存入相關信息到數據庫中:
令牌存儲到redis中相比於存儲到數據庫中來講,存儲redis中,首先在性能上有必定的提高,其次,令牌都有有效時間,超過這個時間,令牌將不可再用,而redis的能夠給對應的key設置過時時間,完美切合需求,全部令牌存儲到redis中也是一種值得使用的方法。
首先,咱們須要在項目中添加redis依賴,同時配置redis
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
新建配置類RedisConfigure
@Configuration public class RedisConfigure{ @Bean @ConditionalOnClass(RedisOperations.class) public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(mapper); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key採用 String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的 key也採用 String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式採用 jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的 value序列化方式採用 jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
配置redis的鏈接
spring: redis: database: 0 host: 127.0.0.1 port: 6379 lettuce: pool: min-idle: 8 max-idle: 500 max-active: 2000 max-wait: 10000 timeout: 5000
這時咱們啓動項目測試,會發現項目將會報錯:
Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig
這是因爲咱們配置RedisConfigure時,使用到了一個ObjectMapper類,這個類須要咱們引入Apache的一個工具包
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
最後,咱們須要在認證服務器中配置token的存儲方式,仍是同jdbc的配置,在tokenStore()方法中配置,打開咱們的TokenConfigure類,而後作以下配置:
@Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public TokenStore tokenStore() { return new RedisTokenStore(redisConnectionFactory); }
認證服務器中的配置仍是跟以前的同樣,保持不變,啓動項目,獲取Token測試:
咱們查看Redis中,看是否已經存入相關信息到數據庫中:
咱們打開TokenStore接口的實現類,會發現,還有一種JwkTokenStore,這種實際上就是將咱們UUID格式令牌變成能夠帶特殊含義的jwt格式的令牌,咱們已經在第三篇文章中介紹過了,能夠參考前面的文章。
可是咱們須要明白一點的是,這種令牌仍是存儲在內存中的,後期咱們如何將其存儲到redis中是咱們研究的方向。
在上面的token存儲到數據庫和存儲到redis中,咱們會發現一個問題,那就是咱們屢次獲取令牌,可是其值是固定不變的,爲了解決這個問題,咱們可使用以下方式解決:
@Bean public TokenStore tokenStore() { RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); // 解決每次生成的 token都同樣的問題 redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString()); return redisTokenStore; }
在以前的全部使用方法中,咱們要麼是將Token存儲在數據庫或者redis中的時候,令牌的格式是UUID的無心義字符串,或者是使用JWT的有意義字符串,可是確是將令牌存儲在內存中,那麼,咱們如今想使用JWT令牌的同時,也想將令牌存儲到數據庫或者Redis中。咱們該怎麼配置呢?下面咱們以Redis存儲爲例,進行探索:
首先,咱們仍是要使用到TokenConfigure中的JWT和Redis配置:
@Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); //對稱祕鑰,資源服務器使用該祕鑰來驗證 converter.setSigningKey(SIGNING_KEY); return converter; } @Bean public TokenStore tokenStore() { RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); // 解決每次生成的 token都同樣的問題 redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString()); return redisTokenStore; }
接下來,是配置認證服務器,在認證服務器中,跟以前的配置有一點區別,以下:
@Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private TokenStore tokenStore; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints // 配置密碼模式管理器 .authenticationManager(authenticationManager) // 配置受權碼模式管理器 .authorizationCodeServices(authorizationCodeServices()) // 令牌管理 // .tokenServices(tokenServices()); .accessTokenConverter(jwtAccessTokenConverter) .tokenStore(tokenStore); }
啓動項目,進行測試,獲取token
而後使用該令牌請求資源
至此,咱們差很少完成了Token令牌的存儲和獲取