在开发过程中,一般都不建议使用硬编码的形式,因为硬编码不利于代码维护和升级,举个简单的例子:以PI圆周率为例,当我们在多处代码中使用时,如果需要修改PI值时,就需要在多处代码处对PI值都进行修改。但是如果将常量参数改造为可配置方式,则可通过修改一处,其他所有地方都改变。
在搭建elsa-auth认证服务器时,我们在ElsaAuthorizationServerConfigure配置类里有两处采用了硬编码的形式。
client_id,client_secret
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("elsa")
.secret(passwordEncoder.encode("123456"))
.authorizedGrantTypes("password", "refresh_token")
.scopes("all");
}
accessTokenValiditySeconds和refreshTokenValiditySeconds
public DefaultTokenServices defaultTokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
// 设置为true表示开启刷新令牌的支持
tokenServices.setSupportRefreshToken(true);
// 指定了令牌的基本配置,比如令牌有效时间为60 * 60 * 24秒,刷新令牌有效时间为60 * 60 * 24 * 7秒
tokenServices.setAccessTokenValiditySeconds(60 * 60 * 24);
tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
return tokenServices;
}
现在将它们改造为可配置的方式。
在elsa-auth模块的com.elsa.auth路径下新建properties包,然后在该包下新建一个Client配置类ElsaClientsProperties:
public class ElsaClientsProperties {
private String client;
private String secret;
private String grantType = "password,authorization_code,refresh_token";
private String scope = "all";
public String getClient() {
return client;
}
public void setClient(String client) {
this.client = client;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
client对应client_id,secret对应client_secret,grantType对应当前令牌支持的认证类型,scope对应认证范围。grantType和scope包含默认值。
接着新建一个和Auth相关的配置类ElsaAuthProperties:
@SpringBootConfiguration
//指定读取的配置文件路径;
@PropertySource(value = {"classpath:elsa-auth.properties"})
//指定了要读取的属性的统一前缀名称为elsa.auth;
@ConfigurationProperties(prefix = "elsa.auth")
public class ElsaAuthProperties {
//ElsaClientsProperties数组,因为可能有多个client
private ElsaClientsProperties[] clients = {};
//指定access_token的有效时间,默认值为60 * 60 * 24秒;
private int accessTokenValiditySeconds = 60 * 60 * 24;
//指定refresh_token的有效时间,默认值为60 * 60 * 24 * 7秒。
private int refreshTokenValiditySeconds = 60 * 60 * 24 * 7;
public ElsaClientsProperties[] getClients() {
return clients;
}
public void setClients(ElsaClientsProperties[] clients) {
this.clients = clients;
}
public int getAccessTokenValiditySeconds() {
return accessTokenValiditySeconds;
}
public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
this.accessTokenValiditySeconds = accessTokenValiditySeconds;
}
public int getRefreshTokenValiditySeconds() {
return refreshTokenValiditySeconds;
}
public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) {
this.refreshTokenValiditySeconds = refreshTokenValiditySeconds;
}
}
自定义配置类还需引入spring-boot-configuration-processor依赖,因为这个依赖会在多个微服务子系统里使用到,所以将其添加到elsa-common的pom文件中:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
接下来在elsa-auth的resources路径下新建配置文件elsa-auth.properties:
elsa.auth.accessTokenValiditySeconds=86400
elsa.auth.refreshTokenValiditySeconds=604800
elsa.auth.clients[0].client=elsa
elsa.auth.clients[0].secret=123456
elsa.auth.clients[0].grantType=password,authorization_code,refresh_token
elsa.auth.clients[0].scope=all
去除elsa.auth前缀,剩下部分和ElsaAuthProperties配置类属性名称对应上的话,就会被读取到ElsaAuthProperties相应的属性中。数组形式的属性值使用[]加元素下标表示,具体可以参考properties文件的语法。
定义好ElsaAuthProperties配置类后,我们对ElsaAuthorizationServerConfigure进行修改:
注入ElsaAuthProperties
@Autowired
private ElsaAuthProperties authProperties;
configure方法可配置调整,方法由原先硬编码的形式改造成了从配置文件读取配置的形式,并且判断了client和secret不能为空;
defaultTokenServices方法可配置调整,方法指定有效时间也从原先硬编码的形式改造成了从配置文件读取配置的形式。
ElsaAuthorizationServerConfigure完整代码
@Configuration
@EnableAuthorizationServer
public class ElsaAuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private ElsaUserDetailService userDetailService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private ElsaAuthProperties authProperties;
/**
* 1.客户端从认证服务器获取令牌的时候,必须使用client_id为elsa,client_secret为123456的标识来获取;
* 2. 该client_id支持password模式获取令牌,并且可以通过refresh_token来获取新的令牌;
* 3. 在获取client_id为elsa的令牌的时候,scope只能指定为all,否则将获取失败
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
ElsaClientsProperties[] clientsArray = authProperties.getClients();
InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
if (ArrayUtils.isNotEmpty(clientsArray)) {
for (ElsaClientsProperties client : clientsArray) {
if (StringUtils.isBlank(client.getClient())) {
throw new Exception("client不能为空");
}
if (StringUtils.isBlank(client.getSecret())) {
throw new Exception("secret不能为空");
}
String[] grantTypes = StringUtils.splitByWholeSeparatorPreserveAllTokens(client.getGrantType(), ",");
builder.withClient(client.getClient())
.secret(passwordEncoder.encode(client.getSecret()))
.authorizedGrantTypes(grantTypes)
.scopes(client.getScope());
}
}
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore())
.userDetailsService(userDetailService)
.authenticationManager(authenticationManager)
.tokenServices(defaultTokenServices());
}
// 认证服务器生成的令牌将被存储到Redis中
@Bean
public TokenStore tokenStore() {
return new RedisTokenStore(redisConnectionFactory);
}
@Primary
@Bean
public DefaultTokenServices defaultTokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
// 设置为true表示开启刷新令牌的支持
tokenServices.setSupportRefreshToken(true);
// 指定了令牌的基本配置,比如令牌有效时间为60 * 60 * 24秒,刷新令牌有效时间为60 * 60 * 24 * 7秒
tokenServices.setAccessTokenValiditySeconds(authProperties.getAccessTokenValiditySeconds());
tokenServices.setRefreshTokenValiditySeconds(authProperties.getRefreshTokenValiditySeconds());
return tokenServices;
}
}
源码地址:参数配置化