(八)SpringCloud+Security+Oauth2--token增强个性化和格式化输出
创始人
2024-03-21 22:32:14
0

一 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";@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {if (CLIENT_CREDENTIALS.equals(authentication.getOAuth2Request().getGrantType())) {return accessToken;}final Map 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 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 returnValueHandlers = handlerAdapter.getReturnValueHandlers();  List 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 responseEntity = (ResponseEntity) proceed;  OAuth2AccessToken body = responseEntity.getBody();  return ResponseEntity  .status(HttpStatus.OK)  .body(R.ok(body));  }  
}  

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...