实作login试误篇

前言

这星期二突然发现我们应该做的登入系统还没做,听队友们讨论听得头皮发麻,太多我听都没听过的专有名词,但也只能故作镇定继续听下去,讨论结束前,试图用很具体很直白的方式确认我该做的事后,回到位子上,我其实不知道怎么开始,先研究了一下什么是pinia,再研究一下前端登入token,老实说我连token是啥都不知道ㄎㄎ,这天的结束,Tim给了我一个功课:去看一下quasar cookies...点头后带着满满的问号回家去。
在家看完cookies后,我也不知道这到底用来干嘛?顺便把那堆专有名词查一查,恩~好像很有趣,虽然有趣,问号不减反增,心里髒话都要飙出来了,隔天Tim居然一早就出现在工作室,根本一盏明灯啊~~赶快巴着他把问号解一解,他也很好心的带着我一步一步实作,如果没有他,我根本毫无头绪,做完后趁还有记忆时赶快把来龙去脉笔记下来,本来说好的V-model你就再等等嘿!这礼拜救火先...

那就开始吧~

开新档案Login.vue,先简单刻出画面,form中放两个input,分别为username及password
            <q-form class="q-gutter-md">              <q-input                filled                v-model="username"                label="Username"                lazy-rules              />              <q-input                filled                type="password"                v-model="password"                label="Password"                lazy-rules              />              <div>                <q-btn label="Login" type="submit" color="primary" />              </div>            </q-form>
以regex做简单验证,先设定条件:a.此栏位必填 b.仅可输入英数字,在q-input标籤内加入以下code
         :rules="[                  (val) => !!val || `此栏位必填`,                  (val) => /^[a-zA-Z0-9]*$/.test(val) || `请输入英数字`,                ]"
设监听器:在submit后执行函式onlogin在步骤2&3做完后,template中的html如下:
            <q-form class="q-gutter-md" @submit="onLogin">              <q-input                filled                v-model="username"                label="Username"                lazy-rules                :rules="[                  (val) => !!val || `此栏位必填`,                  (val) => /^[a-zA-Z0-9]*$/.test(val) || `请输入英数字`,                ]"              />              <q-input                filled                type="password"                v-model="password"                label="Password"                lazy-rules                :rules="[                  (val) => !!val || `此栏位必填`,                  (val) => /^[a-zA-Z0-9]*$/.test(val) || `请输入英数字`,                ]"              />              <div>                <q-btn label="Login" type="submit" color="primary" />              </div>            </q-form>
function onLogin内容:取得使用者输入的data
const username = ref("");const password = ref("");async function onLogin() {    const formData = {      username: username.value,      password: password.value,    };} 
画面页先到此告一段落,转开一个新档案auth.js,执行验证动作,后端部分队友已经帮我写好一只api,跟他们确认我要做的事应该就是这样吧?!(满脸懞样...)如果status等于200即验证过关,存token如果status不等于200,跳出错误讯息,告知使用者帐密错误
export const useAuthStore = defineStore("auth", {  state: () => ({    token: ref(""),    errorMessage: ref(""),  }),  actions: {    async login(formData) {      try {        const res = await api.post("/auth/login", formData);        if (res.status === 200) {          this.token = res.data.token;        }      } catch (error) {        console.log(error.message);        this.errorMessage = "帐号或密码有误";      }    },
天真如我以为上面那样就存好token了。但根本不是这样,好的,原来我得用setCookie的方式把token放入cookies里
export const useAuthStore = defineStore("auth", {  state: () => ({    token: ref(""),    errorMessage: ref(""),  }),  actions: {    async login(formData) {      try {        const res = await api.post("/auth/login", formData);        if (res.status === 200) {          this.token = res.data.token;          //加在这里          Cookies.set("jwt", this.token);           //加在这里        }      } catch (error) {        console.log(error.message);        this.errorMessage = "帐号或密码有误";      }    },
另外要加一个函式执行从cookie拿token的动作
getTokenFromCookie() {      this.token = Cookies.get("jwt");      if (!this.token) throw new Error();    },
还有另一个函式执行从cookie中移除token的动作
removeJWTCookie() {      Cookies.remove("jwt");      this.token = null    },
回到Login.vue,引入useAuthStore,在函式onLogin中加上验证,如果验证有过则导入首页
const authStore = useAuthStore()async function onLogin() {    const formData = {      username: username.value,      password: password.value,    };    await authStore.login(formData);    router.push("/");}
前面login告一段落,要去首页处理如果使用者cookies带有JWT,则可直接浏览页面,无需再登入,反之则导入登入页面引入useAuthStore引入onBeforeMount from vue
import { onBeforeMount } from "vue";import { useAuthStore } from "src/stores/auth";onBeforeMount(() => {  try {    authStore.getTokenFromCookie();  } catch (error) {    router.replace("/login");  }});
接着要把token绑在api的request上,让后端确认身分权限,再根据其权限回传相对应资料,这一部分要在管理api的档案axios.js中做处理
api.interceptors.request.use(  (config) => {    if (authStore.token) {      config.headers.Authorization = `Bearer ${authStore.token}`;    }    return config;  },  function (error) {    // Do something with request error    return Promise.reject(error);  });
另外为了判断JWT真伪,在axios.js先做如果response的status为401时,就清空JWT并导回登入页的设定备用,待后端逻辑写好后会用上
api.interceptors.response.use(  function (response) {    return response;  },  function (error) {    if (401 === error.response.status) {      authStore.removeJWTCookie();      router.replace("/login");    }    return Promise.reject(error);  });

目前实作先到此(汗),之后还可以优化加上过期时间及登出功能。

重构

过了一天后端逻辑写好来测试步骤13功能,登愣,虽然会清除JWT但不会导回登入页。

最后在网路上求解,解方如下:
http://img2.58codes.com/2024/20163234kc0lw7Ap2K.png
http://img2.58codes.com/2024/20163234RYb4E21slq.png

依样画葫芦后成功了,感谢网路大神~之后Tim跟我讲解了一下原理,我们直接引用,在画面渲染前是拿不到router的,所以必须创一个globalRouter在一开始还没有接到时预设为null,主要画面渲染后有router时就变为router,再将globalRouter输出供axios.js引用即可
6. 经过高人指点,发现我一开始用变数存取token的方式并不优,直接用cookies的方式会简洁许多,所以从步骤6之后做修改

export const useAuthStore = defineStore("auth", {  state: () => ({    errorMessage: ref(""),  }),  actions: {    async login(formData) {      try {        const res = await api.post("/auth/login", formData);        if (res.status === 200) {          Cookies.set("jwt", res.data.token, { expires: 1 });        }      } catch (error) {        console.log(error.message);        this.errorMessage = "帐号或密码有误";      }    },
不再需要这个getTokenfromCookie的函式,改成在axios.js档中,只要一发出请求就先从cookies取JWT
  api.interceptors.request.use(  (config) => {    //加上这行    const token = Cookies.get("jwt");    //加上这行    if (token) {      config.headers.Authorization = `Bearer ${token}`;    }    return config;  },  function (error) {    return Promise.reject(error);  });
同理也不需要removeJWTCookie这个函式,直接写在axios.js中即可
api.interceptors.response.use(  function (response) {    return response;  },  function (error) {    if (error.response.status == 401) {      //修改这一行      Cookies.remove("jwt")      //修该这一行      globalRouter.router.push("/login");    }    return Promise.reject(error);  });
原步骤11则修改如下:
onBeforeMount(() => {  if (!Cookies.get("jwt")) router.replace("/login");});

重构到此告一段落,未来如果又发现更好的方式再继续更新,人生就是不断地试误不断地进步,大家说是吧(压力解除后有空讲废话了XD)

可能大家都知道但我突然之间领悟的小笔记

onBeforeMount:在画面渲染前执行/onMount:在画面渲染后执行,所以如果是权限问题应该放在渲染前,而不是渲染后,如果是放在渲染后,渲染完才发现没有权限看,再导入登入页,虽然使用者端没有差别,domain端却多了一步,很没必要,可能有耗效能的问题,我不确定,自己猜的...throw的error一定要有个相对应接它的对象,不然会报错

资料来源

quasar cookiesonMountedaxios interceptorsHow to use router inside axios interceptors. React and Vue

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章