开头:先讲一个真实场景:你接手的Vue项目,.env 里躺着一行 CLIENT_ID=f9d6262000304e1b83b00eb616edfb87,旁边躺着 CLIENT_SECRET、REDIRECT_URI、VUE_APP_API_BASE_URL 之类的配置。如果你只是把它当成"后端给的参数,照抄就行",那大概率会在联调时被 token 相关的报错折磨到怀疑人生。这篇内容就围绕这行 CLIENT_ID 展开,讲清楚它在 Jeecg 微服务 + OAuth2 获取 token 的链路里到底负责什么、前端为什么要在 .env 里维护它、改错了会出什么问题,以及生产环境怎么把这套配置管理得更稳。
1. 从一串字符开始:CLIENT_ID在OAuth2授权流程里到底扮演什么角色
1.1 它不是密码,而是"应用的门牌号"
CLIENT_ID=f9d6262000304e1b83b00eb616edfb87 这一串 32 位十六进制字符串,在 OAuth2 协议里的官方定义叫 client identifier,翻译过来就是"客户端标识"。你可以把它理解为应用在授权服务器那边登记过的门牌号:授权服务器(JeecgBoot 里通常是基于 Spring Security OAuth2 实现的认证中心)看到这个 ID,就知道"哦,这是哪个前端应用在请求令牌"。
很多人会把它和 CLIENT_SECRET 搞混。这里必须分清楚:
- CLIENT_ID 是半公开的,它出现在授权链接、重定向请求、token 请求参数里都没有问题,前端代码里暴露它也不会造成直接的安全事故。
- CLIENT_SECRET 是客户端的"密码",原则上只能保存在后端服务里。纯浏览器端的 SPA 应用其实不适合持有 secret,因为打包后的 JS 任何人都能扒出来。
所以在 JeecgBoot 的典型前端项目里,你会看到 .env 中往往只配置了 CLIENT_ID,而真正需要 secret 的 token 交换环节,要么走后端网关代理,要么使用 authorization_code + PKCE 这种不需要 secret 的模式。这也是为什么这行配置看起来"孤零零",却在整套认证流程里不可或缺。
1.2 它在授权码模式下的两次出场
以 Jeecg 微服务最常见的授权码模式(Authorization Code)为例,CLIENT_ID 会出现在两个关键节点:
第一次是前端引导用户跳转到认证中心的登录页时,拼接的 URL 大概是这样的:
text复制https://auth.xxx.com/oauth/authorize?client_id=f9d6262000304e1b83b00eb616edfb87&response_type=code&redirect_uri=https://app.xxx.com/callback&scope=all
授权服务器拿到 client_id 后,会去数据库里查这个应用是否注册过、redirect_uri 是否在白名单里。如果 CLIENT_ID 写错了,这里直接就会报 Invalid client 或者 Invalid redirect_uri。
第二次是前端拿到授权码 code 之后,需要用 code 去换 token。这一步按规范应该带上 client_id、client_secret、code、redirect_uri、grant_type。但前面说了,纯前端不能存 secret,所以 JeecgBoot 的网关层通常会把 token 端点挡在内部,由后端统一去换 token,前端只需要把 code 传给后端接口就够了。此时 CLIENT_ID 的作用更多是让后端知道"这次换取令牌是针对哪个前端应用发起的",方便后续做 token 粒度控制、权限隔离和审计。
1.3 为什么 JeecgBoot 体系里前端一定要显式配置它
有人可能会问:既然是后端网关统一换 token,前端还要这个 CLIENT_ID 干什么?直接写在后端不行吗?
这个问题的答案藏在 JeecgBoot 的多租户、多应用设计里。JeecgBoot 的微服务版本里,同一个认证中心可能同时服务多个前端:可能是管理后台,可能是手机 H5,可能是小程序对接层。每个前端的回调地址、token 有效期、可访问资源范围都不一样。CLIENT_ID 就是用来区分这些应用的。
假设你现在同时在维护一个运营后台和一个用户端 H5,它们的 .env 里配置的 CLIENT_ID 一定不同。运营后台的 client_id 指向一个 redirect_uri 为 https://admin.xxx.com/callback 的应用;H5 的 client_id 指向 https://h5.xxx.com/callback。如果前端把两个 CLIENT_ID 搞混,跳转登录后授权服务器会返回 redirect_uri mismatch,因为它在注册信息里看到的是一个完全不同的回调地址。
所以,这行 CLIENT_ID=f9d6262000304e1b83b00eb616edfb87 表面上只是一串配置,实际是前端应用在认证体系里的"身份证号",服务端所有与"这个应用"相关的策略,都靠它来索引。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. .env 如何把前端配置"藏"起来又"喂"给请求层
2.1 Vue CLI 与 Vite 环境下 .env 的加载规则
Vue 项目里管理环境变量,最常见的就是 .env 文件。以 Vue CLI 为例,当你执行 npm run serve 时,默认加载 .env、.env.development;执行 npm run build 时,加载 .env、.env.production。具体加载规则见下表:
| 文件 | 环境 | 说明 |
|---|---|---|
| .env | 所有环境 | 公共配置,优先级最低 |
| .env.development | 开发环境 | vue-cli-service serve 时加载 |
| .env.production | 生产环境 | vue-cli-service build 时加载 |
| .env.local | 本地覆盖 | 优先级最高,通常不进 Git |
如果你用的是 Vite,规则类似,只是变量前缀有区别:Vite 默认只向客户端暴露 VITE_ 开头的变量,而 Vue CLI 暴露 VUE_APP_ 开头的变量。这里要特别留意:CLIENT_ID 这个变量名,它不带任何前缀,那么它在 Vue CLI 和 Vite 环境下默认不会被注入到前端代码里。
你可能会问:那为什么很多 JeecgBoot 项目的 .env 里直接写 CLIENT_ID=xxx,前端代码
