8.Spring Security Oauth2 -- redis 和 JWT存储token

1.令牌的存储方式

令牌有多种存储方式,每种方式都是实现了 TokenStore 接口

  • 存储在本机内存: InMemoryTokenStore
  • 存储在数据库: JdbcTokenStore
  • JWT: JwtTokenStore,Json Web Token 不会存储在任何介质中,不过我还是不推荐这种做法啊,并发 2w 以后会有问题
  • 存储在 Redis: RedisTokenStore

2.使用 Redis 存储令牌

2.1 pom添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-pool2</artifactId>
</dependency>

2.2 application配置

spring:
  application:
    name: security-demo
  redis:
    host: 127.0.0.1
    port: 6379
    # password: 没有不用写
    lettuce:
      # 连接池配置
      pool:
        # 连接池中的最小空闲连接,默认 0
        min-idle: 0
        # 连接池中的最大空闲连接,默认 8
        max-idle: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制),默认 -1ms
        max-wait: -1ms
        # 连接池最大连接数(使用负值表示没有限制),默认 8
        max-active: 8

2.3 Redis存储令牌配置

@Configuration
public class RedisTokenStoreConfig {

    // 注入 RedisConnectionFactory,用于连接 Redis
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

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

2.4 指定存储策略为Redis

  • 在认证服务器配置中指定令牌的存储策略为Redis
  • AuthorizationServerConfig.class
// 注入redisTokenStore
    @Resource(name = "redisTokenStore")
    private TokenStore tokenStore;

    // 使用密码模式需要配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
            // 设置为redis 配置令牌存储策略
            .tokenStore(tokenStore);
    }
    ... 省略

2.5 运行项目

  • 127.0.0.1:8666/oauth/token
    8.Spring Security Oauth2 -- redis 和 JWT存储token_第1张图片
    8.Spring Security Oauth2 -- redis 和 JWT存储token_第2张图片
  • 进行获取令牌操作,可以发现令牌已经被存储到Redis中。
    8.Spring Security Oauth2 -- redis 和 JWT存储token_第3张图片

3. JWT存储Token

3.1 JWT 简介

JWT是JSON WEB TOKEN的缩写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的。

  • JWT token的格式:header.payload.signature;
  • header中用于存放签名的生成算法;
{
"alg": "HS256",
"typ": "JWT"
}
  • payload中用于存放数据,比如过期时间、用户名、用户所拥有的权限等;
{
"exp": 1572682831,
"user_name": "macro",
"authorities": [
  "admin"
],
"jti": "c1a0645a-28b5-4468-b4c7-9623131853af",
"client_id": "admin",
"scope": [
  "all"
]
}
  • signature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败。

3.2 JWT存储令牌的配置

@Configuration
public class JwtTokenStoreConfig {
    @Primary
    @Bean("jwtTokenStore")
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("test_key");//配置JWT使用的秘钥
        return accessTokenConverter;
    }
}

3.3 指定令牌存储策略JWT

  • AuthorizationServerConfig.class
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    // 注入redisTokenStore
   /* @Resource(name = "redisTokenStore")
    private TokenStore tokenStore;*/

    // 改为JWT
    @Resource(name = "jwtTokenStore")
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    // 使用密码模式需要配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
            // 设置为redis 配置令牌存储策略
            .tokenStore(tokenStore)
            // 将值转为jwt格式
            .accessTokenConverter(jwtAccessTokenConverter); 
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("admin")//配置client_id
            .secret(passwordEncoder.encode("admin123456"))//配置client_secret
            .accessTokenValiditySeconds(3600)//配置访问token的有效期
            .refreshTokenValiditySeconds(864000)//配置刷新token的有效期
            .redirectUris("http://www.baidu.com")//配置redirect_uri,用于授权成功后跳转
            .scopes("all") // 配置申请的权限范围,授权页面会显示
            .authorizedGrantTypes("authorization_code","password");//配置grant_type,表示授权类型 ,"refresh_token"
    }
}

3.4 运行

8.Spring Security Oauth2 -- redis 和 JWT存储token_第4张图片
发现获取到的令牌已经变成了JWT令牌,将access_token拿到https://jwt.io/ 网站上去解析下可以获得其中内容。
8.Spring Security Oauth2 -- redis 和 JWT存储token_第5张图片

4. 刷新令牌

在Spring Cloud Security 中使用oauth2时,如果令牌失效了,可以使用刷新令牌通过refresh_token的授权模式再次获取access_token。

  • 只需修改认证服务器的配置,添加refresh_token的授权模式即可。
  • 这里使用 redis的方式
  • AuthorizationServerConfig.class
@Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("admin")//配置client_id
            .secret(passwordEncoder.encode("admin123456"))//配置client_secret
            .accessTokenValiditySeconds(3600)//配置访问token的有效期
            .refreshTokenValiditySeconds(864000)//配置刷新token的有效期
            .redirectUris("http://www.baidu.com")//配置redirect_uri,用于授权成功后跳转
            .scopes("all") // 配置申请的权限范围,授权页面会显示
            .autoApprove(true) //自动授权配置
            .authorizedGrantTypes("authorization_code","password","refresh_token");//配置grant_type,表示授权类型
    }

8.Spring Security Oauth2 -- redis 和 JWT存储token_第6张图片
8.Spring Security Oauth2 -- redis 和 JWT存储token_第7张图片

  • redis中也多了一些key

5. 扩展JWT中存储的内容

有时候我们需要扩展JWT中存储的内容,这里我们在JWT中扩展一个key为enhance,value为enhance info的数据。

  • 继承TokenEnhancer实现一个JWT内容增强器:
public class JwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        Map<String, Object> info = new HashMap<>();
        info.put("enhance", "enhance info");
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
        return accessToken;
    }
}
  • 创建一个JwtTokenEnhancer实例:

@Configuration
public class JwtTokenStoreConfig {

    //省略代码...

    @Bean
    public JwtTokenEnhancer jwtTokenEnhancer() {
        return new JwtTokenEnhancer();
    }
}
  • 在认证服务器配置中配置JWT的内容增强器:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserService userService;

    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    /**
     * 使用密码模式需要配置
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer); //配置JWT的内容增强器
        delegates.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(delegates);
        endpoints.authenticationManager(authenticationManager)
                .tokenStore(tokenStore) //配置令牌存储策略
                .accessTokenConverter(jwtAccessTokenConverter)
                .tokenEnhancer(enhancerChain);
    }

    //省略代码...
}

  • 运行项目后使用密码模式来获取令牌,之后对令牌进行解析,发现已经包含扩展的内容。
{
  "user_name": "macro",
  "scope": [
    "all"
  ],
  "exp": 1572683821,
  "authorities": [
    "admin"
  ],
  "jti": "1ed1b0d8-f4ea-45a7-8375-211001a51a9e",
  "client_id": "admin",
  "enhance": "enhance info"
}

你可能感兴趣的:(#,Spring,Cloud)