【SpringSecurity+OAuth2+JWT入门到实战】24.自定义令牌配置

编程

简介

之前获取到的令牌都是基于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)方法,指定 AuthenticationManagerUserDetailService

修改认证服务器配置类WebSecurityConfigurer,在里面注册我们需要的AuthenticationManagerBean:

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)方法主要配置了:

  1. 定义两个client_id,及客户端可以通过不同的client_id来获取不同的令牌;

  2. client_id为myhk1的令牌有效时间为3600秒,client_id为myhk2的令牌有效时间为7200秒;

  3. client_id为myhk1的refresh_token(下面会介绍到)有效时间为864000秒,即10天,也就是说在这10天内都可以通过refresh_token来换取新的令牌;

  4. 在获取client_id为myhk1的令牌的时候,scope只能指定为"all", "read", "write"中的某个值,否则将获取失败;

  5. 只能通过密码模式(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中是否存储了令牌相关信息:

以上是 【SpringSecurity+OAuth2+JWT入门到实战】24.自定义令牌配置 的全部内容, 来源链接: utcz.com/z/514608.html

回到顶部