前言

因为新参加前端大哥在不到三个月之内就离职了,之前他担任的项目中还缺少了获奖名单的需求,正好我没活了就由我接手了。乍一看署理装备,好家伙,乱中麻(代码虽然能走通,但增加了后续保护的了解本钱)。

因为触及到 SSO 单点登录,需求域名才干正常登录(即恳求登录和用户信息的接口),因而需求在 nginx 服务器装备署理,导致了署理绕来绕去。

需求域名的原因:

由所以国际服,账号登录运用的是 SSO 单点登录,需求凭借 facebook, google 等登录接口(以 facebook 为例,即登录后 facebook 会回来 token 等用户信息),因为 facebook 规则凭借 SSO 单点登录时需求域名,因而团队中一般用的 ip 地址不能作为测验环境,而需求有域名的 nginx 服务器署理到测验环境,才干进行登录。

事务中关于本地 devServer 署理和 nginx 署理的弯弯绕绕以及标准(附赠cookie特典)

而在开发过程中,一般设置了登录逻辑,因而有必要经过登录(即成功发送登录恳求)才干执行其他操作(其他接口恳求,页面跳转等)。但因为后端那边的规划,把普通邮箱的登录也设置成了需求域名才干发送恳求,因而需求凭借 nginx 服务器署理到普通 ip 的测验环境。

正文

在本文中,普通测验地址为 10.10.13.187:8082/,带有域名的 nginx 服务器为 debug-test.example.com:59998/,后端接口 ip 地址分别为 登录接口 12.12.5.205:21301/,事务接口 12.12.5.205:21302/,上传接口 12.12.5.205:21303/

环境变量 env.js 的装备

关于环境变量,因为不同的环境需求不同的跳转链接,署理地址等等,即开发环境有开发环境相匹配的 env,镜像环境有镜像环境的 env,正式环境有正式环境的 env。

在咱们团队比较老的项目中,一般是这样布置的

事务中关于本地 devServer 署理和 nginx 署理的弯弯绕绕以及标准(附赠cookie特典)

// .env.development
NODE_ENV=development
VUE_APP_ENV=development
PUBLIC_PATH=/account
事务中关于本地 devServer 署理和 nginx 署理的弯弯绕绕以及标准(附赠cookie特典)

经过 process.env.VUE_APP_ENV ,拿到此刻的开发环境,再做相应的匹配,但这样做交给后端打包布置后,后端是无法看到 env 的装备的

而最新的项目中,则是放到 public 文件下,后端把项目打包后能够露出出来看到

// public/env.js
window.$$env = {}
// src/utils/env.ts
export const env = (window as any).$$env as Env;

axios 的装备

一开端的 axios 的装备

account.js

const instance = Axios.create({
  baseURL: "https://" + env.baseUrl.login + "/",
});
instance.interceptors.request.use((config) => {
      return intercepeReq(
        env.baseUrl.login,
        env.signature.secret
      );
});

从这儿能够初略看出,baseURL 过于不合理,按理来说axios应该是由本地发送恳求的,但这儿竟然拼接了https成了一个网址,有点不符合认知。

interceptors 恳求阻拦部分,关于 intercepeReq 函数,是用于传签名的,但依照语义来看,命名应该是放入签名 signature 中的变量却放入了恳求根本途径 baseUrl 的变量当中,而且签名和根本途径竟然同用一个变量。

一开端的 env 装备

window.$$env = {
  baseUrl: {
    login: "debug-test.example.com:59998",
    contribute: "debug-test.example.com:59998",
    upload: "debug-test.example.com:59998",
  },
  signature: {
    secret: "WAxxhxxa8afxxqaWTxxwyt8BxxBeQpHcTxxG6xxaknA=",
  },
}

修改后的 axios 装备

const instance = Axios.create({
  baseURL: env.baseUrl.account,
});
instance.interceptors.request.use((config) => {
      return intercepeReq(
        env.signature.backend,
        env.signature.secret
      );
});

修改后的 env 装备

window.$$env = {
  baseUrl: {
    account: "/account-web-sso",
    createjam: "/1st-co-creation/createjam",
    uploadFile: "/1st-co-creation/upload",
  },
  signature: {
    secret: "WAxxhxxa8afxxqaWTxxwyt8BxxBeQpHcTxxG6xxaknA=",
    backend: "debug-test.example.com:59998",
  },
}

其间,因为恳求头设置了签名的原因,签名中包含了主机 host 即 signature 中的 backend,客户端宣布的网络恳求的主机host有必要与设置的签名的主机host相同,考虑到该项目因为登录权限有必要运用到域名,因而签名中的主机host设置一致设置为域名的测验环境主机host,而不是本地 ip 或许其他测验环境的。

一开端其他恳求的 axios 装备

contribute.js

const instance = Axios.create({
  baseURL: "https://" + env.baseUrl.contribute + "/createjam/",
});
instance.interceptors.request.use((config) => {
      return intercepeReq(
        env.baseUrl.contribute,
        env.signature.secret
      );
});

upload.js

const instance = Axios.create({
  baseURL: "https://" + env.baseUrl.upload + "/",
});
instance.interceptors.request.use((config) => {
      return intercepeReq(
        env.baseUrl.upload,
        env.signature.secret
      );
});

devServer 装备

一开端 devServer 的装备

1"/apis/v1/userinfo": {
         target: "http://10.10.5.205:21301/",
         changeOrigin: true,
       },
  【1"/apis/v1/logout": {
         target: "http://10.10.5.205:21301/",
         changeOrigin: true,
       },
  【2"/apis/v1/upload": {
         target: "http://10.10.5.205:21303/",
         changeOrigin: true,
       },
  【3"/apis/": {
         target: "http://10.10.5.205:21302/",
         changeOrigin: true,
       },

再结合 axios 的 各个接口恳求装备片段

// account.js
export function getUserinfo() {
  return instance.get("/apis/v1/userinfo");
}
export function postLogout() {
  return instance.get("/apis/v1/logout");
}
// contribute.js
export function contribute(path: string, data: ContributeData) {
  return instance.post(`/apis/v1/works/submit/${path}`, data);
}
// upload.js       
export function uploadFile (file: File) {
  return instance.post(`/apis/v1/upload/anniversary`, file);
}

能够看出,每一个接口恳求都根本在 deServer 中装备了,然后其他特殊接口匹配不上就会匹配常用事务接口,难以区别出每个署理的意义,这真的对后续开发人员极其不友好(后续开发者要缕一遍逻辑才干知道这接口是衔接谁的)。从上述能够看出, account.js 中的 两个 /apis/v1/xxx 分别装备了两个,都是指向的同一个 ip 地址,这明显不太合适。

修改后

每个 devServer 的装备最好有个前缀,能够用 vue.config.js 中的 publicPath 的值,即标明晰这个接口是哪个项目的,而且一致,比方以 /1st-co-creation 为例。

结合 axios 中的 baseUrl 的值,署理前缀 /1st-co-creation 加上 baseUrl,再调配正则表达式对接口重写来恳求后端提供的接口地址。

而关于 devServer 的装备能够看这篇文章 白嫖即食:构建工具的proxy署理装备差异(处理跨域)

"/1st-co-creation/createjam": {
    target: "http://10.10.5.205:21302/",
    changeOrigin: true,
    pathRewrite: {
      "/1st-co-creation/createjam": "",
    },
}
"/1st-co-creation/upload": {
    target: "http://10.10.5.205:21303/",
    changeOrigin: true,
    pathRewrite: {
      "/1st-co-creation/upload": "",
    },
  },

然后再说说通用接口,比方登录接口在事务中肯定有复用的,所以直接标明该接口的功能就行了。

 "/account-web-sso": {
    target: "http://10.10.5.205:21301/",
    changeOrigin: true,
    pathRewrite: {
      "/account-web-sso": "",
    },
  },

聊到这儿,前端的署理根本聊完了,但最坑爹的便是触及到 SSO 单点登录需求域名,然后咱们就需求在 nginx 再做一层署理。所以也不能怪老哥,毕竟搁谁来也得蒙,我隔壁呆了好几年安卓转前端的哥也蒙。

nginx 的装备

而只需你敢用域名署理到本地(即 nginx 中【1】的署理,我就敢说它回来的便是一个 html 文档(整个文档是项目经过webpack打包之后放入署理后服务器中的),然后依据 html 文档里的js恳求途径,以此刻域名发送恳求,即你上面在 devServer 中的署理装备根本便是白费力气,因为它是以debug域名发送恳求的,而你的 devServer 署理装备是以署理后服务器恳求的。

【1】location /1st-co-creation/ {
        proxy_pass http://10.10.13.187:8082/1st-co-creation/;
    }
    location /apis/v1/userinfo {
        proxy_pass http://10.10.5.205:21301;
    }
    location /apis/v1/logout {
        proxy_pass http://10.10.5.205:21301;
    }
    location /apis/v1/works/submit {
        proxy_pass http://10.10.5.205:21302;
    }
    location /apis/v1/upload {
        proxy_pass http://10.10.5.205:21303;
    }

依然能够看到,犯得错误和 devServer 中的如出一辙,而且这儿是 nginx 的装备,许多需求用到debug的项目都会到这儿装备,所以标明是哪个项目非常重要,而且要留意不要污染了整个装备(即只需你用了整个署理姓名,其他人就不能用了)

其实nginx署理的装备和devServer署理的装备原理根本相同,都是匹配成功后把匹配后的字段往后接。 假如在 nginx 没有装备相应的署理或许没有匹配上,会回来 404

修改后的

location /1st-co-creation/ {
    proxy_pass http://10.10.13.187:8082/1st-co-creation/;
}
location /1st-co-creation/createjam/ {
    proxy_pass http://10.10.5.205:21301/;
}
location /account-web-sso/ {
    proxy_pass http://10.10.5.205:21302/;
}
location /1st-co-creation/upload/ {
    proxy_pass http://10.10.5.205:21303/;
}

看看就能够

因为以下设定,导致了客户端的 html 会进行缓存,而 js 或许 css 不会进行缓存,因而当你更新 env.js 时,不能对 env.js 进行删除和改动这两操作,因为当你更新 env.js 并上线后,此刻 env.js 是最新的,为了以防客户端拿到是缓存的(即老的html文件),这时 env.js 是最新的,老的 html 文件无法找到 env.js 中东西而导致报错。

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;
    access_log  /var/log/nginx/host.access.log  main;
    location ~* \.(?:css|js)$ {
        add_header Cache-Control "no-cache";
        root        /usr/share/nginx/html;
        try_files $uri =404;
    }
    location / {
        root        /usr/share/nginx/html;
        try_files   $uri $uri/ /index.html;
    }
}

因为凭借了 nginx 服务器署理来启动项目,导致UI规划开发时不能热更新,需手动改写才干更新一次,但在规划UI时可运用本地,触及到接口时运用域名测验环境。

特典

因为在开发过程中,触及到了 cookie 相关的问题,即因为署理问题导致了后端没有给前端下发cookie,回来了401的错误

事务中关于本地 devServer 署理和 nginx 署理的弯弯绕绕以及标准(附赠cookie特典)

然后经此发现,我对 Cookie 除了简略的了解(它是一个类似于 session 的本地存储空间)外,其他竟一窍不通。

cookie

因为 HTTP 的恳求是无状态的,浏览器宣布的恳求后端服务器没办法区别用户的身份和信息。而浏览器每次发送恳求时都会在恳求头上带着 Cookie 。而 Cookie 会存储一些用户的信息,比方 token,sessionid(通话id),偏好设置等。

一般 Cookie 是由后端设置的,大致流程如下:

  1. 首次拜访网站时,浏览器发送恳求中并未带着 Cookie 。(类似于登录)
  2. 后端看到恳求中未带着 Cookie ,在 HTTP 的呼应头中参加 Set-Cookie (里面放了 Cookie 的信息)回来给浏览器。
  3. 浏览器收到 Set-Cookie 后,会自动将 Cookie 保存下来。
  4. 之后拜访该网站时,HTTP 恳求头就会带着 Cookie,用户就不需求再次登录什么的了。

当然,也有由前端搜集,后端存储的,比方日夜间形式,语言等偏好设置由前端搜集好,发送恳求后带着上由后端存储和管理。

token恳求中呼应头set-Cookie

事务中关于本地 devServer 署理和 nginx 署理的弯弯绕绕以及标准(附赠cookie特典)

恳求中恳求头带着的cookie

事务中关于本地 devServer 署理和 nginx 署理的弯弯绕绕以及标准(附赠cookie特典)

留意:恳求头中带着的cookie是以key=value,“;”离隔的形式带着的。

而想要更直观的看,能够检查浏览器Network中的cookie选项

事务中关于本地 devServer 署理和 nginx 署理的弯弯绕绕以及标准(附赠cookie特典)

cookie中特点

特点 意义
Name Cookie的名称
Value 对应名称的值
Domain Cookie效果的域名
Path Cookie收效的途径
Expires 过期时刻,过了这个时刻后Cookie失效(详细的时刻日期)
Max-age 收效时刻,表明Cookie在多长时刻后失效(倒计时)
Size Cookie的长度,为name和value的长度和
HttpOnly 避免经过JavaScript拜访Cookie(即在操控台中经过document.cookie读取,设置cookie)
Secure 只在HTTPS协议的情况下才会将Cookie传到后端(http达咩)
SameSite 是否允许跨站恳求时发送Cookie
Partitioned 第三方Cookie分区
Priority 优先级

效果范围

Domain

设置 Cookie 效果的域名,即 Cookie 在哪个网站收效。比方后端在 Domain 中设置了 .juejin.cn,那该 Cookie只能在这网站下收效。

Path

当咱们期望 Cookie 只在部分途径下收效,就能够运用Path进行约束。假如 path=/user ,相当于除了 .juejin.cn/user 这个路由途径下能运用设置的 Cookie 外,其他途径都不能运用。

.juejin.cn 能运用
.juejin.cn/user 能运用
.juejin.cn/user/2239042325059533 能运用
.juejin.cn/edior 不能运用

效果时刻

Session

假如Cookie的有效期为Session,类似于sessionStorge相同,即封闭浏览器就失效。

Expires

指定详细的失效时刻,格局大致为2025-02-17T04:44:56.921Z,即到2025年2月17日4点44分56秒921毫秒失效

Max-Age

从拿到 Cookie 后开端倒计时设置 的Max-Age的值。例如Max-age=3000。当获取到该Cookie后开端倒计时,3000秒之后便失效。

Priority优先级

当Cookie的数量超过约束时,浏览器会铲除一部分Cookie。铲除哪些合适呢?Priority特点用来定义Cookie的优先级,低优先级的Cookie会优先被铲除。

Priority特点有三种: Low, Medium, High

其他的过于复杂或许在上述概括中大致说到的就不详细说了。