登录鉴权

登录鉴权

Voun MAX++

「HTTP 无状态」我们知道,HTTP 是无状态的。也就是说,HTTP 请求方和响应方间无法维护状态,都是一次性的,它不知道前后的请求都发生了什么。但有的场景下,我们需要维护状态。最典型的,一个用户登陆微博,发布、关注、评论,都应是在登录后的用户状态下的。「标记」那解决办法是什么呢?image-20220414095345868

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const express = require("express");
const session = require("express-session");
const MongoStore = require("connect-mongo");
const app = express();


app.use(
session({
secret: "this is session", // 服务器生成 session 的签名
resave: true,
saveUninitialized: true, //强制将为初始化的 session 存储
cookie: {
maxAge: 1000 * 60 * 10,// 过期时间
secure: false, // 为 true 时候表示只有 https 协议才能访问cookie
},
rolling: true, //为 true 表示 超时前刷新,cookie 会重新计时; 为 false 表示在超时前刷新多少次,都是按照第一次刷新开始计时。
store: MongoStore.create({
mongoUrl: 'mongodb://127.0.0.1:27017/kerwin_session',
ttl: 1000 * 60 * 10 // 过期时间
}),

})
);

app.use((req,res,next)=>{
if(req.url==="/login"){
next()
return;
}
if(req.session.user){
req.session.garbage = Date();
next();
}else{
res.redirect("/login")
}
})

2. JSON Web Token (JWT)

(1)介绍

image-20220415082822828

我为什么要保存这可恶的session呢, 只让每个客户端去保存该多好?

image-20220415083015066

当然, 如果一个人的token 被别人偷走了, 那我也没办法, 我也会认为小偷就是合法用户, 这其实和一个人的session id 被别人偷走是一样的。

这样一来, 我就不保存session id 了, 我只是生成token , 然后验证token , 我用我的CPU计算时间获取了我的session 存储空间 !

解除了session id这个负担, 可以说是无事一身轻, 我的机器集群现在可以轻松地做水平扩展, 用户访问量增大, 直接加机器就行。 这种无状态的感觉实在是太好了!

缺点:

  1. 占带宽,正常情况下要比 session_id 更大,需要消耗更多流量,挤占更多带宽,假如你的网站每月有 10 万次的浏览器,就意味着要多开销几十兆的流量。听起来并不多,但日积月累也是不小一笔开销。实际上,许多人会在 JWT 中存储的信息会更多;
  2. 无法在服务端注销,那么久很难解决劫持问题;
  3. 性能问题,JWT 的卖点之一就是加密签名,由于这个特性,接收方得以验证 JWT 是否有效且被信任。对于有着严格性能要求的 Web 应用,这并不理想,尤其对于单线程环境。

注意:

CSRF攻击的原因是浏览器会自动带上cookie,而不会带上token;

以CSRF攻击为例:

cookie:用户点击了链接,cookie未失效,导致发起请求后后端以为是用户正常操作,于是进行扣款操作;
token:用户点击链接,由于浏览器不会自动带上token,所以即使发了请求,后端的token验证不会通过,所以不会进行扣款操作;

(2)实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//jsonwebtoken 封装
const jsonwebtoken = require("jsonwebtoken")
const secret = "kerwin"
const JWT = {
generate(value,exprires){
return jsonwebtoken.sign(value,secret,{expiresIn:exprires})
},
verify(token){
try{
return jsonwebtoken.verify(token,secret)
}catch(e){
return false
}
}
}

module.exports = JWT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//node中间件校验
app.use((req,res,next)=>{
// 如果token有效 ,next()
// 如果token过期了, 返回401错误
if(req.url==="/login"){
next()
return;
}

const token = req.headers["authorization"].split(" ")[1]
if(token){
var payload = JWT.verify(token)
// console.log(payload)
if(payload){
const newToken = JWT.generate({
_id:payload._id,
username:payload.username
},"1d")
res.header("Authorization",newToken)
next()
}else{
res.status(401).send({errCode:"-1",errorInfo:"token过期"})
}
}
})

1
2
3
4
5
6
7
 //生成token
const token = JWT.generate({
_id: result[0]._id,
username: result[0].username
}, "1d")

res.header("Authorization", token)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//前端拦截
/*
* @作者: kerwin
* @公众号: 大前端私房菜
*/
import axios from 'axios'
// Add a request interceptor
axios.interceptors.request.use(function (config) {
const token = localStorage.getItem("token")
config.headers.Authorization = `Bearer ${token}`

return config;
}, function (error) {
return Promise.reject(error);
});

// Add a response interceptor
axios.interceptors.response.use(function (response) {

const {authorization } = response.headers
authorization && localStorage.setItem("token",authorization)
return response;
}, function (error) {

const {status} = error.response
if(status===401){
localStorage.removeItem("token")
window.location.href="/login"
}
return Promise.reject(error);
});

  • 标题: 登录鉴权
  • 作者: Voun
  • 创建于 : 2023-08-04 16:10:00
  • 更新于 : 2024-08-13 05:34:31
  • 链接: http://www.voun.top/2023/08/04/05login/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论