Spring Security 增强点

发布于:2022-11-01 ⋅ 阅读:(514) ⋅ 点赞:(0)


本文不详述如何创建 JWT,oauth 2.0 服务端可以参考文章: Spring Security oauth2.0 服务端

在这里插入图片描述

 

JWT 添加额外信息

oauth 2.0 JWT 默认返回 OAuth2AccessToken 接口的实现类,默认实现类是 DefaultOAuth2AccessToken,返回字段有 5 个

  • access_token:表示访问令牌,必选项
  • token_type:表示令牌类型,该值大小写不敏感,必选项,默认是 bearer 类型
  • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
  • refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
  • scope:表示权限范围,如果与客户端申请的范围一致,此处可省略
     

TokenEhancer

往 JWT 添加额外信息,首先要实现 TokenEhancer(令牌增强器)接口

  • DefaultOAuth2AccessToken:默认的 token 实现类
  • setAdditionalInformation:添加其他信息,参数类型是 Map<String, Object>
@Bean
public TokenEnhancer customTokenEnhancer() {
    return (accessToken, authentication) -> {
        final Map<String, Object> additionalInfo = new HashMap<>();
        additionalInfo.put("fat", "test");
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        return accessToken;
    };
}

 

AuthorizationServerEndpointsConfigurer

把 TokenEnhancer 添加到 AuthorizationServerConfigurerAdapter 里

  • 创建 TokenEnhancerChain,传参类型为 list
  • 创建 DefaultTokenServices,传入 TokenEnhancerChain
  • endpoints 传入 DefaultTokenServices
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
...

   @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        enhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter(), customTokenEnhancer()));

        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenEnhancer(enhancerChain);
        tokenServices.setTokenStore(tokenStore());

        endpoints
                .authenticationManager(authenticationManager)
                .tokenServices(tokenServices)
        ;
    }

...
}

访问 /oauth/token 接口时,返回值返回插入的 fat 字段
在这里插入图片描述

JWT 解析发现没有 fat 字段
在这里插入图片描述

这里有一个值得注意的地方了,若要 JWT 解析时有新增字段,在 setTokenEnhancers 时,参数 list 需要把自定义的 customTokenEnhancer() 放前面

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), jwtAccessTokenConverter()));

        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenEnhancer(enhancerChain);
        tokenServices.setTokenStore(tokenStore());

        endpoints
                .authenticationManager(authenticationManager)
                .tokenServices(tokenServices)
        ;
    }

 

check/token 添加额外信息

自定义 AccessTokenConverter

  • 继承 DefaultAccessTokenConverter
  • 重新 convertAccessToken 方法,添加需要新增的字段
@Configuration
public class AuthAccessTokenConverter extends DefaultAccessTokenConverter {

    @Override
    public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
        Map<String, Object> response = (Map<String, Object>) super.convertAccessToken(token, authentication);
        response.put("feng","myfat");
        return response;
    }
    
}

把新建的 AccessTokenConverter 放进配置里


@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
  
    @Autowired
    private AuthAccessTokenConverter converter;
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
 
		...		
        endpoints.accessTokenConverter(converter);
    }
    
}

在这里插入图片描述

 

每次返回新 token

默认情况下,通过相同的参数请求 /oauth/token 接口,返回的 access_token 是同一个 token,而 expires_in 会减少,expires_in 是 token 的有效期,单位为秒

在这里插入图片描述

很多场景,都需要我们每次请求都返回一个新的 token,那么如何实现呢,下面先了解一下 DefaultAuthenticationKeyGenerator

  • 默认的身份验证密钥生成器
  • 根据 client id、scope、username 生成
  • authentication.isClientOnly():判断 grant_type 是否 client_credentials 模式
	public String extractKey(OAuth2Authentication authentication) {
		Map<String, String> values = new LinkedHashMap<String, String>();
		OAuth2Request authorizationRequest = authentication.getOAuth2Request();
		if (!authentication.isClientOnly()) {
			values.put(USERNAME, authentication.getName());
		}
		values.put(CLIENT_ID, authorizationRequest.getClientId());
		if (authorizationRequest.getScope() != null) {
			values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope())));
		}
		return generateKey(values);
	}

很容易看出,只要 values 不相同,那么生成的 token 就不相同

那么思路来了

  • 新增 AuthenticationKeyGenerator 继承 DefaultAuthenticationKeyGenerator
  • 重新 extractKey() 方法
  • values 添加永远不重复的 CODE
  • 把 AuthenticationKeyGenerator 添加到 TokenStore

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	...
	
	private class AuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {
	
	    private static final String CLIENT_ID = "client_id";
	
	    private static final String SCOPE = "scope";
	
	    private static final String USERNAME = "username";
	
	    private static final String CODE = "code";
	
	    @Override
	    public String extractKey(OAuth2Authentication authentication) {
	        Map<String, String> values = new LinkedHashMap<String, String>();
	        OAuth2Request authorizationRequest = authentication.getOAuth2Request();
	        if (!authentication.isClientOnly()) {
	            values.put(USERNAME, authentication.getName());
	        }
	        values.put(CLIENT_ID, authorizationRequest.getClientId());
	        if (authorizationRequest.getScope() != null) {
	            values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope())));
	        }
	        UUID uuid4 = UUID.randomUUID();
	        values.put(CODE, uuid4.toString());
	        return generateKey(values);
	    }
	}
	
	@Bean
	public TokenStore tokenStore() {
	    InMemoryTokenStore tokenStore = new InMemoryTokenStore();
	    tokenStore.setAuthenticationKeyGenerator(new AuthenticationKeyGenerator());
	    return tokenStore;
	}

	...
}
本文含有隐藏内容,请 开通VIP 后查看