外部授权

更新时间: 2022-09-22 17:45:31

API 网关支持外部授权功能。通过该功能,用户可以在网关将请求转发给 real-server 之前,插入一个自定义的远程接口。网关会先将请求转发给这个自定义接口,该接口继而选择是否允许这个请求正常转发到 real-server。

外部授权服务编写须知

您需要在本地开发一个外部授权接口。当 API 需要验证授权关系时,会调用该外部授权接口进行授权校验。外部授权接口不限制协议类型,但会限制请求体和响应体。请求和响应均为 JSON 字符串,格式如下:

  • 请求体

    {
    "context":{
    "key":"value"
    }
    }

    字段说明:Request 中只有一个 context 字段,格式为 kv。这些参数来自于 client 的 request,需要在 API 网关控制台平台进行配置。

  • 响应体

    {
    "success":true/false,
    "principal":{
    "key":"value"
    },
    "failResponseHeader":{
    "key":"value"
    },
    "failResponseBody": jsonarray/jsonobject
    "failResponseStatus": ${httpcode}
    }

    字段说明

    • success:是否允许该请求转发到 real-server。

    • principal:如果允许转发到 real-server,可以将一些信息传递给 real-server,kv 格式。

      • 如果 real-server 是 HTTP 接口,则 principal 会放到 header 中。

      • 如果 real-server 是 SOFARPC 接口,则 principal 会放到 baggage 中。

    • failResponseHeader:如果不允许转发到 real-server,可以设置响应头返回给 client。

    • failResponseBody:如果不允许转发到 real-server,可以设置响应 body 给 client。

    • failResponseStatus:如果不允许转发到 real-server,可以设置响应 HTTP 状态码。

对应外部授权 API 的定义标准如下:

  • AuthRequest

    publicclassAuthRequest{
    private Map<String,String> context;
    }
  • AuthResponse

    publicclassAuthResponse{
    private boolean             success;
    private Map<String,String>  principal;
    private Map<String,String> failResponseHeader;
    private Object              failResponseBody;
    private int                 failResponseStatus;
    }
说明

AuthRequest 和 AuthResponse 需要按照本文提供的数据结构不能改动,类名可以自定义修改。

public class AuthRequest {
    private Map<String, String> context;

    public Map<String, String> getContext() {
        return context;
    }

    public void setContext(Map<String, String> context) {
        this.context = context;
    }
}
@Data
public class AuthResponse {

    private boolean success;
    private Map<String, String> principal;
    private Map<String, String> failResponseHeader;
    private int failResponseStatus;
    private Object failResponseBody;

    public void setFailResponse(Object failResponse) {
        this.failResponseBody = failResponse;
    }

}
@RestController
@RequestMapping("/api")
public class AuthController {

    @RequestMapping("/auth")
    public AuthResponse auth(@RequestBody AuthRequest request) {
        return AuthUtil.buildAuthRes(request);
    }

}
public class AuthUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthUtil.class);

    public static AuthResponse buildAuthRes(AuthRequest request) {
        AuthResponse response = new ObjectAuthResponse();
        //---以下都是在外部授权配置页面中配置的参数key才能在这边取得到值----
        String headerKey = request.getContext().get("headerKey");
        String path = request.getContext().get("x-mosn-path");
        String method = request.getContext().get("x-mosn-method");
        String bodyKey = request.getContext().get("bodyKey");
        String queryKey = request.getContext().get("queryKey");
        LOGGER.info("headerKey:" + headerKey + ",bodyKey:" + bodyKey + ",queryKey:" + queryKey + ",path:" + path + ",method:" + method);
        //------ end ------
        if ("h".equalsIgnoreCase(headerKey)) {
            // success
            response.setSuccess(true);//外部授权通过
            Map<String, String> principal = new HashMap<>();
            principal.put("param-to-server", "abc");
            principal.put("userName", "user name is tom aaa");
            response.setPrincipal(principal);//需要透传到后端服务的参数
            return response;
        } else {
            // error
            response.setSuccess(false);//外部授权失败
            Map<String, String> failResponseHeader = new HashMap<>();
            queryKey = request.getContext().get("queryKey");
            if ("q".equalsIgnoreCase(queryKey)) {
                failResponseHeader.put("header-to-client", "query");
            } else {
                failResponseHeader.put("header-to-client", "no-query");
            }
            response.setFailResponseHeader(failResponseHeader);//失败的响应头
            Map<String, String> failResponseBody = new HashMap<>();
            String context = JSON.toJSONString(request.getContext());
            failResponseBody.put("test", context);
            response.setFailResponse(failResponseBody);//失败的错误信息
            response.setFailResponseStatus(401);//失败响应码
            LOGGER.info("buildAuthRes|response|{}|{}", (response instanceof ByteAuthResponse), JSON.toJSONString(response));
            return response;
        }
    }
}

阿里云首页 SOFAStack API 统一网关 相关技术圈