外部访问地址※
http://127.0.0.1:8081/user-center/#/?access_token=5bbafc23ae47465aaab8b5461c17f211
内部代理后的地址※
http://127.0.0.1:9909/user-center/oauth2/login?token=5bbafc23ae47465aaab8b5461c17f211
Nginx 和任何其他后端服务器一样,是绝对获取不到 URL 中 # (哈希/片段) 后面的内容的。
这是由 Web 标准决定的:# 后面的内容是专门给浏览器使用的,用于页面内部的锚点定位或在单页面应用(SPA)中进行路由,它根本不会被浏览器发送到服务器。
因此,您无法直接用一条 Nginx proxy_pass 或 rewrite 规则来实现这个转换。
正确的解决方案:客户端重定向※
要解决这个问题,我们需要“欺骗”一下浏览器,让它自己把 access_token 从 # 后面挪到 ? 后面,再重新向 Nginx 发起一次请求。这通常通过返回一个包含一小段 JavaScript 的中间页面来实现。
整个流程如下:
- 第一次请求:浏览器访问
http://127.0.0.1:8081/user-center/#/?access_token=...。 - Nginx 响应:Nginx 接收到对
/user-center/的请求(注意,没有#后面的部分)。您的配置应该让 Nginx 返回一个特殊的redirect.html页面。 - 浏览器执行JS:浏览器加载
redirect.html,页面里的 JavaScript 会读取地址栏中完整的 URL,从#后面提取出access_token的值。 - 浏览器重定向:JavaScript 构建一个新的 URL
http://127.0.0.1:8081/user-center/oauth2/login?token=...,并命令浏览器跳转到这个新地址。 - 第二次请求:浏览器向 Nginx 发起第二次请求。这次的 URL 是 Nginx 可以完全理解的,因为它包含了标准的查询参数
?token=...。 - Nginx 代理:Nginx 接收到这个新请求,并根据配置将其完美地代理到后端的
http://127.0.0.1:9909/user-center/oauth2/login?token=...。
Nginx配置※
server {
listen 8081;
server_name 127.0.0.1;
# 这里不再需要全局的 root 指令
# 规则一:处理 SSO 登录重定向的静态 HTML 文件
location = /user-center/ {
# root 指令只对这个 location 生效
root /var/www/sso-redirect; # <-- 指定 redirect.html 所在的目录
try_files /redirect.html =404;
}
location = /user-center {
root /var/www/sso-redirect;
try_files /redirect.html =404;
}
# 规则二:处理登录请求的反向代理
# 这个 location 负责代理,不需要 root 指令
location /user-center/oauth2/login {
proxy_pass http://127.0.0.1:9909; # 注意这里proxy_pass后面没有 /
# proxy_pass http://...; 会将完整的URI (/user-center/oauth2/login) 传递给后端
# 所以后端收到的请求是 http://127.0.0.1:9909/user-center/oauth2/login
# 这正好符合您后端Java代码的 @RequestMapping
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 规则三:为您的另一个应用(博客)提供服务
# 比如,访问 http://127.0.0.1:8081/blog/
location /blog/ {
# 这个 location 使用一个完全不同的根目录
root /var/www/my-blog;
index index.html index.htm;
try_files $uri $uri/ /blog/index.html;
}
# 规则四:为通用静态资源(如图片、CSS)提供服务
# 同样可以有独立的 root
location ~ \.(css|js|png|jpg|gif)$ {
root /var/www/common-assets;
expires 30d; # 可以顺便加上缓存策略
}
}
redirect.html※
<!DOCTYPE html>
<html>
<head>
<title>Redirecting...</title>
<script type="text/javascript">
try {
// 1. 获取URL中 # 后面的内容,例如 "#/?access_token=xyz"
var hash = window.location.hash.substring(1);
// 2. 将其解析为 URLSearchParams 对象,方便获取参数
// hash 可能是 /?access_token=xyz 或 ?access_token=xyz,我们去掉开头的 /
if (hash.startsWith('/')) {
hash = hash.substring(1);
}
var params = new URLSearchParams(hash);
// 3. 获取 access_token 的值
var accessToken = params.get('access_token');
if (accessToken) {
// 4. 构建新的目标 URL
var newUrl = '/user-center/oauth2/login?token=' + encodeURIComponent(accessToken);
// 5. 使用 replace 跳转,这样浏览器的历史记录中不会留下这个中间页
window.location.replace(newUrl);
} else {
// 如果没有 access_token,可以显示错误信息
document.write('Error: access_token not found.');
}
} catch (e) {
document.write('An error occurred during redirection.');
console.error(e);
}
</script>
</head>
<body>
<p>Please wait, you are being redirected...</p>
</body>
</html>