This commit is contained in:
van
2026-03-23 17:29:31 +08:00
parent 184a53005d
commit ef286d3bd2
4 changed files with 107 additions and 15 deletions

View File

@@ -37,6 +37,18 @@ public class KdocsCallbackController extends BaseController {
return handleOAuthCallback(code, state, error, errorDescription);
}
/**
* 部分开放平台校验可能使用 POST。
*/
@Anonymous
@PostMapping
public ResponseEntity<?> oauthCallbackPost(@RequestParam(value = "code", required = false) String code,
@RequestParam(value = "state", required = false) String state,
@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "error_description", required = false) String errorDescription) {
return handleOAuthCallback(code, state, error, errorDescription);
}
private ResponseEntity<?> handleOAuthCallback(String code, String state, String error, String errorDescription) {
try {
if (error != null) {
@@ -64,7 +76,7 @@ public class KdocsCallbackController extends BaseController {
private ResponseEntity<String> htmlPage(boolean success, String message, KdocsTokenInfo tokenInfo) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
headers.setContentType(MediaType.parseMediaType("text/html;charset=UTF-8"));
String esc = message.replace("\\", "\\\\").replace("'", "\\'").replace("\n", "\\n").replace("\r", "\\r");
String uid = tokenInfo != null && tokenInfo.getUserId() != null ? tokenInfo.getUserId().replace("\\", "\\\\").replace("'", "\\'") : "";
StringBuilder html = new StringBuilder();
@@ -89,15 +101,6 @@ public class KdocsCallbackController extends BaseController {
* 无授权参数时的占位页HTTP 200避免被误判为「回调不可用」也不向 opener 误发失败消息。
*/
private ResponseEntity<String> callbackEndpointInfoPage() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
String html = "<!DOCTYPE html><html lang='zh-CN'><head><meta charset='UTF-8'><meta name='robots' content='noindex'>"
+ "<title>金山文档授权回调</title></head>"
+ "<body style='font-family:sans-serif;text-align:center;padding:40px;color:#333'>"
+ "<h2>金山文档授权回调</h2>"
+ "<p>此地址用于 OAuth 授权完成后的跳转,请勿直接收藏或打开。</p>"
+ "<p>请在系统中点击「连接金山文档」或「授权」后,由金山文档页面自动跳转到此处。</p>"
+ "</body></html>";
return new ResponseEntity<>(html, headers, HttpStatus.OK);
return KdocsCallbackProbeResponses.callbackReadyPage();
}
}

View File

@@ -0,0 +1,31 @@
package com.ruoyi.web.controller.jarvis;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
/**
* 开放平台校验回调 URL 时多为 GET、无 code需直接 200勿对校验请求返回 302。
*/
public final class KdocsCallbackProbeResponses {
private KdocsCallbackProbeResponses() {
}
private static final MediaType HTML_UTF8 = MediaType.parseMediaType("text/html;charset=UTF-8");
private static final String BODY = "<!DOCTYPE html><html lang='zh-CN'><head><meta charset='UTF-8'><meta name='robots' content='noindex'>"
+ "<title>金山文档授权回调</title></head>"
+ "<body style='font-family:sans-serif;text-align:center;padding:40px;color:#333'>"
+ "<h2>金山文档授权回调</h2>"
+ "<p>此地址用于 OAuth 授权完成后的跳转,请勿直接收藏或打开。</p>"
+ "<p>请在系统中点击「连接金山文档」或「授权」后,由金山文档页面自动跳转到此处。</p>"
+ "</body></html>";
public static ResponseEntity<String> callbackReadyPage() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(HTML_UTF8);
return new ResponseEntity<>(BODY, headers, HttpStatus.OK);
}
}

View File

@@ -0,0 +1,37 @@
package com.ruoyi.web.controller.jarvis;
import com.ruoyi.common.utils.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* 反向代理后拼浏览器可访问的绝对 URLOAuth 302 用)。
*/
public final class KdocsCallbackUrlBuilder {
private KdocsCallbackUrlBuilder() {
}
public static String absoluteKdocsCallback(HttpServletRequest request, String queryString) {
String scheme = request.getHeader("X-Forwarded-Proto");
if (StringUtils.isBlank(scheme)) {
scheme = request.getScheme();
} else if (scheme.contains(",")) {
scheme = scheme.substring(0, scheme.indexOf(',')).trim();
}
String host = request.getHeader("Host");
if (StringUtils.isBlank(host)) {
int port = request.getServerPort();
host = request.getServerName();
if (port != 80 && port != 443) {
host = host + ":" + port;
}
}
StringBuilder sb = new StringBuilder();
sb.append(scheme).append("://").append(host).append("/kdocs-callback");
if (StringUtils.isNotBlank(queryString)) {
sb.append('?').append(queryString);
}
return sb.toString();
}
}

View File

@@ -6,13 +6,15 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
/**
* 迁移金山文档后,开放平台若仍登记旧回调 /wps365-callback,则 302 到新路径并保留 query含 code
* 旧回调 /wps365-callback平台校验多为「GET、无 code」须直接 200真实授权带 code/error 时再 302 到 /kdocs-callback
*/
@Anonymous
@RestController
@@ -20,11 +22,30 @@ public class Wps365ToKdocsCallbackRedirectController {
@Anonymous
@GetMapping("/wps365-callback")
public ResponseEntity<Void> redirectToKdocs(HttpServletRequest request) {
public ResponseEntity<?> wps365Get(HttpServletRequest request,
@RequestParam(value = "code", required = false) String code,
@RequestParam(value = "error", required = false) String error) {
return handleWps365(request, code, error);
}
/**
* 部分校验或代理可能使用 POST。
*/
@Anonymous
@PostMapping("/wps365-callback")
public ResponseEntity<?> wps365Post(HttpServletRequest request,
@RequestParam(value = "code", required = false) String code,
@RequestParam(value = "error", required = false) String error) {
return handleWps365(request, code, error);
}
private ResponseEntity<?> handleWps365(HttpServletRequest request, String code, String error) {
if (StringUtils.isBlank(code) && StringUtils.isBlank(error)) {
return KdocsCallbackProbeResponses.callbackReadyPage();
}
String q = request.getQueryString();
String path = "/kdocs-callback" + (StringUtils.isNotBlank(q) ? "?" + q : "");
HttpHeaders headers = new HttpHeaders();
headers.setLocation(URI.create(path));
headers.setLocation(URI.create(KdocsCallbackUrlBuilder.absoluteKdocsCallback(request, q)));
return new ResponseEntity<>(null, headers, HttpStatus.FOUND);
}
}