OAuth + Security - 5 - Token存儲升級(數據庫、Redis)

PS:此文章爲系列文章,建議從第一篇開始閱讀。java

在咱們以前的文章中,咱們當時獲取到Token令牌時,此時的令牌時存儲在內存中的,這樣顯然不利於咱們程序的擴展,因此爲了解決這個問題,官方給咱們還提供了其它的方式來存儲令牌,存儲到數據庫或者Redis中,下面咱們就來看一看怎麼實現。git

不使用Jwt令牌的實現

  • 存儲到數據庫中(JdbcTokenStore)

使用數據庫存儲方式以前,咱們須要先準備好對應的表。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

image

咱們查看數據中,看是否已經存入相關信息到數據庫中:

image

  • 存儲到redis(RedisTokenStore)

令牌存儲到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測試:

image

咱們查看Redis中,看是否已經存入相關信息到數據庫中:

image

  • 其它

咱們打開TokenStore接口的實現類,會發現,還有一種JwkTokenStore,這種實際上就是將咱們UUID格式令牌變成能夠帶特殊含義的jwt格式的令牌,咱們已經在第三篇文章中介紹過了,能夠參考前面的文章。

可是咱們須要明白一點的是,這種令牌仍是存儲在內存中的,後期咱們如何將其存儲到redis中是咱們研究的方向。

image

在上面的token存儲到數據庫和存儲到redis中,咱們會發現一個問題,那就是咱們屢次獲取令牌,可是其值是固定不變的,爲了解決這個問題,咱們可使用以下方式解決:

@Bean
public TokenStore tokenStore() {
    RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
    // 解決每次生成的 token都同樣的問題
    redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString());
    return redisTokenStore;
}

使用JWT令牌的實現

在以前的全部使用方法中,咱們要麼是將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

image

image

而後使用該令牌請求資源

image

至此,咱們差很少完成了Token令牌的存儲和獲取

源碼傳送門: https://gitee.com/dimples820/dimples-explore

相關文章
相關標籤/搜索