Spring Security TokenStore實現3+1詳解

CatalpaFlat發表於2017-12-29

TokenStore:Persistence interface for OAuth2 tokens.(對於OAuth2令牌持久化介面)
官方文件
TokenStore 的預設實現有三種:

  • InMemoryTokenStore
  • JdbcTokenStore
  • JwtTokenStore

spring 關於tokenstore的文件

此外,將會根據TokenStor的特性多自定義一種實現——RedisTokenStore

一、InMemoryTokenStore

1.1.概要

這個是OAuth2預設採用的實現方式。在單服務上可以體現出很好特效(即併發量不大,並且它在失敗的時候不會進行備份),大多專案都可以採用此方法。根據名字就知道了,是儲存在記憶體中,畢竟存在記憶體,而不是磁碟中,除錯簡易。

1.2.實現

既然InMemoryTokenStore是OAuth2預設實現,那麼就不需要我們再去配置,直接呼叫即可。

InMemoryTokenStore 實現

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模式

InMemoryTokenStore 測試

二、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原始碼中也有很多關於表的操作:

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

JdbcTokenStore 測試
JdbcTokenStore 測試
JdbcTokenStore 測試
JdbcTokenStore 測試

三、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

JwtTokenStore 測試
JwtTokenStore 測試

四、RedisTokenStore

4.1.概要

由於TokenStore作用就是對於OAuth2令牌持久化介面,而我們在實際開發中,對於記憶體的使用是慎之又慎,而對於儲存到資料庫也是根據專案需求進行調配。因此就想,可不可以用redis來進行儲存持久化我們的OAuth2令牌。偷偷瞄了一眼OAuth2還有那些實現了TokenStore的,找到了一個RedisTokenStore。

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);
   ....
}
複製程式碼

4.4.測試呼叫訪問獲取Token

RedisTokenStore 測試
RedisTokenStore 測試

相關文章