Spring Cloud OAuth2中/oauth/token的返回内容格式

背景

在前后端分离的项目中,一般后端返回给前端的格式是一个固定的json格式。在这个前提下,Spring Cloud OAuth2 生成access token的请求/oauth/token的返回内容就需要自定义。

访问/oauth/token示例如下:

Spring Cloud OAuth2中/oauth/token的返回内容格式_第1张图片

原始返回值的格式如下:

Spring Cloud OAuth2中/oauth/token的返回内容格式_第2张图片

我们希望使用我们自己固定的json格式,如下:

Spring Cloud OAuth2中/oauth/token的返回内容格式_第3张图片

实现原理

原理就是通过切面编程实现对/oauth/token端点请求的结果进行拦截封装处理,由于/oauth/token是Spring Cloud OAuth2的内部端点,因此需要对相关的Spring源码进行分析。最终定位到

org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken()

方法上。

代码实现

相关类

CodeEnum.java

package com.wongoing.common.model;

/**
 * @description: 代码枚举
 * @author: zheng
 * @date: Created in 2021/1/26 11:18
 * @version: 0.0.1
 * @modified By:
 */
public enum CodeEnum {
    SUCCESS(0),
    ERROR(1);

    private Integer code;

    CodeEnum(Integer code) {
        this.code = code;
    }

    public Integer getCode() {
        return this.code;
    }
}

Result.java

package com.wongoing.common.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @description: Rest API 接口方法返回类型定义
 * @author: zheng
 * @date: Created in 2021/1/26 13:25
 * @version: 0.0.1
 * @modified By:
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result implements Serializable {
    private T data;
    private Integer code;
    private String msg;

    public static  Result of(T data, Integer code, String msg) {
        return new Result<>(data, code, msg);
    }

    public static  Result succeed(String msg) {
        return of(null, CodeEnum.SUCCESS.getCode(), msg);
    }

    public static  Result succeed(T model, String msg) {
        return of(model, CodeEnum.SUCCESS.getCode(), msg);
    }

    public static  Result succeed(T model) {
        return of(model, CodeEnum.SUCCESS.getCode(), "");
    }

    public static  Result failed(String msg) {
        return of(null, CodeEnum.ERROR.getCode(), msg);
    }

    public static  Result failed(T model, String msg) {
        return of(model, CodeEnum.ERROR.getCode(), msg);
    }
}

关键切面拦截器

在uaa项目中定义OauthTokenAspect.java

package com.wongoing.oauth2.filter;

import com.wongoing.common.constant.SecurityConstants;
import com.wongoing.common.context.TenantContextHolder;
import com.wongoing.common.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Component;

import java.security.Principal;
import java.util.Map;

/**
 * @description: oauth-token拦截器
 * 1. 赋值租户
 * 2. 统一返回token格式
 *
 * @author: zheng
 * @date: Created in 2021/7/12 16:25
 * @version: 0.0.1
 * @modified By:
 */
@Slf4j
@Component
@Aspect
public class OauthTokenAspect {

    @Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
    public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            Object[] args = joinPoint.getArgs();
            Principal principal = (Principal) args[0];
            if (!(principal instanceof Authentication)) {
                throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
            }
            String clientId = this.getClientId(principal);
            Map parameters = (Map) args[1];
            String grantType = parameters.get(OAuth2Utils.GRANT_TYPE);

            //保存租户id
            TenantContextHolder.setTenant(clientId);
            Object proceed = joinPoint.proceed();
            if (SecurityConstants.AUTHORIZATION_CODE.equals(grantType)) {
                /**
                 * 如果使用 @EnableOAuth2Sso 注解不能修改返回格式,否则授权码模式可以统一改
                 * 因为本项目的 sso-demo/ss-sso 里面使用了 @EnableOAuth2Sso 注解,所以这里就不修改授权码模式的token返回值了
                 */
                return proceed;
            } else {
                ResponseEntity responseEntity = (ResponseEntity) proceed;
                OAuth2AccessToken body = responseEntity.getBody();
                return ResponseEntity
                        .status(HttpStatus.OK)
                        .body(Result.succeed(body));
            }
        } finally {
            TenantContextHolder.clear();
        }
    }

    private String getClientId(Principal principal) {
        Authentication client = (Authentication) principal;
        if (!client.isAuthenticated()) {
            throw new InsufficientAuthenticationException("The client is not authenticated.");
        }
        String clientId = client.getName();
        if (client instanceof OAuth2Authentication) {
            clientId = ((OAuth2Authentication) client).getOAuth2Request().getClientId();
        }
        return clientId;
    }
}

其中的常量值:

public abstract class OAuth2Utils {
	public static final String GRANT_TYPE = "grant_type";
}
public interface SecurityConstants {
	/**
     * 授权码模式
     */
    String AUTHORIZATION_CODE = "authorization_code";
}

到此这篇关于Spring Cloud OAuth2中/oauth/token的返回内容格式的文章就介绍到这了,更多相关Spring Cloud OAuth2返回内容格式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(Spring Cloud OAuth2中/oauth/token的返回内容格式)