接下来的内容是:
token 的处理在认证服务器处理的。之前已经配置了资源服务器,现在来自定义认证服务器
spirng boot 2 的自动配置文件和1.5的不一样
直接跟着视频走是不会成功的,原因如下
OAuth2AuthorizationServerConfiguration 类是 @EnableAuthorizationServer
的自动配置类;
如果我们 继承了 AuthorizationServerConfigurerAdapter,那么该类将不会被初始化,认证服务器将不能正常工作
(看源码中的条件注解声明得知)
这是根据自动配置类简化而来的配置。正常使用
package cn.mrcode.imooc.springsecurity.securityapp;
/**
* ${desc}
* @author zhuqiang
* @version 1.0.1 2018/8/7 10:52
* @date 2018/8/7 10:52
* @since 1.0
*/
@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
// @Autowired
// private PasswordEncoder passwordEncoder;
public MyAuthorizationServerConfig(
AuthenticationConfiguration authenticationConfiguration) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("myid")
.secret("myid")
.redirectUris("http://example.com", "http://ora.com")
.and()
.withClient("myid2")
.secret("myid2")
.redirectUris("http://example.com", "localhost:8080")
.authorizedGrantTypes("refresh_token", "password")
.accessTokenValiditySeconds(7200)
.scopes("all", "read", "write");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// 这里使用什么密码需要 根据上面配置client信息里面的密码类型决定
// 目前上面配置的是无加密的密码
security.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}
下面记录下一些注意的地方
yml中数组和对象嵌套的写法如下:
imooc:
security:
oauth2:
clients:
-
clientId: myid
clientSecret: myid
redirectUris:
- "http://example.com"
- "http://ora.com"
accessTokenValiditySeconds: 0
-
clientId: myid2
clientSecret: myid2
authorizedGrantTypes: ["refresh_token", "password"]
redirectUris:
- "http://example.com"
- "localhost:8080"
scopes: ["all", "read", "write"]
accessTokenValiditySeconds: 7200
配置类对应,注意看下面的数组。都是默认为空数组,这样不会导致代码中npe
public class OAuth2Properties {
private OAuth2ClientProperties[] clients = {};
public class OAuth2ClientProperties {
private String clientId;
private String clientSecret;
private String[] authorizedGrantTypes = {};
private String[] redirectUris = {}; // 信任的回调域
private String[] scopes = {};
private int accessTokenValiditySeconds; // token有效期
配置使用的地方
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
InMemoryClientDetailsServiceBuilder inMemory = clients.inMemory();
OAuth2ClientProperties[] clientsInCustom = securityProperties.getOauth2().getClients();
for (OAuth2ClientProperties p : clientsInCustom) {
inMemory.withClient(p.getClientId())
.secret(p.getClientSecret())
.redirectUris(p.getRedirectUris())
.authorizedGrantTypes(p.getAuthorizedGrantTypes())
.accessTokenValiditySeconds(p.getAccessTokenValiditySeconds())
.scopes(p.getScopes());
}
logger.info(Arrays.toString(clientsInCustom));
}
上面的配置都是使用的内存来存储令牌信息;令牌的存储和获取比较频繁,为了能持久化。使用redis
在认证服务配置类中配置tokenStore即可
cn.mrcode.imooc.springsecurity.securityapp.MyAuthorizationServerConfig
@Autowired(required = false)
public TokenStore tokenStore;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
endpoints.tokenStore(tokenStore);
}
TokenStore 需要使用的地方初始化对象,也就是app中
package cn.mrcode.imooc.springsecurity.securityapp;
import cn.mrcode.imooc.springsecurity.securitycore.MyRedisTokenStore;
@Configuration
public class TokenStoreConfig {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore tokenStore() {
return new MyRedisTokenStore(redisConnectionFactory);
}
}
注意在spring boot 2.0.4 中;使用默认的 RedisTokenStore 在存储的时候会出现异常;
解决方案:把RedisTokenStore的代码完全copy一份,然后把 storeAccessToken 方法中调用
conn.set
的代码全部缓存 conn.stringCommands().set
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
byte[] serializedAccessToken = serialize(token);
byte[] serializedAuth = serialize(authentication);
byte[] accessKey = serializeKey(ACCESS + token.getValue());
byte[] authKey = serializeKey(AUTH + token.getValue());
byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
RedisConnection conn = getConnection();
try {
conn.openPipeline();
// 这里 set的时候,子类都不支持存储自己数组
conn.stringCommands().set(accessKey, serializedAccessToken);
一篇不错的redis的配置文章:https://majing.io/posts/10000020931206
切换到jedis中还是会出现一样的错误,所以不用测试jedis了;就是依赖包不兼容的问题,