vue+axios+koa 前端后端实现登录拦截--http拦截

一、前言

要想统一处理所有http请求和响应,就得用上 axios 的拦截器。通过配置http response inteceptor,当后端接口返回401 Unauthorized(未授权),让用户重新登录。

 

二、具体实现

当前端使用localStorage存储登陆信息,但是这个时候,后端一般在登陆的成功的时候,使用JWT(json web token)会生成token,这个token包含了失效时间和秘钥,以及用户的信息。比如:

1、登陆时候后端生成失效时间的JWT

const jwt = require("jsonwebtoken");

const token = jwt.sign(userToken, secret, {expiresIn: '7d'});

这个userToken可以是,比如是登陆进来的user的id和name。

const userToken = {
     name: name,
     id: res[0]["id"]
};

secret是自己定义的salt,确保这个jwt生成的字符串的随机性和不易破解性。比如项目英文名称

const secret = 'happy-chat-sec'

 

2、前端的http请求的封装

http请求都是需要需要经过axios的,通过axios的create的方法可以添加http的前缀和超时时间。比如这个实例:

const service = axios.create({
  baseURL: '/',
  // baseURL: '/',
  timeout: 10000
})

通过interceptors.request.use方法对所有的http请求,进行封装,比如对于已经登陆的,localstorage里有这个字段HappyChatUserToken,说明需要在http报文头部加上Authorization这个字段用来校验前端是否登陆,后端会从http报文头部中取到这个值,并进行校验,是否是正确的。

service.interceptors.request.use(config => {
  const token = localStorage.getItem('HappyChatUserToken');
  if (token) {
    /*Bearer是JWT的认证头部信息*/
    config.headers.common['Authorization'] = 'Bearer ' + token;
  }
  return config;
},error => {
  return Promise.reject(error);
});

 

3、前端的http返回的拦截

前端是本地缓存,但是后端才是最根本来控制登陆的,如果失效时间到了,jwt的token就会失效,但是前端还是拿着这个失效的token去请求后端接口,那肯定是拿不到数据的,这时候后端返回401,未授权。

此时后端代码遇到失效或者错误的JWT的返回

/*处理验证是否登陆*/
const jwt = require("jsonwebtoken");
const secret = require("../config").secret;

module.exports = async (ctx, next) => {
    /*同步 校验token*/
    const auth = ctx.get("Authorization");
    const token = auth.split(' ')[1];
    try {
        const userToken = jwt.verify(token, secret)
        ctx.user_id = userToken.id;
        ctx.name = userToken.name;
        await next()
    } catch (err){
        ctx.throw(401, err)
    }
}

这时候前端对返回的http请求做拦截,并跳转到登陆页面

service.interceptors.response.use(
  response => {
    let data = response.data;
    if (!data.data) {
      //   登陆成功的回调地址
      return data;
    } else {
      return data;
    }
  },
  error => {
    if(error.response) {
      switch (error.response.status){
        case 401:
          /*返回401,清空token信息,关闭socketio,并跳转到登陆页*/
          let userInfo = JSON.parse(localStorage.getItem("HappyChatUserInfo"));
          socketWeb.emit('logout', userInfo.user_id)
          localStorage.removeItem("HappyChatUserToken");
          localStorage.removeItem("HappyChatUserInfo");
          setTimeout(function() {
            router.push({
              path: "/login",
              query: {redirect: router.currentRoute.fullPath}
            });
          }, 500);
      }
    }
    return Promise.reject(error.response)   // 返回接口返回的错误信息
  }
);

跳转到登陆,同时使用setimeout定时器,作为延迟跳转,使用router.push({path:'/login',query: {redirect: router.currentRoute.fullPath}}),这里使用query的redirect的方式,用来解决:登录失效后跳转到去登录页面。登录后打开的是你最后登录的页面。

这时候,登陆成功跳转页面时不能写死写成首页。

  let redirect = decodeURIComponent(this.$route.query.redirect || '/robot');
  this.$router.push({ path: redirect });

上面代码是在登陆成功之后,先去$route.query.redirect中找是否有值,没有值,那就正常跳转到/robot路由上。

注意:decodeURIComponent函数编码的 URI 进行解码

 

三、总结

使用router.push({path:'/login',query: {redirect: router.currentRoute.fullPath}})用来登录失效后跳转到去登录页面。登录后打开的是你最后登录的页面。修改登陆页面的跳转let redirect = decodeURIComponent(this.$route.query.redirect || '/robot');

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章