唯杰地图 logo唯杰地图
VJMAP
EN

token-cors

vjmap/references/trouble/token-cors.md 6.9 KB227 行
原文

本文回答:请求 403 / 401 / CORS —— token 配置错、无权限、前置代理丢了跨域头。

一、症状

403 Forbidden(token 问题看这里)

GET https://vjmap.com/server/api/v1/map/openmap/xxx 403 (Forbidden)
// body: {"code":7,"data":{"reload":true},"msg":"Token is invalid"}

token 缺失/无效/过期都由网关 JWT 中间件返回 403(msg 为 Token is invalid / Token expired 等),不是 401。

401 Unauthorized

GET .../map/openmap/xxx 401 (Unauthorized)

vjmap 服务只在服务未激活授权 / 超出最大用户数时返回 401(body {error:...}),与 token 无关。

CORS blocked

Access to fetch at 'https://xxx.com/api/...' from origin 'https://yyy.com'
has been blocked by CORS policy

二、token 错误(403,body.code=7)

原因

  • token 未设
  • token 过期
  • token 对应的账号没权限
  • 前端硬编码 token(被泄漏后封禁)

排查

const svc = new vjmap.Service(
  'https://vjmap.com/server/api/v1/', // url
  'xxxxxxx'                            // token
);

Node 环境(自定义 IRequest)的 403 陷阱

浏览器版 Service 内部用 XMLHttpRequest,且 token 走 args.headers.token 请求头,不在 query 参数里。在 Node 里测 SDK 纯接口时必须:① globalThis.window = globalThis(UMD 顶层引用 window);② 注入 fetch 版 IRequest(new Service(url, token, req) 第三参),且必须转发 args.headers,否则所有请求静默 403 {code:7, msg:"Token is invalid"}、SDK 层只看到 {reload:true}:

globalThis.window = globalThis;
const req = {
  get: async (url, params, args) => {
    const u = new URL(url);
    for (const [k, v] of Object.entries(params ?? {})) u.searchParams.set(k, v);
    const r = await fetch(u, { headers: args?.headers });  // ← token 在这里
    const t = await r.text();
    try { return JSON.parse(t); } catch { return t; }
  },
  post: async (url, data, args) => {
    const r = await fetch(url, { method: "POST", headers: args?.headers, body: JSON.stringify(data) });
    return r.json();
  },
  put: async () => ({}), patch: async () => ({}), del: async () => ({}), options: async () => ({}),
};
const svc = new vjmap.Service(url, token, req);  // 第三参注入

排查特征:SDK 返回 {reload: true} 但 currentMapParam() 里 bounds/version 全是 null → 多半是所有请求都在 403。

三、403 权限不足(body 为 {error:"no permission to access this map…"})

原因

  • token 账号没有该地图的访问权限
  • 密码图没带 secretKey/accessKey(openMap 是 HTTP 200 + {status:403, error} 带内返回;其他命令是 HTTP 403)
  • 地图被设为"仅自己可见"
  • 共享过期

排查

  1. 登录 vjmap 后台 → 我的地图 → 找到对应 mapid
  2. 看"访问权限"设置
  3. 或用管理员 token 测试:能行 → 是权限问题

四、CORS 错误

原因

  • 前端域名 https://app.com 请求后端 https://api.other.com,且中间某层没带 Access-Control-Allow-Origin
  • vjmap 服务(官方与自建)默认已放行 *:地图服务每个响应都带 Access-Control-Allow-Origin: *,/api/v1/service 组走 cors 中间件(AllowOrigins *)——没有 CORS 配置项可开关
  • 所以自建部署出现 CORS 报错,多是前置 Nginx/网关未透传、改写了头,或 OPTIONS 预检被拦

前置 Nginx 时补头

如果前置代理吞了上游的跨域头,在 Nginx 反向代理加头:

add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Content-Type, Authorization, token';   # SDK 用 token 请求头

if ($request_method = OPTIONS) {
  return 204;
}

前端代理(开发)

Vite vite.config.js:

export default {
  server: {
    proxy: {
      '/vjmap-api': {
        target: 'https://vjmap.com',
        changeOrigin: true,
        rewrite: p => p.replace(/^\/vjmap-api/, '/server/api/v1')
      }
    }
  }
};
const svc = new vjmap.Service('/vjmap-api/', token);   // 走代理,绕 CORS

五、混合内容(Mixed Content)

Blocked loading mixed active content

原因

  • 前端 HTTPS 页面调 HTTP 后端 → 被浏览器拦

修法

  • 后端升级 HTTPS
  • 或前端也用 HTTP(仅测试用)
  • 或配置 Content-Security-Policy: upgrade-insecure-requests

六、WebSocket 握手失败

vjmap 某些后端特性走 WebSocket:

WebSocket connection to 'wss://...' failed

原因:

  • 后端未开 WSS
  • Nginx 未代理 Upgrade 头

Nginx:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

七、CORS 的 preflight

浏览器对 PUT / DELETE 或带自定义 header 的请求先发 OPTIONS:

Access-Control-Allow-Headers: Authorization, Content-Type

后端必须响应 OPTIONS 并返回允许的头。

八、排查一步到位

try {
  const res = await svc.openMap({ mapid: 'xxx' });
  console.log(res);
  if (res.error) console.error('业务错误(HTTP 200 带内):', res.error);
} catch (err) {
  // SDK 走 XMLHttpRequest:非 2xx 一律 reject,err = {status, response, data, xhr}(不是 Error/TypeError)
  //   err.status === 0 → CORS / 网络 / URL 错
  //   err.status === 403 → JSON.parse(err.response).msg 为 "Token is invalid" 等 → token 问题;.error 为 no permission → 权限
  //   err.status === 404 → 图不存在;500 → 后端问题
  console.error('请求失败:', err.status, err.response);
}

结合 Network 面板看响应头:

  • Access-Control-Allow-Origin: * 存在 → CORS OK
  • 403 + {code:7} → token 问题;403 + {error:"no permission…"} → 权限
  • 401 → 服务未激活 / 超最大用户数
  • 500 → 后端问题

九、自建后端的常见配置缺失

缺 症状 修
Access-Control-Allow-Origin CORS blocked Nginx / 后端加
Authorization 头未放行 preflight 失败 Access-Control-Allow-Headers: Authorization
HTTPS 证书 混合内容 证书升级
WebSocket 升级头 实时查询失败 Nginx upgrade 配置

十、常见坑

  1. 前端写死 token 上 git:泄露后被封;改用后端签发。
  2. token 有尾部空格:复制粘贴时带上了 → 403 Token is invalid;trim 一下。
  3. URL 末尾 / 无需手工处理:new vjmap.Service(url) 会自动补末尾 /。
  4. Cookie 鉴权 + CORS 不带 credentials:设 credentials: 'include' + 后端 Access-Control-Allow-Credentials: true。
  5. IP 白名单限制:开发环境 IP 变了被拒;后台加白名单或用 token。

相关