Session和JWT(JSONWEBTOKEN)都是HTTP协议中用于前后端身份认证的手段,其目的都是:让服务端知晓请求者是哪个用户。
-
为什么需要认证?
因为HTTP是无状态的,这一次的请求跟上一次没有关系,那如果想让服务器知道是“我”,就必须在http请求中加入一些内容,标明我的身份。
-
如何认证?
-
Session认证:客户端会为每一个初次使用网站的用户创建一个session,并将SessionID存储在cookie中发送给客户端,客户端每次请求时都会携带Cookie,服务器端通过SessionID来查找对应的Session,获取用户的信息。
-
JWT认证是一种基于令牌的身份验证机制,它使用JSON格式来存储用户的信息和其他元数据。在用户成功登录后,服务器会生成一个JWT令牌并将其发送给客户端。客户端在后续的请求中都会携带该JWT令牌,并通过JWT的签名来验证用户的身份。
-
-
各自的优缺点
-
跨域
session认证一般采用cookie,cookie一般是不支持跨域访问的,也就是说,如果客户端与服务端不同源,session认证就不起作用了。(可以设置允许跨域,看下面)
-
安全性
session的用户认证信息存储在服务端,而jwt存储在客户端。从这点来看,session认证要比jwt认证更安全。即使用户窃取了sessionID,可以假冒用户发起请求,但也没有获取到用户具体信息。
-
开销
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>][; ...]
也就是说,我们是可以将其他信息保存在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可以存储什么?
- 用户偏好设置:例如语言、主题、字体大小等。
- 购物车信息:例如商品编号、数量、价格等。
- 用户行为记录:例如浏览历史、搜索记录、点击记录等。
- 用户登录状态:例如记住登录状态、自动登录等。
- 浏览器识别信息:例如浏览器类型、版本、屏幕分辨率等。
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)=>{
})