Spring Security OAuth2 Redis 模式下认证服务器

四种授权码模式

  • 授权码模式
  • 密码模式
  • 客户端模式
  • 简化模式

密码模式

  • grant_type:授权类型,必选,此处固定值“password”
  • username:表示用户名,必选
  • password:表示用户密码,必选
  • scope:权限范围,可选

(1)获取access_token

1
http://localhost:9003/oauth/token?username=user&password=password&grant_type=password&client_id=client&client_secret=secret
1
2
3
4
5
6
7
8
{
"access_token":"9a263f17f894488fa0973136e6809198",
"token_type":"bearer",
"refresh_token":"1d9b97d9eeb74c62adf9f834868518e3",
"expires_in":59,
"scope":"all",
"client_id":"client_password"
}

(2)通过refresh_token获取access_token

1
http://localhost:9003/oauth/token?grant_type=refresh_token&refresh_token=1d9b97d9eeb74c62adf9f834868518e3&client_id=client_password&client_secret=secret
1
2
3
4
5
6
7
8
{
"access_token":"9229d299bf264b0d8771306d1da71f42",
"token_type":"bearer",
"refresh_token":"1d9b97d9eeb74c62adf9f834868518e3",
"expires_in":59,
"scope":"all",
"client_id":"client_password"
}

授权码模式

  • client_id:客户端ID,必选
  • response_type:必须为code,必选
  • redirect_uri:回调url,必选

(1)获取授权码

1
http://localhost:9003/oauth/authorize?client_id=auth_code&response_type=code&redirect_uri=http://localhost:9003/auth_user/get_auth_code

(2)获取access_token

1
http://localhost:9003/oauth/token?grant_type=authorization_code&code=S4x5Yi&client_id=auth_code&client_secret=secret&redirect_uri=http://localhost:9003/auth_user/get_auth_code
1
2
3
4
5
6
7
8
{
"access_token":"46d216c6e19c4761a95c3313883574d7",
"token_type":"bearer",
"refresh_token":"5124987a8e4643088de2ba03bdb24730",
"expires_in":59,
"scope":"select replace insert update del",
"client_id":"auth_code"
}

若报错如下:

1
{"status":405,"message":"Request method 'GET' not supported"}

参考:https://github.com/lexburner/oauth2-demo/issues/3

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class OAuthSecurityConfig extends AuthorizationServerConfigurerAdapter {
...
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
...
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); //add get method
...

endpoints.tokenServices(tokenServices);
}
...
}

(3)通过refresh_token获取access_token

1
http://localhost:9003/oauth/token?grant_type=refresh_token&refresh_token=5124987a8e4643088de2ba03bdb24730&client_id=auth_code&client_secret=secret
1
2
3
4
5
6
7
8
{
"access_token":"c90e2b29cc2e4b9192e68566148db8cf",
"token_type":"bearer",
"refresh_token":"604baf02b5294daa951fea6fede6a398",
"expires_in":59,
"scope":"select replace insert update del",
"client_id":"auth_code"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
D:\Server\Redis-x64-3.2.100>redis-cli.exe
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> keys *
1) "refresh:604baf02b5294daa951fea6fede6a398"
2) "access_to_refresh:c90e2b29cc2e4b9192e68566148db8cf"
3) "auth_to_access:a724bdfe1be1f3d30a3c89764c2adca9"
4) "refresh_auth:604baf02b5294daa951fea6fede6a398"
5) "refresh_to_access:604baf02b5294daa951fea6fede6a398"
6) "codeCache"
7) "client_id_to_access:auth_code"
8) "uname_to_access:auth_code:admin"
9) "auth:c90e2b29cc2e4b9192e68566148db8cf"
10) "access:c90e2b29cc2e4b9192e68566148db8cf"
11) "test"
127.0.0.1:6379>

(4)通过refresh_token获取新的access_token时可以自定义用户信息验证service

1
http://localhost:9003/auth_user/get_token_info?access_token=c90e2b29cc2e4b9192e68566148db8cf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"authorities":[
{
"authority":"{"interfaces":["/a/b","/a/c","/oauth/token"]}"
},
{
"authority":"{"username":"admin"}"
}
],
"details":{
"remoteAddress":"0:0:0:0:0:0:0:1",
"sessionId":"8233149F60205EF21BAD74B80BFE7F06"
},
"authenticated":true,
"principal":{
"password":null,
"username":"admin",
"authorities":[
{
"authority":"{"interfaces":["/a/b","/a/c","/oauth/token"]}"
},
{
"authority":"{"username":"admin"}"
}
],
"accountNonExpired":true,
"accountNonLocked":true,
"credentialsNonExpired":true,
"enabled":true
},
"credentials":null,
"name":"admin"
}

Client模式

  • client_id: 客户端ID,必选
  • client_secret: 客户端密码,必选
  • grant_type: 必须为password,必选
  • scope: 授权范围,必选
1
http://localhost:9003/oauth/token?grant_type=client_credentials&scope=insert&client_id=client&client_secret=secret
1
2
3
4
5
6
7
{
"access_token":"a1016035f36f493391648bb57b1bf6a7",
"token_type":"bearer",
"expires_in":59,
"scope":"insert",
"client_id":"client"
}

极简模式

1
http://localhost:9003/oauth/authorize?client_id=client_implicit&response_type=token&redirect_uri=http://localhost:9003/auth_user/get_token_info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"authorities":[
{
"authority":"{"interfaces":["/a/b","/a/c","/oauth/token"]}"
},
{
"authority":"{"username":"admin"}"
}
],
"details":{
"remoteAddress":"0:0:0:0:0:0:0:1",
"sessionId":"9B935C3B239869DF4A840B57F512F2E7"
},
"authenticated":true,
"principal":{
"password":null,
"username":"admin",
"authorities":[
{
"authority":"{"interfaces":["/a/b","/a/c","/oauth/token"]}"
},
{
"authority":"{"username":"admin"}"
}
],
"accountNonExpired":true,
"accountNonLocked":true,
"credentialsNonExpired":true,
"enabled":true
},
"credentials":null,
"name":"admin"
}

用户认证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @Description: 用户认证
* @Package: cn.appblog.security.oauth2.service.UserAuthDetailsService
* @Version: 1.0
*/
@Component
public class UserAuthDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;

/**
* 根据用户名查询用户角色、权限等信息
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
GrantedAuthority authority = new UserGrantedAuthority("username", username);

JSONArray array = new JSONArray();
array.add("/a/b");
array.add("/a/c");
array.add("/oauth/token");
GrantedAuthority interfaces = new UserGrantedAuthority("interfaces", array);

/**
isEnabled 账户是否启用
isAccountNonExpired 账户没有过期
isCredentialsNonExpired 身份认证是否是有效的
isAccountNonLocked 账户没有被锁定
*/
return new User(username, passwordEncoder.encode("123456"),
true,
true,
true,
true,
Arrays.asList(authority, interfaces));
}

}

OAuth2授权服务配置

在Security OAuth2授权服务配置类中添加上自定义的用户信息校验类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
DefaultTokenServices tokenServices = new DefaultTokenServices();
//token持久化容器
tokenServices.setTokenStore(tokenStore);
//客户端信息
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
//自定义token生成
tokenServices.setTokenEnhancer(tokenEnhancer());
//access_token 的有效时长 (秒), 默认 12 小时
tokenServices.setAccessTokenValiditySeconds(60 * 1);
//refresh_token 的有效时长 (秒), 默认 30 天
tokenServices.setRefreshTokenValiditySeconds(60 * 2);
//是否支持refresh_token,默认false
tokenServices.setSupportRefreshToken(true);
//是否复用refresh_token,默认为true(如果为false,则每次请求刷新都会删除旧的refresh_token,创建新的refresh_token)
tokenServices.setReuseRefreshToken(false);

endpoints
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) // add get method
//通过authenticationManager开启密码授权
.authenticationManager(authenticationManager)
//自定义refresh_token刷新令牌对用户信息的检查,以确保用户信息仍然有效
.userDetailsService(userAuthDetailsService)
//token相关服务
.tokenServices(tokenServices)
.pathMapping("/oauth/confirm_access", "/custom/confirm_access")
//自定义异常转换处理类
.exceptionTranslator(webResponseExceptionTranslator);
}

自定义token生成

  • 自定义一个实现TokenEnhancer接口的token增强器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
**
* @Description: 用户自定义token令牌,包括access_token和refresh_token
* @Package: cn.appblog.security.oauth2.enhancer.UserTokenEnhancer
* @Version: 1.0
*/
public class UserTokenEnhancer implements TokenEnhancer {
/**
* 重新定义令牌token
*/
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
if (accessToken instanceof DefaultOAuth2AccessToken) {
DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;
token.setValue(getToken());
//使用DefaultExpiringOAuth2RefreshToken类生成refresh_token,自带过期时间,否则不生效,refresh_token一直有效
DefaultExpiringOAuth2RefreshToken refreshToken = (DefaultExpiringOAuth2RefreshToken) token.getRefreshToken();
//OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken instanceof DefaultExpiringOAuth2RefreshToken) {
token.setRefreshToken(new DefaultExpiringOAuth2RefreshToken(getToken(), refreshToken.getExpiration()));
}
Map<String, Object> additionalInformation = Maps.newHashMap();
additionalInformation.put("client_id", authentication.getOAuth2Request().getClientId());
//添加额外配置信息
token.setAdditionalInformation(additionalInformation);
return token;
}
return accessToken;
}

/**
* 生成自定义token
*/
private String getToken() {
return StringUtils.join(UUID.randomUUID().toString().replace("-", ""));
}
}
  • 将自定义的token增强器加入IOC容器中
1
2
3
4
5
6
7
/**
* 自定义生成令牌token
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return new UserTokenEnhancer();
}
  • 将token增强器加入授权配置端点
1
2
3
4
5
6
7
8
9
10
/**
* 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
DefaultTokenServices tokenServices = new DefaultTokenServices();
...
tokenServices.setTokenEnhancer(tokenEnhancer());
...
}

自定义token过期时长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
DefaultTokenServices tokenServices = new DefaultTokenServices();
//token持久化容器
tokenServices.setTokenStore(tokenStore);
//客户端信息
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
//自定义token生成
tokenServices.setTokenEnhancer(tokenEnhancer());
//access_token 的有效时长 (秒), 默认 12 小时
tokenServices.setAccessTokenValiditySeconds(60 * 1);
//refresh_token 的有效时长 (秒), 默认 30 天
tokenServices.setRefreshTokenValiditySeconds(60 * 2);
//是否支持refresh_token,默认false
tokenServices.setSupportRefreshToken(true);
//是否复用refresh_token, 默认为true(如果为false, 则每次请求刷新都会删除旧的refresh_token, 创建新的refresh_token)
tokenServices.setReuseRefreshToken(false);

...
}

认证服务器配置 - token存入redis缓存

  • 使用Redis缓存需要引入的依赖
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 认证服务器配置代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
* @Description: @EnableAuthorizationServer注解开启OAuth2授权服务机制, 优先级顺序order=0
* @Package: cn.appblog.security.oauth2.config.OAuth2ServerConfig
* @Version: 1.0
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private OAuth2ClientDetailsService oAuth2ClientDetailsService;
@Autowired
private UserAuthDetailsService userAuthDetailsService;
@Autowired
private WebResponseExceptionTranslator webResponseExceptionTranslator;
@Autowired
private TokenStore tokenStore;
@Autowired
private OAuthTokenAuthenticationFilter oAuthTokenAuthenticationFilter;

/**
* 用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里初始化,
* 你可以把客户端详情信息写死也可以写入内存或者数据库中
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//使用自定义ClientDetailsService初始化配置
clients.withClientDetails(oAuth2ClientDetailsService);
}

/**
* 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
DefaultTokenServices tokenServices = new DefaultTokenServices();
//token持久化容器
tokenServices.setTokenStore(tokenStore);
//客户端信息
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
//自定义token生成
tokenServices.setTokenEnhancer(tokenEnhancer());
//access_token 的有效时长 (秒), 默认 12 小时
tokenServices.setAccessTokenValiditySeconds(60 * 1);
//refresh_token 的有效时长 (秒), 默认 30 天
tokenServices.setRefreshTokenValiditySeconds(60 * 2);
//是否支持refresh_token,默认false
tokenServices.setSupportRefreshToken(true);
//是否复用refresh_token,默认为true(如果为false,则每次请求刷新都会删除旧的refresh_token,创建新的refresh_token)
tokenServices.setReuseRefreshToken(false);

endpoints
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) // add get method
//通过authenticationManager开启密码授权
.authenticationManager(authenticationManager)
//自定义refresh_token刷新令牌对用户信息的检查,以确保用户信息仍然有效
.userDetailsService(userAuthDetailsService)
//token相关服务
.tokenServices(tokenServices)
/**
pathMapping用来配置端点URL链接,第一个参数是端点URL默认地址,第二个参数是你要替换的URL地址
上面的参数都是以“/”开头,框架的URL链接如下:
/oauth/authorize:授权端点。对应的类:AuthorizationEndpoint.java
/oauth/token:令牌端点。对应的类:TokenEndpoint.java
/oauth/confirm_access:用户确认授权提交端点。对应的类:WhitelabelApprovalEndpoint.java
/oauth/error:授权服务错误信息端点
/oauth/check_token:用于资源服务访问的令牌解析端点
/oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话
*/
.pathMapping("/oauth/confirm_access", "/custom/confirm_access")
//自定义异常转换处理类
.exceptionTranslator(webResponseExceptionTranslator);
}

/**
* 用来配置令牌端点(Token Endpoint)的安全约束
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
/**
* 主要是让/oauth/token支持client_id和client_secret做登陆认证
* 如果开启了allowFormAuthenticationForClients,那么就在BasicAuthenticationFilter之前
* 添加ClientCredentialsTokenEndpointFilter,使用ClientDetailsUserDetailsService来进行
* 登陆认证
*/
.allowFormAuthenticationForClients()
//oauth/token端点过滤器
.addTokenEndpointAuthenticationFilter(oAuthTokenAuthenticationFilter);
}

/**
* ApprovalStore用户保存、检索和撤销用户审批的界面
*/
/*
@Bean
public ApprovalStore approvalStore() throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore());
return store;
}
*/

/*
@Bean
public UserApprovalHandler userApprovalHandler1(){
TokenStoreUserApprovalHandler userApprovalHandler = new TokenStoreUserApprovalHandler();
userApprovalHandler.setTokenStore(tokenStore());
return userApprovalHandler;
}
*/

/**
* 自定义生成令牌token
*/
@Bean
public TokenEnhancer tokenEnhancer() {
return new UserTokenEnhancer();
}

}
  • Redis缓存配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
##单机应用环境配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
#spring.redis.password=
##Redis数据库索引,默认0
spring.redis.database=0
#spring.redis.timeout=

##redis连接池配置
## 连接池中的最小空闲连接,默认0
spring.redis.jedis.pool.min-idle=0
## 连接池中的最大空闲连接,默认8
spring.redis.jedis.pool.max-idle=8
## 连接池最大阻塞等待时间(使用负值表示没有限制),默认-1ms
spring.redis.jedis.pool.max-wait=-1ms
##连接池最大连接数(使用负值表示没有限制),默认8
spring.redis.jedis.pool.max-active=8

基于Spring Security的安全认证

替换的核心是将InMemoryTokenStore对象更换为RedisTokenStore对象,并传递一个RedisConnectionFactory接口,接口的具体实现类是JedisConnectionFactory类;

RedisConnectionFactory可以通过如下三个配置类应用在不同的应用场景:

  • RedisStandaloneConfiguration:RedisConnectionFactory 工厂类单机模式的配置类
  • RedisSentinelConfiguration:RedisConnectionFactory 工厂类高可用模式的配置类
  • RedisClusterConfiguration:RedisConnectionFactory 工厂类集群模式的配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* @Description: 启动基于Spring Security的安全认证, 优先级顺序order=100
* @Package: cn.appblog.security.oauth2.config.WebSecurityConfigurer
* @Version: 1.0
*/
@Configuration
@EnableWebSecurity(debug = true)
@Order(2)
public class BaseSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private UserAuthDetailsService authUserDetailsService;
@Autowired
private UserAuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private UserAccessDeniedHandler accessDeniedHandler;
@Autowired
private UserLogoutSuccessHandler logoutSuccessHandler;

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
// .requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**", "/test/**")
// .and()
// .authorizeRequests()
// .antMatchers("/oauth/**").authenticated()
// .and()
// .formLogin().loginPage("/test/login").permitAll(); //新增login form支持用户登录及授权
.requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**", "/test/**")
.and()
// 指定支持基于表单的身份验证, 如果未指定FormLoginConfigurer#loginPage(String), 则将生成默认登录页面
.formLogin()
// 自定义登录页url, 默认为/login
.loginPage("/test/login")
// 登录请求拦截的url, 也就是form表单提交时指定的action
.loginProcessingUrl("/user/login")
// 用户名的请求字段 username
.usernameParameter("username")
// 密码的请求字段 默认为password
.passwordParameter("password")
// 登录成功
// .successHandler(authenticationSuccessHandler)
// 登录失败
.failureHandler(authenticationFailureHandler)
// 无条件允许访问
.permitAll()
.and()
.requestMatchers()
.anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/**")
.authenticated()
//.permitAll()
/* .and()
// 其它的请求要求必须有身份认证
.authorizeRequests()
.anyRequest()
.authenticated()
*/
.and()
.logout()
.logoutSuccessHandler(logoutSuccessHandler)
.permitAll()
.and()
// 认证过的用户访问无权限资源时的处理
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
http.csrf().disable();
}

@Override
public void configure(WebSecurity web) throws Exception {
//忽略swagger访问权限限制
// web.ignoring().antMatchers(
// "/userlogin",
// "/userlogout",
// "/userjwt",
// "/v2/api-docs",
// "/swagger-resources/configuration/ui",
// "/swagger-resources",
// "/swagger-resources/configuration/security",
// "/swagger-ui.html",
// "/css/**",
// "/js/**",
// "/images/**",
// "/webjars/**",
// "**/favicon.ico",
// "/index");
super.configure(web);
}

/**
* Spring Security认证服务中的相关实现重新定义
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 加入自定义的安全认证
auth.userDetailsService(this.authUserDetailsService)
.passwordEncoder(this.passwordEncoder())
.and()
.authenticationProvider(smsAuthenticationProvider())
.authenticationProvider(authenticationProvider());
}

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

/**
* Spring security认证Bean
*/
@Bean
public AuthenticationProvider authenticationProvider() {
AuthenticationProvider authenticationProvider = new UserAuthenticationProvider();
return authenticationProvider;
}

@Bean
public AuthenticationProvider smsAuthenticationProvider() {
AuthenticationProvider authenticationProvider = new UserSmsAuthenticationProvider();
return authenticationProvider;
}

/**
* 自定义加密
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

/**
* OAuth2 token持久化接口
*/
@Bean
public TokenStore tokenStore() {
//token保存在内存中(也可以保存在数据库、Redis中)
//如果保存在中间件(数据库、Redis),那么资源服务器与认证服务器可以不在同一个工程中
//注意:如果不保存access_token,则没法通过access_token取得用户信息
//return new InMemoryTokenStore();
return new RedisTokenStore(redisConnectionFactory);
}
}

本文转载参考 原文 并加以调试

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :