(八)SpringCloud+Security+Oauth2--token增强个性化和格式化输出

一 token的个性化输出

我们知道token默认的输出格式是:

{
    
    
	"access_token": "21bd6b0b-0c24-40d1-8928-93274aa1180f",
	"token_type": "bearer",
	"refresh_token": "2c38965b-d4ce-4151-b88d-e39f278ce1bb",
	"expires_in": 3599,
	"scope": "all read write"
}

我们可以发现这里并没有包含用户等关键信息,如果我们在此基础上扩展输出,直接可以通过认证接口获取到用户信息等,大大提高系统性能

1.1 源码分析

我们在前面的文章分析知道token是通过DefaultTokenServices来生成的我们看看createAccessToken 核心逻辑

// 默认刷新token 的有效期
private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.
// 默认token 的有效期
private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.

private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
    
    
    DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(uuid);
    token.setExpiration(Date)
    token.setRefreshToken(refreshToken);
    token.setScope(authentication.getOAuth2Request().getScope());
    return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
}

在拼装好token对象后会调用认证服务器配置TokenEnhancer( 增强器) 来对默认的token进行增强

1.2 个性化token

TokenEnhancer.enhance 通过上下文中的用户信息来个性化Token

自定义TokenEnhancer增强器

@Component
public class CustomTokenEnhancer implements TokenEnhancer {
    
    
	private final static String CLIENT_CREDENTIALS = "client_credentials";

	
	@Override
	public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
    
    
		if (CLIENT_CREDENTIALS.equals(authentication.getOAuth2Request().getGrantType())) {
    
    
			return accessToken;
		}

		final Map<String, Object> additionalInfo = new HashMap<>(8);
		User user = (User) authentication.getUserAuthentication().getPrincipal();
		additionalInfo.put("username",user.getUsername());
		// todo 自定义user实现自己想要扩展信息
		((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
		return accessToken;
	}

}

配置

在这里插入图片描述

增强结果

在这里插入图片描述

二 格式化输出

在一些场景下我们需要自定义一下返回报文的格式,例如使用R 对象返回,全部包含code业务码信息」

{
    
      
    "code":1,  
    "msg":"",  
    "data":{
    
      
        "access_token":"e6669cdf-b6cd-43fe-af5c-f91a65041382",  
        "token_type":"bearer",  
        "refresh_token":"da91294d-446c-4a89-bdcf-88aee15a75e8",  
        "expires_in":43199,  
        "scope":"server"  
    }  
}  

2.1 HandlerMethodReturnValueHandler

利用Spring MVC 提供给我们修改方法返回值的接口

public class FormatterToken implements HandlerMethodReturnValueHandler {
    
      

 private static final String POST_ACCESS_TOKEN = "postAccessToken";  

 @Override  
 public boolean supportsReturnType(MethodParameter returnType) {
    
      
     // 判断方法名是否是 oauth2 的token 接口,是就处理  
  return POST_ACCESS_TOKEN.equals(Objects  
    .requireNonNull(returnType.getMethod()).getName());  
 }  
    
  // 获取到返回值然后使用 R对象统一包装  
 @Override  
 public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer container, NativeWebRequest request) throws Exception {
    
      
  ResponseEntity<OAuth2AccessToken> responseEntity = (ResponseEntity) returnValue;  
  OAuth2AccessToken body = responseEntity.getBody();  

  HttpServletResponse response = request.getNativeResponse(HttpServletResponse.class);  
  assert response != null;  
  WebUtils.renderJson(response, R.ok(body));  
 }  
}  

注入FormatterToken,一定要这么处理,不要直接使用 MVCconfig 注入,保证此Handler比 SpringMVC 默认的提前执行。

public class FormatterTokenAutoConfiguration implements ApplicationContextAware, InitializingBean {
    
      
 private ApplicationContext applicationContext;  

 @Override  
 public void afterPropertiesSet() {
    
      
  RequestMappingHandlerAdapter handlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);  
  List<HandlerMethodReturnValueHandler> returnValueHandlers = handlerAdapter.getReturnValueHandlers();  

  List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>();  
  newHandlers.add(new FormatterToken());  
  assert returnValueHandlers != null;  
  newHandlers.addAll(returnValueHandlers);  
  handlerAdapter.setReturnValueHandlers(newHandlers);  
 }  

 @Override  
 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
      
  this.applicationContext = applicationContext;  
 }  
}  

2.2 aop 拦截增强 /oauth/token 接口

@Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")  
public Object handlePostAccessTokenMethod(ProceedingJoinPoint joinPoint) throws Throwable {
    
      
   // 获取原有值,进行包装返回  
      Object proceed = joinPoint.proceed();  

      ResponseEntity<OAuth2AccessToken> responseEntity = (ResponseEntity<OAuth2AccessToken>) proceed;  
        OAuth2AccessToken body = responseEntity.getBody();  
        return ResponseEntity  
                  .status(HttpStatus.OK)  
                  .body(R.ok(body));  
        }  
}  

猜你喜欢

转载自blog.csdn.net/Instanceztt/article/details/128203948