diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackController.java index 8786607..e0c7dfb 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackController.java @@ -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 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 callbackEndpointInfoPage() { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.TEXT_HTML); - String html = "" - + "金山文档授权回调" - + "" - + "

金山文档授权回调

" - + "

此地址用于 OAuth 授权完成后的跳转,请勿直接收藏或打开。

" - + "

请在系统中点击「连接金山文档」或「授权」后,由金山文档页面自动跳转到此处。

" - + ""; - return new ResponseEntity<>(html, headers, HttpStatus.OK); + return KdocsCallbackProbeResponses.callbackReadyPage(); } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackProbeResponses.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackProbeResponses.java new file mode 100644 index 0000000..1a9c3f6 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackProbeResponses.java @@ -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 = "" + + "金山文档授权回调" + + "" + + "

金山文档授权回调

" + + "

此地址用于 OAuth 授权完成后的跳转,请勿直接收藏或打开。

" + + "

请在系统中点击「连接金山文档」或「授权」后,由金山文档页面自动跳转到此处。

" + + ""; + + public static ResponseEntity callbackReadyPage() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(HTML_UTF8); + return new ResponseEntity<>(BODY, headers, HttpStatus.OK); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackUrlBuilder.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackUrlBuilder.java new file mode 100644 index 0000000..b52b396 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/KdocsCallbackUrlBuilder.java @@ -0,0 +1,37 @@ +package com.ruoyi.web.controller.jarvis; + +import com.ruoyi.common.utils.StringUtils; + +import javax.servlet.http.HttpServletRequest; + +/** + * 反向代理后拼浏览器可访问的绝对 URL(OAuth 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(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/Wps365ToKdocsCallbackRedirectController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/Wps365ToKdocsCallbackRedirectController.java index fa59760..c610418 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/Wps365ToKdocsCallbackRedirectController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/Wps365ToKdocsCallbackRedirectController.java @@ -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 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); } }