• (八)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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在拼装好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;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    配置

    在这里插入图片描述

    增强结果

    在这里插入图片描述

    格式化输出

    在一些场景下我们需要自定义一下返回报文的格式,例如使用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"  
        }  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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));  
     }  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    注入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;  
     }  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    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));  
            }  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    排列(全排,前一个,下一个)
    Minio分布式存储入门(使用新版本)
    RabbitMQ生产故障问题分析
    一文带你了解 Spring Security 集成 Authing OIDC 认证
    Redis十大数据类型
    Docker | docker常用命令
    接口测试工具的实验,Postman、Swagger、knife4j(黑马头条)
    Java面向对象
    1、Kafka急速入门
    从collections库的Counter类看items()方法和enumerate()方法
  • 原文地址:https://blog.csdn.net/Instanceztt/article/details/128203948