-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #55 from xezzon/feature/issue-54
开放平台转发请求 Close #54
- Loading branch information
Showing
23 changed files
with
529 additions
and
293 deletions.
There are no files selected for viewing
119 changes: 104 additions & 15 deletions
119
...-service-open/src/main/java/io/github/xezzon/zeroweb/call/SubscriptionCallController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,132 @@ | ||
package io.github.xezzon.zeroweb.call; | ||
|
||
import static com.google.auth.http.AuthHttpConstants.BEARER; | ||
import static org.springframework.web.bind.annotation.RequestMethod.*; | ||
|
||
import com.auth0.jwt.JWT; | ||
import io.github.xezzon.zeroweb.ZerowebOpenConstant; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import io.github.xezzon.zeroweb.auth.JwtFilter; | ||
import io.github.xezzon.zeroweb.subscription.domain.Subscription; | ||
import io.github.xezzon.zeroweb.subscription.service.ISubscriptionService4Call; | ||
import io.github.xezzon.zeroweb.third_party_app.service.IThirdPartyAppService4Call; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import java.time.Instant; | ||
import java.util.Map; | ||
import java.util.Map.Entry; | ||
import java.util.stream.Collectors; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.http.HttpStatusCode; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestHeader; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.client.RestClient; | ||
|
||
/** | ||
* 订阅服务调用记录 | ||
* @author xezzon | ||
*/ | ||
@RestController | ||
@RequestMapping("/subscription-call") | ||
@RequestMapping("/call") | ||
public class SubscriptionCallController { | ||
|
||
private final SubscriptionCallService subscriptionCallService; | ||
private final ISubscriptionService4Call subscriptionService; | ||
private final IThirdPartyAppService4Call thirdPartyAppService; | ||
|
||
public SubscriptionCallController(SubscriptionCallService subscriptionCallService) { | ||
this.subscriptionCallService = subscriptionCallService; | ||
public SubscriptionCallController( | ||
ISubscriptionService4Call subscriptionService, | ||
IThirdPartyAppService4Call thirdPartyAppService | ||
) { | ||
this.subscriptionService = subscriptionService; | ||
this.thirdPartyAppService = thirdPartyAppService; | ||
} | ||
|
||
/** | ||
* 转发 GET 请求 | ||
*/ | ||
@GetMapping(value = "/{openapiCode}") | ||
public ResponseEntity<byte[]> forwardForSafe( | ||
@PathVariable String openapiCode, | ||
@RequestBody(required = false) byte[] body, | ||
@RequestHeader(ZerowebOpenConstant.ACCESS_KEY_HEADER) String accessKey, | ||
@RequestHeader(ZerowebOpenConstant.TIMESTAMP_HEADER) Instant timestamp, | ||
@RequestHeader(ZerowebOpenConstant.SIGNATURE_HEADER) String signature, | ||
@RequestHeader HttpHeaders headers, | ||
HttpServletRequest request | ||
) { | ||
Map<String, String[]> parameterMap = request.getParameterMap(); | ||
return forward(openapiCode, body, accessKey, timestamp, signature, headers, parameterMap); | ||
} | ||
|
||
@PostMapping("/validate") | ||
public void validateCall( | ||
@RequestBody byte[] body, | ||
@RequestParam("path") String path, | ||
/** | ||
* 转发非 GET 请求 | ||
*/ | ||
@RequestMapping(value = "/{openapiCode}", method = {POST, PUT, DELETE, PATCH}) | ||
public ResponseEntity<byte[]> forwardForUnsafe( | ||
@PathVariable String openapiCode, | ||
@RequestBody(required = false) byte[] body, | ||
@RequestHeader(ZerowebOpenConstant.ACCESS_KEY_HEADER) String accessKey, | ||
@RequestHeader(ZerowebOpenConstant.TIMESTAMP_HEADER) Instant timestamp, | ||
@RequestHeader(ZerowebOpenConstant.SIGNATURE_HEADER) String signature, | ||
HttpServletResponse response | ||
@RequestHeader HttpHeaders headers, | ||
HttpServletRequest request | ||
) { | ||
Map<String, String[]> parameterMap = request.getParameterMap(); | ||
return forward(openapiCode, body, accessKey, timestamp, signature, headers, parameterMap); | ||
} | ||
|
||
/** | ||
* 转发请求 | ||
* @param openapiCode 对外路径 | ||
* @param body 请求体 | ||
* @param originalHeaders 请求头 | ||
* @param accessKey AccessKey(请求头) | ||
* @param timestamp 时间戳(请求头) | ||
* @param signature 签名(请求头) | ||
* @param parameterMap 原始请求参数 | ||
* @return 响应体 | ||
*/ | ||
private ResponseEntity<byte[]> forward( | ||
String openapiCode, byte[] body, String accessKey, Instant timestamp, String signature, | ||
HttpHeaders originalHeaders, Map<String, String[]> parameterMap | ||
) { | ||
/* 所有校验通过后发放令牌 */ | ||
String jwt = subscriptionCallService.signJwt(accessKey, path, body, signature, timestamp); | ||
response.setHeader(HttpHeaders.AUTHORIZATION, BEARER + " " + jwt); | ||
/* 签发JWT */ | ||
String jwt = thirdPartyAppService.signJwt(accessKey, body, signature, timestamp); | ||
/* 获取相应的后端地址 */ | ||
String appId = JWT.decode(jwt).getSubject(); | ||
Subscription subscription = subscriptionService.getSubscription(appId, openapiCode); | ||
/* 转发请求 */ | ||
originalHeaders.remove(ZerowebOpenConstant.TIMESTAMP_HEADER.toLowerCase()); | ||
originalHeaders.remove(ZerowebOpenConstant.SIGNATURE_HEADER.toLowerCase()); | ||
originalHeaders.remove(JwtFilter.PUBLIC_KEY_HEADER.toLowerCase()); | ||
return RestClient.builder() | ||
.defaultStatusHandler(HttpStatusCode::isError, (req, resp) -> { | ||
}) | ||
.build() | ||
// 请求方法由对外接口定义 | ||
.method(HttpMethod.valueOf(subscription.getOpenapi().getHttpMethod().getCode())) | ||
// 请求路径由目标地址定义 | ||
.uri(subscription.getOpenapi().getDestination(), uri -> { | ||
// 请求参数与路径参数都由原始请求的请求参数提供 | ||
parameterMap.forEach(uri::queryParam); | ||
Map<String, String> pathVariableMap = parameterMap.entrySet().parallelStream() | ||
.collect(Collectors.toMap( | ||
Entry::getKey, | ||
entry -> String.join(",", entry.getValue()) | ||
)); | ||
return uri.build(pathVariableMap); | ||
}) | ||
// 请求头由原始请求的请求头提供,但需要移除签名和时间戳 | ||
.headers(headers -> headers.addAll(originalHeaders)) | ||
// 认证头由本系统签发的JWT提供 | ||
.header(HttpHeaders.AUTHORIZATION, BEARER + " " + jwt) | ||
// 请求体由原始请求的请求体提供 | ||
.body(body) | ||
.retrieve() | ||
.toEntity(byte[].class); | ||
} | ||
} |
81 changes: 0 additions & 81 deletions
81
...web-service-open/src/main/java/io/github/xezzon/zeroweb/call/SubscriptionCallService.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
...eroweb-service-open/src/main/java/io/github/xezzon/zeroweb/openapi/domain/HttpMethod.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package io.github.xezzon.zeroweb.openapi.domain; | ||
|
||
import io.github.xezzon.tao.dict.IDict; | ||
|
||
/** | ||
* 开放平台允许使用的HTTP方法 | ||
*/ | ||
public enum HttpMethod implements IDict { | ||
|
||
GET, | ||
POST, | ||
PUT, | ||
DELETE, | ||
PATCH, | ||
; | ||
|
||
@Override | ||
public String getTag() { | ||
return this.getClass().getSimpleName(); | ||
} | ||
|
||
@Override | ||
public String getCode() { | ||
return this.name(); | ||
} | ||
|
||
@Override | ||
public String getLabel() { | ||
return this.name(); | ||
} | ||
|
||
@Override | ||
public int getOrdinal() { | ||
return this.ordinal(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.