fix: remove unfinished web oauth surface

This commit is contained in:
malei 2026-08-02 02:45:22 +08:00
parent 21846dba8b
commit 339c793855
11 changed files with 47 additions and 105 deletions

View File

@ -20,7 +20,6 @@
| `MYSQL_PASSWORD` | **是** | 空 | 数据库密码 |
| `WECHAT_APPID` | 生产必填 | 空 | 微信小程序 AppID |
| `WECHAT_APPSECRET` | 生产必填 | 空 | 微信小程序 AppSecret |
| `WECHAT_REDIRECT_URI` | 生产必填 | `http://localhost:8080/api/wechat/callback` | 微信回调;生产必须为真实 HTTPS 地址 |
| `PETSTORE_SESSION_SECRET` | **生产必填** | `dev-change-me` | HMAC session token 签名密钥;改密会使所有已签发 token 立即失效 |
| `PETSTORE_SESSION_TTL_SECONDS` | 否 | `604800`7 天) | session token 有效期 |
| `APP_BASE_URL` | 生产必填 | `http://localhost:8080` | 后端对外可访问 base URL用于生成媒体绝对 URL |

View File

@ -9,7 +9,6 @@ MYSQL_PASSWORD=
WECHAT_APPID=
WECHAT_APPSECRET=
WECHAT_REDIRECT_URI=https://api.petstore.invalid/api/wechat/callback
PETSTORE_SESSION_SECRET=
SMS_UNIVERSAL_CODE=

View File

@ -18,7 +18,7 @@ require_env() {
}
for name in MYSQL_URL MYSQL_USERNAME MYSQL_PASSWORD WECHAT_APPID WECHAT_APPSECRET \
WECHAT_REDIRECT_URI PETSTORE_SESSION_SECRET APP_BASE_URL UPLOAD_PATH CORS_ALLOWED_ORIGINS; do
PETSTORE_SESSION_SECRET APP_BASE_URL UPLOAD_PATH CORS_ALLOWED_ORIGINS; do
require_env "$name"
done
@ -27,11 +27,6 @@ done
[[ "$APP_BASE_URL" != *.invalid* ]] || fail "APP_BASE_URL must not use a placeholder domain"
[[ "$APP_BASE_URL" != *.example* && "$APP_BASE_URL" != *.test* ]] \
|| fail "APP_BASE_URL must not use a reserved example/test domain"
[[ "$WECHAT_REDIRECT_URI" == https://* ]] || fail "WECHAT_REDIRECT_URI must use https"
[[ "$WECHAT_REDIRECT_URI" != *localhost* && "$WECHAT_REDIRECT_URI" != *.invalid* ]] \
|| fail "WECHAT_REDIRECT_URI must use a production domain"
[[ "$WECHAT_REDIRECT_URI" != *.example* && "$WECHAT_REDIRECT_URI" != *.test* ]] \
|| fail "WECHAT_REDIRECT_URI must not use a reserved example/test domain"
[[ ${#PETSTORE_SESSION_SECRET} -ge 32 ]] || fail "PETSTORE_SESSION_SECRET must be at least 32 characters"
[[ "$PETSTORE_SESSION_SECRET" != "dev-change-me" ]] || fail "PETSTORE_SESSION_SECRET must not use the default"
[[ -z "${SMS_UNIVERSAL_CODE:-}" ]] || fail "SMS_UNIVERSAL_CODE must be empty in production"

View File

@ -61,10 +61,6 @@ public class AuthInterceptor implements HandlerInterceptor {
"POST /api/report/*/testimonial",
"POST /api/report/unsubscribe",
"POST /api/report/open-track",
// 微信网页授权 OAuth 回调链 wechat.redirect_uri 配置一致callback 当前为 TODO demo
// 真实微信登录走 /api/user/wx-phone-login此处放行仅为兼容 redirect_uri 配置
"GET /api/wechat/authorize",
"GET /api/wechat/callback",
"GET /api/upload/image/**",
"GET /api/upload/legacy/**",
"GET /uploads/**"

View File

@ -20,7 +20,6 @@ public class ProductionConfigurationValidator {
private final String mysqlPassword;
private final String wechatAppId;
private final String wechatAppSecret;
private final String wechatRedirectUri;
private final String sessionSecret;
private final String smsUniversalCode;
private final String appBaseUrl;
@ -34,7 +33,6 @@ public class ProductionConfigurationValidator {
@Value("${spring.datasource.password:}") String mysqlPassword,
@Value("${wechat.appid:}") String wechatAppId,
@Value("${wechat.appsecret:}") String wechatAppSecret,
@Value("${wechat.redirect_uri:}") String wechatRedirectUri,
@Value("${auth.session-secret:}") String sessionSecret,
@Value("${app.demo.sms-universal-code:}") String smsUniversalCode,
@Value("${app.base-url:}") String appBaseUrl,
@ -46,7 +44,6 @@ public class ProductionConfigurationValidator {
this.mysqlPassword = mysqlPassword;
this.wechatAppId = wechatAppId;
this.wechatAppSecret = wechatAppSecret;
this.wechatRedirectUri = wechatRedirectUri;
this.sessionSecret = sessionSecret;
this.smsUniversalCode = smsUniversalCode;
this.appBaseUrl = appBaseUrl;
@ -70,7 +67,6 @@ public class ProductionConfigurationValidator {
require(mysqlPassword, "MYSQL_PASSWORD", failures);
require(wechatAppId, "WECHAT_APPID", failures);
require(wechatAppSecret, "WECHAT_APPSECRET", failures);
requireHttpsUrl(wechatRedirectUri, "WECHAT_REDIRECT_URI", failures);
if (text(sessionSecret).length() < 32 || "dev-change-me".equals(sessionSecret)) {
failures.add("PETSTORE_SESSION_SECRET(至少32字符且非默认值)");
}
@ -97,13 +93,6 @@ public class ProductionConfigurationValidator {
if (text(value).isEmpty()) failures.add(name);
}
private static void requireHttpsUrl(String value, String name, List<String> failures) {
String normalized = text(value);
if (!isSafeHttpsUrl(normalized)) {
failures.add(name + "必须为生产https地址");
}
}
private static void requireHttpsOrigin(String value, String name, List<String> failures) {
if (!isSafeHttpsOrigin(text(value))) {
failures.add(name + "必须为生产https源");
@ -124,14 +113,6 @@ public class ProductionConfigurationValidator {
}
}
private static boolean isSafeHttpsUrl(String value) {
try {
return isSafeHttpsUri(URI.create(value));
} catch (IllegalArgumentException ignored) {
return false;
}
}
private static boolean isSafeHttpsUri(URI uri) {
String host = text(uri.getHost()).toLowerCase();
return "https".equalsIgnoreCase(uri.getScheme())

View File

@ -3,31 +3,20 @@ package com.petstore.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/** 微信小程序服务端凭据;不承载已下线的网页 OAuth redirect 配置。 */
@Configuration
public class WechatConfig {
// TODO: 替换为实际的微信开放平台 AppID AppSecret
@Value("${wechat.appid:YOUR_APPID}")
@Value("${wechat.appid:}")
private String appid;
@Value("${wechat.appsecret:YOUR_APPSECRET}")
@Value("${wechat.appsecret:}")
private String appsecret;
@Value("${wechat.redirect_uri:http://localhost:8080/api/wechat/callback}")
private String redirectUri;
public String getAppid() {
return appid;
}
public String getAppid() { return appid; }
public String getAppsecret() { return appsecret; }
public String getRedirectUri() { return redirectUri; }
/**
* 获取微信授权跳转地址
*/
public String getAuthorizeUrl() {
return "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=" + appid +
"&redirect_uri=" + redirectUri +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=petstore#wechat_redirect";
public String getAppsecret() {
return appsecret;
}
}

View File

@ -1,49 +0,0 @@
package com.petstore.controller;
import com.petstore.config.WechatConfig;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/wechat")
@RequiredArgsConstructor
@CrossOrigin
public class WechatController {
private final WechatConfig wechatConfig;
/**
* 获取微信授权跳转地址
*/
@GetMapping("/authorize")
public Map<String, Object> getAuthorizeUrl() {
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("data", wechatConfig.getAuthorizeUrl());
return result;
}
/**
* 微信授权回调
* 通过 code 换取 access_token返回用户信息
*/
@GetMapping("/callback")
public Map<String, Object> callback(@RequestParam String code, @RequestParam String state) {
Map<String, Object> result = new HashMap<>();
// TODO: 通过 code 调用微信接口换取 openid access_token
// https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
// 演示用直接返回成功
result.put("code", 200);
result.put("message", "微信授权成功");
result.put("data", Map.of(
"openid", "demo_openid_" + code,
"nickname", "微信用户",
"avatar", ""
));
return result;
}
}

View File

@ -43,7 +43,6 @@ logging:
wechat:
appid: ${WECHAT_APPID:}
appsecret: ${WECHAT_APPSECRET:}
redirect_uri: ${WECHAT_REDIRECT_URI:http://localhost:8080/api/wechat/callback}
# 服务回顾短片ffmpeg可选 ffprobe 读时长/音轨)
app:

View File

@ -71,7 +71,6 @@ app:
wechat:
appid: ${WECHAT_APPID:}
appsecret: ${WECHAT_APPSECRET:}
redirect_uri: ${WECHAT_REDIRECT_URI:http://localhost:8080/api/wechat/callback}
# 最小鉴权上下文HMAC session token不引入完整 Spring Security
# 生产必须通过环境变量覆盖 PETSTORE_SESSION_SECRET勿提交真实密钥。

View File

@ -0,0 +1,35 @@
package com.petstore.config;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse;
class LegacyWechatOauthRemovalContractTest {
@Test
void unimplementedWebOauthSurfaceDoesNotReturnToThePilotRelease() throws Exception {
assertFalse(Files.exists(Path.of(
"src/main/java/com/petstore/controller/WechatController.java"
)));
for (Path path : List.of(
Path.of("src/main/java/com/petstore/auth/AuthInterceptor.java"),
Path.of("src/main/java/com/petstore/config/WechatConfig.java"),
Path.of("src/main/resources/application.yml"),
Path.of("src/main/resources/application-example.yml"),
Path.of("deploy/release-preflight.sh"),
Path.of("deploy/petstore-backend.env.example"),
Path.of("README.md")
)) {
String content = Files.readString(path);
assertFalse(content.contains("/api/wechat/authorize"), path.toString());
assertFalse(content.contains("/api/wechat/callback"), path.toString());
assertFalse(content.contains("WECHAT_REDIRECT_URI"), path.toString());
assertFalse(content.contains("demo_openid_"), path.toString());
}
}
}

View File

@ -61,7 +61,6 @@ class ProductionConfigurationValidatorTest {
"database-secret",
"wx-app-id",
"wx-app-secret",
"https://api.chongxiaota.cn/api/wechat/callback",
"01234567890123456789012345678901",
smsCode,
"https://api.chongxiaota.cn",