解决Spring OAuth2 每次生成的access_token和refresh_token都是新的问题

一. 问题描述

我们的项目中使用到了Spring OAuth2,但是在利用Spring OAuth2生成access_token和refresh_token的时候,每次传递过来username和password,生成的对应的token都是一个新的值,过期时间也一直是最新的默认值89400秒,说明每次都是重新生成了一个token.

原因是自己这边采用的是默认的InMemory,把生成的token存在在了内存中,没有进行持久化.

二. 解决办法

将生成的token保存在redis中进行持久化即可.

package com.syc.cloud.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

@Configuration
@EnableAuthorizationServer
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//super.configure(clients);

clients.inMemory()
.withClient("oauth-client")
.secret("123456")
.scopes("server")
.authorizedGrantTypes("password","refresh_token")
.accessTokenValiditySeconds(24*3600);
}

@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter converter=new JwtAccessTokenConverter();
ClassPathResource resource=new ClassPathResource("syc-jwt.jks");
KeyStoreKeyFactory factory=new KeyStoreKeyFactory(resource,"syc123".toCharArray());
converter.setKeyPair(factory.getKeyPair("syc-jwt"));
return converter;
}

@Autowired
private RedisConnectionFactory redisConnectionFactory;

@Bean
public TokenStore tokenStore(){
//return new JwtTokenStore(jwtAccessTokenConverter());
//用RedisToken替换JwtToken,解决每次请求access_token和refresh_token都是新的问题.
return new RedisTokenStore(redisConnectionFactory);
}

@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//super.configure(endpoints);
endpoints.tokenStore(tokenStore())
.tokenEnhancer(jwtAccessTokenConverter())
//.tokenServices(tokenServices())
.authenticationManager(authenticationManager);
//该字段设置设置refresh token是否重复使用,true:reuse;false:no reuse
//.reuseRefreshTokens(false);
}

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()");
security.checkTokenAccess("isAuthenticated()");
security.allowFormAuthenticationForClients();
//解决Encoded password does not look like BCrypt报错
//因为springsecurity在最新版本升级后,默认把之前的明文密码方式给去掉了
//https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-storage-updated
security.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}