【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)方法,指定 AuthenticationManager和UserDetailService。
修改认证服务器配置类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)方法主要配置了:
定义两个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中是否存储了令牌相关信息:
以上是 【SpringSecurity+OAuth2+JWT入门到实战】24.自定义令牌配置 的全部内容, 来源链接: utcz.com/z/514608.html

