Session和JWT认证机制

170 阅读5分钟

Session和JWT(JSONWEBTOKEN)都是HTTP协议中用于前后端身份认证的手段,其目的都是:让服务端知晓请求者是哪个用户。

  1. 为什么需要认证?

    因为HTTP是无状态的,这一次的请求跟上一次没有关系,那如果想让服务器知道是“我”,就必须在http请求中加入一些内容,标明我的身份。

  2. 如何认证?

    1. Session认证:客户端会为每一个初次使用网站的用户创建一个session,并将SessionID存储在cookie中发送给客户端,客户端每次请求时都会携带Cookie,服务器端通过SessionID来查找对应的Session,获取用户的信息。

    2. JWT认证是一种基于令牌的身份验证机制,它使用JSON格式来存储用户的信息和其他元数据。在用户成功登录后,服务器会生成一个JWT令牌并将其发送给客户端。客户端在后续的请求中都会携带该JWT令牌,并通过JWT的签名来验证用户的身份。

  3. 各自的优缺点

    1. 跨域

      session认证一般采用cookie,cookie一般是不支持跨域访问的,也就是说,如果客户端与服务端不同源,session认证就不起作用了。(可以设置允许跨域,看下面)

    2. 安全性

      session的用户认证信息存储在服务端,而jwt存储在客户端。从这点来看,session认证要比jwt认证更安全。即使用户窃取了sessionID,可以假冒用户发起请求,但也没有获取到用户具体信息。

    3. 开销

      session认证将用户信息存储在服务端,增大了服务端的负担,当session信息过多的时候,需要多台服务器之间共享session数据。而jwt是将用户信息存储在了本地,不过这样子的话,token能保存的用户信息不会太复杂。

session认证

import express from 'express'
import session from 'express-session'
import cors from 'cors'
const app = express()
app.use(express.urlencoded({ extended: false }))

// 允许指定的域名跨域访问
app.use(
cors({
 origin: 'http://127.0.0.1:8080',
 methods: ['GET', 'POST', 'PUT', 'DELETE'],
 allowedHeaders: ['Content-Type', 'Authorization'],
 credentials: true
})
)

app.use(session({
 name: 'session',
 secret: 'mySecret',
 resave: false,   // 不管有什么修改session,都重新保存
 saveUninitialized: false,  // 如果未修改session,不为新用户创建session
 cookie: {
   // sameSite: null,
   // secure: true,
   maxAge: 10000
 }
 })
)


app.post('/login', (req, res) => {
const { username, password } = req.body
if (username === 'dax' && password === '123456') {
 req.session.username = username
 req.session.password = password
 res.send({
   status: 200,
   message: '登录成功',
   data: req.session
 })
} else {
 res.send({
   status: 401,
   message: '登录失败'
 })
}
})

理清session和cookie

Session是在服务端存储用户信息,而Cookie是在客户端存储用户信息。请求访问Session,需要用到SessionID,只是一般来说,这个SessionID存储在Cookie里。

我们配置session,就是在每个响应里,都返回一个setCookie,name可以自己指定,值为sessionID
Set-Cookie: <name>=<value>[; <attribute>=<value>][; ...]

image.png

也就是说,我们是可以将其他信息保存在cookie里的,只需要在响应中配置setCookie响应头
res.cookie('username', 'johndoe', { maxAge: 3600000 });

客户端会将当前源下的所有cookie发送到服务器,服务器查找对应的name值,获取sessionID,然后索引得到当前用户的session,挂载到req.session上。

Cookie跨域问题

我们知道,浏览器会通过同源策略来识别Cookie是哪个源的,不同源下也不可以通过document.cookie获取其他源的Cookie。同时,目前流行的前后端分离的开发模式,接口服务器和web服务器是不同源的,那么,是不是就不可以采用session认证了?

app.use(
  cors({
    origin: 'http://127.0.0.1:8080', // 允许向本地8080端口设置cookie
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true
  })
)

app.use(
  session({
    name: 'session',
    secret: 'mySecret',
    resave: false,
    saveUninitialized: false,
    cookie: {
      sameSite: "null", // 允许跨域接收cookie
      secure: true,
      maxAge: 10000
    }
  })
)


// 发送端
    $('button').click(function(e) {
      e.preventDefault();
      const myform = document.getElementById('form');
      $.ajax({
        method: 'POST',
        url: 'http://localhost:80/login',
        xhrFields: {
          withCredentials: true // 允许在跨域请求中使用cookie
        },
        data: {
          username: myform.elements['username'].value,
          password: myform.elements['password'].value,
        },
        success: function(res) {
          console.log(res);
        } ,
        err: function(err) {
          console.log(res);
        }
      })
    })


这里还是不能用document.cookie跨域获取cookie值,只是访问接口时,浏览器会设置这个接口域下cookie

这样就行了, 不过如果设置sameSite为"null",secure必须为true,也就是采用https协议访问,这是出于安全考虑的。(对于跨域访问,推荐使用JWT认证)

cookie可以存储什么?

  1. 用户偏好设置:例如语言、主题、字体大小等。
  2. 购物车信息:例如商品编号、数量、价格等。
  3. 用户行为记录:例如浏览历史、搜索记录、点击记录等。
  4. 用户登录状态:例如记住登录状态、自动登录等。
  5. 浏览器识别信息:例如浏览器类型、版本、屏幕分辨率等。

JWT认证

import express from 'express'
import jwt from 'jsonwebtoken'
const app = express()
// 设置 JWT 密钥
const secretKey = 'mysecretkey'

// 验证 JWT 中间件
function verifyJWT(req, res, next) {
  const token = req.headers.authorization.split(' ')[1];

  if (!token) {
    return res.status(401).json({ message: '无效的 Token' });
  }

  // 验证 JWT
  jwt.verify(token, secretKey, (err, decoded) => {
    if (err) {
      return res.status(401).json({ message: '无效的 Token' });
    }

    req.user = decoded;
    next();
  });
}




// 登录路由
app.post('/login', (req, res) => {
  // 获取用户名和密码
  const { username, password } = req.body

  // 验证用户名和密码是否正确
  if (username === 'admin' && password === 'admin123') {
    // 生成 JWT Token
    const token = jwt.sign({ username }, secretKey, { expiresIn: '1h' })

    // 返回 Token
    res.json({ token })
  } else {
    // 返回错误信息
    res.status(401).json({ message: '用户名或密码错误' })
  }
})

在session认证里,签名并且验证是一个隐式的过程,但在jwt认证里,签名和验证是手动的

    // 签名
    jwt.sign({signObj}, secretKey, {options});
    // 验证
    jwt.verify(token, secretKey, (err, decoded)=>{
    })