1. Cookie&Session
「HTTP 无状态」我们知道,HTTP 是无状态的。也就是说,HTTP 请求方和响应方间无法维护状态,都是一次性的,它不知道前后的请求都发生了什么。但有的场景下,我们需要维护状态。最典型的,一个用户登陆微博,发布、关注、评论,都应是在登录后的用户状态下的。「标记」那解决办法是什么呢?
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", resave: true, saveUninitialized: true, cookie: { maxAge: 1000 * 60 * 10, secure: false, }, rolling: true, 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)介绍
我为什么要保存这可恶的session呢, 只让每个客户端去保存该多好?
当然, 如果一个人的token 被别人偷走了, 那我也没办法, 我也会认为小偷就是合法用户, 这其实和一个人的session id 被别人偷走是一样的。
这样一来, 我就不保存session id 了, 我只是生成token , 然后验证token , 我用我的CPU计算时间获取了我的session 存储空间 !
解除了session id这个负担, 可以说是无事一身轻, 我的机器集群现在可以轻松地做水平扩展, 用户访问量增大, 直接加机器就行。 这种无状态的感觉实在是太好了!
缺点:
- 占带宽,正常情况下要比 session_id 更大,需要消耗更多流量,挤占更多带宽,假如你的网站每月有 10 万次的浏览器,就意味着要多开销几十兆的流量。听起来并不多,但日积月累也是不小一笔开销。实际上,许多人会在 JWT 中存储的信息会更多;
- 无法在服务端注销,那么久很难解决劫持问题;
- 性能问题,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
| 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
| app.use((req,res,next)=>{ if(req.url==="/login"){ next() return; }
const token = req.headers["authorization"].split(" ")[1] if(token){ var payload = JWT.verify(token) 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
| 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
|
import axios from 'axios'
axios.interceptors.request.use(function (config) { const token = localStorage.getItem("token") config.headers.Authorization = `Bearer ${token}`
return config; }, function (error) { return Promise.reject(error); });
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); });
|