外部授權(quán)
API 網(wǎng)關(guān)支持外部授權(quán)功能。通過該功能,用戶可以在網(wǎng)關(guān)將請求轉(zhuǎn)發(fā)給 real-server 之前,插入一個自定義的遠程接口。網(wǎng)關(guān)會先將請求轉(zhuǎn)發(fā)給這個自定義接口,該接口繼而選擇是否允許這個請求正常轉(zhuǎn)發(fā)到 real-server。
外部授權(quán)服務(wù)編寫須知
您需要在本地開發(fā)一個外部授權(quán)接口。當(dāng) API 需要驗證授權(quán)關(guān)系時,會調(diào)用該外部授權(quán)接口進行授權(quán)校驗。外部授權(quán)接口不限制協(xié)議類型,但會限制請求體和響應(yīng)體。請求和響應(yīng)均為 JSON 字符串,格式如下:
請求體
{ "context":{ "key":"value" } }
字段說明:Request 中只有一個 context 字段,格式為 kv。這些參數(shù)來自于 client 的 request,需要在 API 網(wǎng)關(guān)控制臺平臺進行配置。
響應(yīng)體
{ "success":true/false, "principal":{ "key":"value" }, "failResponseHeader":{ "key":"value" }, "failResponseBody": jsonarray/jsonobject "failResponseStatus": ${httpcode} }
字段說明:
success
:是否允許該請求轉(zhuǎn)發(fā)到 real-server。principal
:如果允許轉(zhuǎn)發(fā)到 real-server,可以將一些信息傳遞給 real-server,kv 格式。如果 real-server 是 HTTP 接口,則 principal 會放到 header 中。
如果 real-server 是 SOFARPC 接口,則 principal 會放到 baggage 中。
failResponseHeader
:如果不允許轉(zhuǎn)發(fā)到 real-server,可以設(shè)置響應(yīng)頭返回給 client。failResponseBody
:如果不允許轉(zhuǎn)發(fā)到 real-server,可以設(shè)置響應(yīng) body 給 client。failResponseStatus
:如果不允許轉(zhuǎn)發(fā)到 real-server,可以設(shè)置響應(yīng) HTTP 狀態(tài)碼。
對應(yīng)外部授權(quán) API 的定義標(biāo)準(zhǔn)如下:
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 需要按照本文提供的數(shù)據(jù)結(jié)構(gòu)不能改動,類名可以自定義修改。
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();
//---以下都是在外部授權(quán)配置頁面中配置的參數(shù)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);//外部授權(quán)通過
Map<String, String> principal = new HashMap<>();
principal.put("param-to-server", "abc");
principal.put("userName", "user name is tom aaa");
response.setPrincipal(principal);//需要透傳到后端服務(wù)的參數(shù)
return response;
} else {
// error
response.setSuccess(false);//外部授權(quán)失敗
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);//失敗的響應(yīng)頭
Map<String, String> failResponseBody = new HashMap<>();
String context = JSON.toJSONString(request.getContext());
failResponseBody.put("test", context);
response.setFailResponse(failResponseBody);//失敗的錯誤信息
response.setFailResponseStatus(401);//失敗響應(yīng)碼
LOGGER.info("buildAuthRes|response|{}|{}", (response instanceof ByteAuthResponse), JSON.toJSONString(response));
return response;
}
}
}