前端登录鉴权(JWT,cookie,session内附源码)

235 阅读3分钟

什么叫做登录鉴权呢?

众所周知http协议是无状态的,下一次链接与本次链接没有任何联系,那么这样的话,服务器就不知道客户端的一些情况(比如用户之前登录过,现在是否还有登录状态),所以需要采用一些方法去保留这种登录状态。这些方法就叫登录鉴权。

JWT策略

jwt 又称json web token。 它由三部分组成,中间以两个逗号隔开.分别为头部,负载,和签名。

//大概是这个样子
fcf21f1515d54e6791810b2166c38ece,d169ae7e5c074a6ca020667c32e3f58a,6ca020667c32e3f58a

组成

三部分具体是怎么组成的呢?

头部

原始为json对象,里面有两个字段,type指策略(jwt),加密算法,(这个后面签名部分会用到) 这里还采用一种编码方式(一般是base64)进行编码,对json对象编码形成字符串。这样构成第一部分

负载

也是json对象,主要是一些用户信息,方便之后解码以后去对照鉴权,去快速查询数据库。还有一部分是token的配置信息,如过期时间,生效时间等。如上这里还采用一种编码方式(一般是base64)进行编码生成字符串。

签名

这个是利用,头部的加密算法,一个服务器保存的密钥,对头部,负载部分的字符串进行加密,从而生成第三部分

实现过程

步骤

当客户端第一次请求服务端,需要进入到登录页面,此时,用户输入账号密码进行登录,先到数据库查询账户密码,如果存在,并且对了,就生成token发送到客户端,一般存在浏览器localstorage里面

后续客户端发送请求,需要带上token,一般放在请求头里面的Authorization,服务端去验证,如果没有token就证明没登录态。


//这里我用前端的egg框架去举例子
async login() {
    const username = this.ctx.query.username;
    const password = this.ctx.query.password;
    const sel = await this.app.mysql.get('user', { username });
    //先获取账户密码,然后去查询数据库如果没查到报错
    let msg = {};
    if (sel == null) {
      msg = {
        code: 1001,
        data: {
          message: '账号不存在',
        },
      };
    } else if (sel.password == password) {
    //这里就是关键的生成token的代码。将username,作为数据负载穿进去,编码。调用了 egg的jwt包,可以快速生成,解码。这里的插件采用默认的编码,默认的生效时间,过期时间等。
      const token = this.app.jwt.sign(username, this.app.config.jwt.secret);
      msg = {
        code: 200,
        data: {
          message: '登录成功',
          token,
        },
      };
    } else {
      msg = {
        code: 1002,
        data: {
          message: '密码错误',
        },
      };
    }
    return msg;
  }
//验证token
module.exports = () => {
  return async function jwtErr(ctx, next) {
    const token = ctx.request.header.token;
    console.log(ctx.request.header.token);
    let decode = '';
    if (token) {
      try {
        // 解码token
        decode = ctx.app.jwt.verify(token, '123456');// 验证token
        // 绑定这个中间件的接口 ,就可以获取赋值之后的username;
        ctx.username = decode;
        await next();
        console.log('decode======>', decode);
      } catch (error) {
        ctx.status = 1004;
        ctx.body = {
          message: '解析失败' + error.message,
        };
        return;
      }
    } else {
      ctx.status = 1005;
      ctx.body = {
        message: '没有token',
        data: ctx.request.header,
      };
      return;
    }
  };
};

cookie,session策略

然后再来说一下cookie,session策略,两者相比最大的差别应该是,存储地方不同,客户端登录过后,服务端会开辟出一片区域存储信息,并生成一个sessionId去标识这一个信息。并把sessionId返回给客户端。客户端存在cookie里面在每次发送请求时候自动带给服务端。这样服务端就可以从cookie中的sessionid换取用户信息,从而判断登录态。

const express = require('express');
const session = require('express-session');
//中间件
const sessions = require('express-session');
const app = express();
//app use去应用中间件
app.use(express.json());
app.use(express.urlencoded({extended:true}));
const username ='hei';
const password = '123';
var mysession;
app.use(session({
    secret:'heihei',//签名
    name:'heihei',
    cookie:{maxAge:15000},
    resave:false,
    saveUninitialized:false
}))
//监听访问
app.get('/',(req,res)=>{
    console.log('访问网页');
    mysession =req.session;
    if(mysession.username){
        res.send(`欢迎回来${mysession.username}
        <a href='/logout'>登出</a>
        `)
    }else{
        res.send('你好新用户')
    }
})
app.post('/login',(req,res)=>{
    if(req.body.username ===username&&req.body.password===password){
        mysession = req.session;
        mysession.username = 'hei';
        console.log('中间件生成session',req.session,req.sessionID);
        res.send(`欢迎回来${mysession.username}
            <a href='/logout'>登出</a>
            `)
    }
   else {
    res.send(`密码错误
    `)
   }
})
app.get('/logout',(req,res)=>{
    req.session.destroy();//清除session
    console.log(`登出,${req.session}`)
    res.redirect('/')
})
app.listen('3001',()=>console.log('3001 running!'));