|
|
@@ -44,7 +44,7 @@ import java.util.concurrent.TimeUnit;
|
|
|
* <p><b>流程</b>:LINE → {@code GET /auth/line/callback?code=...&state=...}
|
|
|
* → {@link OAuthVerifyService#verify verify("line", code)}(code 换 token 取 userId)
|
|
|
* → 查 info_user_oauth:已绑定→签 JWT token;未绑定→生成 tempKey(needPhone)
|
|
|
- * → 302 跳到 {@code oauth.line.app-redirect}(如 com.twanmsdyh.app://oauthLogin)带结果:
|
|
|
+ * → 返回 HTML 引导拉起 {@code oauth.line.app-redirect}(如 com.twanmsdyh.app://oauthLogin)带结果:
|
|
|
* <ul>
|
|
|
* <li>已绑定:{@code <app-redirect>?token=xxx}</li>
|
|
|
* <li>未绑定:{@code <app-redirect>?needPhone=1&tempKey=xxx}(App 弹手机号+短信码 UI 后调 /infouser/user/oauthBindPhone)</li>
|
|
|
@@ -81,7 +81,7 @@ public class LineCallbackController extends BaseController {
|
|
|
private String appRedirect;
|
|
|
|
|
|
/**
|
|
|
- * LINE 服务端回调:接 code → 换 token → 登录 → 302 回 App。
|
|
|
+ * LINE 服务端回调:接 code → 换 token → 登录 → 返回 HTML 拉起 App。
|
|
|
* 用 @Anonymous 放行(LINE 以浏览器/App 身份回调,无 token)。
|
|
|
*/
|
|
|
@Anonymous
|
|
|
@@ -114,7 +114,7 @@ public class LineCallbackController extends BaseController {
|
|
|
}
|
|
|
String token = buildOauthToken(u, "line");
|
|
|
log.info("[OAuth][LINE] callback 已绑定登录成功 userId={}", u.getUserId());
|
|
|
- response.sendRedirect(appRedirect + "?token=" + enc(token));
|
|
|
+ respondAppRedirect(response, appRedirect + "?token=" + enc(token));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -122,7 +122,7 @@ public class LineCallbackController extends BaseController {
|
|
|
String tempKey = UUID.randomUUID().toString().replace("-", "");
|
|
|
redisCache.setCacheObject(OAUTH_TEMP_PREFIX + tempKey, "line@" + providerUid, 5, TimeUnit.MINUTES);
|
|
|
log.info("[OAuth][LINE] callback 未绑定,返回 needPhone providerUid={}", providerUid);
|
|
|
- response.sendRedirect(appRedirect + "?needPhone=1&tempKey=" + enc(tempKey));
|
|
|
+ respondAppRedirect(response, appRedirect + "?needPhone=1&tempKey=" + enc(tempKey));
|
|
|
} catch (Exception e) {
|
|
|
log.error("[OAuth][LINE] callback 异常", e);
|
|
|
redirectToApp(response, "error", e.getMessage() == null ? "fail" : e.getMessage());
|
|
|
@@ -154,9 +154,34 @@ public class LineCallbackController extends BaseController {
|
|
|
userDto.setLoginTime(System.currentTimeMillis());
|
|
|
}
|
|
|
|
|
|
- /** 302 跳回 App:app-redirect + ?<key>=<value>(value 做 URL 编码)。 */
|
|
|
+ /** 回 App:app-redirect + ?<key>=<value>(value 做 URL 编码)。 */
|
|
|
private void redirectToApp(HttpServletResponse response, String key, String value) throws IOException {
|
|
|
- response.sendRedirect(appRedirect + "?" + key + "=" + enc(value));
|
|
|
+ respondAppRedirect(response, appRedirect + "?" + key + "=" + enc(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 返回 HTML 引导拉起 App(替代裸 302)。
|
|
|
+ * <p>原因:浏览器 / LINE 内置 webview 常拦截「自动 302 跳自定义 scheme」(防劫持),
|
|
|
+ * 且 App 热启动时收不到 scheme 参数。改为返回 HTML:meta refresh + JS 自动尝试 + 可见的
|
|
|
+ * 「点击打开 App」按钮(用户手势触发 scheme 导航最可靠,冷/热启动都能把参数送达 App)。
|
|
|
+ * <p>schemeUrl 内 value 均已 URL 编码,不含引号/尖括号,可安全嵌入单引号属性与 JS 串。
|
|
|
+ */
|
|
|
+ private void respondAppRedirect(HttpServletResponse response, String schemeUrl) throws IOException {
|
|
|
+ String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'>"
|
|
|
+ + "<meta name='viewport' content='width=device-width,initial-scale=1,user-scalable=no'>"
|
|
|
+ + "<meta http-equiv='refresh' content='0;url=" + schemeUrl + "'>"
|
|
|
+ + "<title>打開 App</title></head>"
|
|
|
+ + "<body style='margin:0;font-family:-apple-system,sans-serif;text-align:center'>"
|
|
|
+ + "<div style='padding:56px 20px'>"
|
|
|
+ + "<p style='font-size:18px;color:#333;margin:0 0 28px'>正在開啟 App…</p>"
|
|
|
+ + "<a href='" + schemeUrl + "' style='display:inline-block;padding:14px 36px;"
|
|
|
+ + "background:#06C755;color:#fff;border-radius:28px;text-decoration:none;font-size:16px'>點擊開啟 App</a>"
|
|
|
+ + "<p style='margin:28px 0 0;color:#999;font-size:13px'>若未自動跳轉,請點上方按鈕</p>"
|
|
|
+ + "</div>"
|
|
|
+ + "<script>try{location.href='" + schemeUrl + "';}catch(e){}</script>"
|
|
|
+ + "</body></html>";
|
|
|
+ response.setContentType("text/html;charset=UTF-8");
|
|
|
+ response.getWriter().write(html);
|
|
|
}
|
|
|
|
|
|
private static String enc(String s) {
|