简介
之前获取到的令牌都是基于Spring Security OAuth2默认配置生成的,Spring Security允许我们自定义令牌配置,比如不同的client_id对应不同的令牌,令牌的有效时间,令牌的存储策略等;
自定义令牌配置
让认证服务器HkAuthorizationServerConfig
继承AuthorizationServerConfigurerAdapter
,并重写它的configure(ClientDetailsServiceConfigurer clients)
方法:
package com.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
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;
/**
* 认证服务器
*/
@Configuration
@EnableAuthorizationServer
public class HkAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailService;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailService);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//保存到内存中
clients.inMemory()
.withClient("myhk1")
.secret("myhk111")
//token令牌过期时间 秒
.accessTokenValiditySeconds(3600)
//刷新令牌时间
.refreshTokenValiditySeconds(864000)
//权限
.scopes("all", "read", "write")
//授权模式
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
//配置多个Client
.and()
.withClient("myhk2")
.secret("myhk222")
.accessTokenValiditySeconds(7200);
}
}
认证服务器在继承了AuthorizationServerConfigurerAdapter适配器后,需要重写configure(AuthorizationServerEndpointsConfigurer endpoints)
方法,指定 AuthenticationManager
和UserDetailService
。
修改认证服务器配置类WebSecurityConfigurer
,在里面注册我们需要的AuthenticationManager
Bean:
package com.spring.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* 认证相关配置
*/
@Primary
@Order(90)
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
此外,重写configure(ClientDetailsServiceConfigurer clients)
方法主要配置了:
-
定义两个client_id,及客户端可以通过不同的client_id来获取不同的令牌;
-
client_id为myhk1的令牌有效时间为3600秒,client_id为myhk2的令牌有效时间为7200秒;
-
client_id为myhk1的refresh_token(下面会介绍到)有效时间为864000秒,即10天,也就是说在这10天内都可以通过refresh_token来换取新的令牌;
-
在获取client_id为myhk1的令牌的时候,scope只能指定为"all", "read", "write"中的某个值,否则将获取失败;
-
只能通过密码模式(password,authorization_code)来获取client_id为test1的令牌,而myhk2则无限制。
启动项目,演示几个效果。启动项目后使用密码模式获取myhk1的令牌:
控制台输出了 Encoded password does not look like BCrypt 的告警。
在新版本的spring-cloud-starter-oauth2指定client_secret的时候需要进行加密处理:
.secret(new BCryptPasswordEncoder().encode("myhk111"))
在前面自定义登录认证获取令牌一节中,我们在HkAuthenticationSuccessHandler
判断了client_secret的值是否正确。由于我们这里client_secret加密了,所以判断逻辑需要调整为下面这样:
// 3. 校验 ClientId和 ClientSecret的正确性
if (clientDetails == null) {
throw new UnapprovedClientAuthenticationException("clientId:" + clientId + "对应的信息不存在");
} else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
throw new UnapprovedClientAuthenticationException("clientSecret不正确");
} else {
// 4. 通过 TokenRequest构造器生成 TokenRequest
tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), "custom");
}
修改后重启项目,重新使用密码模式获取令牌:
{
"access_token": "4051d591-4927-40e1-aae0-8d1b0d982618",
"token_type": "bearer",
"refresh_token": "76192780-121d-499d-a07f-630af95da58a",
"expires_in": 3599,
"scope": "all read write"
}
可以看到expires_in
的时间是我们定义的3600秒。
作为高可用框架我们把ClientId提取到系统配置:
package com.spring.security.properties;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class OAuth2ClientProperties {
private String clientId;
private String clientSecret;
//tpken令牌过期时间
private int accessTokenValiditySeconds;
//令牌刷新时间
private int refreshTokenValiditySeconds;
}
package com.spring.security.properties;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class OAuth2Properties {
private OAuth2ClientProperties[] clients = {};
}
package com.spring.security.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 安全属性
*/
@Data
@ConfigurationProperties(prefix = "hk.security")
public class SecurityProperties {
private BrowserProperties browser = new BrowserProperties();
private ValidateCodeProperties code = new ValidateCodeProperties();
private SocialProperties social = new SocialProperties();
private OAuth2Properties oauth2 = new OAuth2Properties();
}
配置:
hk:
security:
oauth2:
clients[0]:
clientId: myhk1
clientSecret: myhk111
accessTokenValiditySeconds: 3600
refreshTokenValiditySeconds: 3600
clients[1]:
clientId: myhk2
clientSecret: myhk222
accessTokenValiditySeconds: 7200
refreshTokenValiditySeconds: 7200
改造HkAuthorizationServerConfig类:
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//保存到内存中
InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
//判断系统是否配置
if (ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {
//循环
for (OAuth2ClientProperties config : securityProperties.getOauth2().getClients()) {
builder.withClient(config.getClientId())
.secret(new BCryptPasswordEncoder().encode(config.getClientSecret()))
//token令牌过期时间 秒
.accessTokenValiditySeconds(config.getAccessTokenValiditySeconds())
//刷新令牌时间
.refreshTokenValiditySeconds(config.getRefreshTokenValiditySeconds())
//权限
.scopes("all", "read", "write")
//授权模式
.authorizedGrantTypes("password", "authorization_code", "refresh_token");
}
}
}
令牌存储
默认令牌是存储在内存中的,我们可以将它保存到第三方存储中,比如Redis。
创建TokenStoreConfig
:
package com.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
@Configuration
public class TokenStoreConfig {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore redisTokenStore() {
return new RedisTokenStore(redisConnectionFactory);
}
}
然后在认证服务器里指定该令牌存储策略。重写configure(AuthorizationServerEndpointsConfigurer endpoints)
方法:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private TokenStore redisTokenStore;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailService);
}
......
}
重启项目获取令牌后,查看Redis中是否存储了令牌相关信息:
来源:oschina
链接:https://my.oschina.net/u/1046143/blog/3207709