前端登录凭证
1. 为什么需要登录凭证?
-
web开发中,我们使用最多的协议是
http
,但是http
是一个无状态的协议。- 无状态的协议?什么叫做无状态协议呢?
-
举个例子:
-
我们登录了一个网站www.yyds.com (当然这个网站不存在);
-
登录的时候我们需要输入用户名和密码:
比如用户名 fct,密码:fct666.;
-
登录成功之后,我们要以 fct 的身份去访问其他的数据和资源,还是通过http请求去访问。
yyds
的服务器会问:你谁呀?- fct 说:我是 fct 呀,刚刚登录过呀;
yyds
:怎么证明你刚刚登录过呀?- fct 说:这........
http
没有告诉你吗? yyds
:http
的每次请求对我来说都是一个独立的请求,和之前请求过什么没有关系。
-
看到了吧?这就是
http
的无状态,也就是服务器不知道你上一步做了什么,我们必须得有一个办法可以证明用户之前已经登录过。
2. 常见登录凭证
-
cookie + session
-
Token 令牌
3. 认识 cookie
3.1 cookie概念
Cookie
(复数形态 Cookies ),又称为“小甜饼”。类型为“ 小型文本文件 ",某些网站为了辨别用户身份而存储在用户本地终端( Client Side )上的数据。
浏览器会在特定的情况下携带上 cookie
来发送请求,我们可以通过cookie
来获取一些信息;
3.2 cookie 分类
Cookie
总是保存在客户端中,按在客户端中的存储位置,Cookie
可以分为内存Cookie和硬盘Cookie。
- 内存Cookie由浏览器维护,保存在内存中,浏览器关闭时Cookie就会消失,其存在时间是短暂的;
- 硬盘Cookie保存在硬盘中,有一个过期时间,用户手动清理或者过期时间到时,才会被清理;
如何判断一个 cookie
是内存cookie还是硬盘cookie呢?
- 没有设置过期时间,默认情况下cookie是内存cookie,在关闭浏览器时会自动删除;
- 有设置过期时间,并且过期时间不为0或者负数的cookie,是硬盘cookie,需要手动或者到期时,才会删除;
3.3 cookie 的常见属性
3.3.1 cookie的生命周期
默认情况下的cookie是内存cookie,也称之为会话cookie,也就是在浏览器关闭时会自动被删除;
我们可以通过设置 expires 或者 max-age 来设置过期的时间:
expires
:设置的是 Date.toUTCString();设置格式是:expires=date-in-GMTString-format ;(到某个时刻cookie失效)max-age
:设置过期的秒钟;max-age=max-age-in-seconds (例如一年为60*60*24*365
);(过去多久cookie失效)
3.3.2 cookie的作用域
访问哪些域名的哪个路径时会默认携带上 cookie,称为cookie的作用域。也就是允许cookie发送给哪些URL。
-
Domain
:指定哪些主机(域名)可以接收 cookie。-
如果不指定,那么默认是 origin(即同域名请求会携带),不包括子域名;
域名为
www.fct.com
,子域名为www.music.fct.com
。 -
如果指定 Domain,则包含子域名。例如,如果设置 Domain=mozila.org ,则 Cookie 也包含在子域名中(如 develope.mozilla.org )。
-
-
Path
:指定主机下哪些路径可以接收cookie- 例如,设置 Path=/docs ,则以下地址都会匹配︰
- /docs
- /docs/Web/
- /docs/Web/HTTP
- 例如,设置 Path=/docs ,则以下地址都会匹配︰
3.4 客户端设置 cookie
// 1. 设置成键值对
// document.cookie = "key=value";
// 2. 不设置过期时间,默认是内存(会话)cookie
document.cookie = "name=fct";
// 3. 设置过期时间,5s后
document.cookie = "hobby=running;max-age=5;";
3.5 服务器设置、获取 cookie
const express = require("express");
// 下载中间件 npm i cookie-parser
const cookieParser = require("cookie-parser");
const app = express();
// 使用cookie中间件
app.use(cookieParser());
app.get("/test", (req, res, next) => {
// 设置cookie,过 10s 后 cookie 失效
res.cookie("username", "fct666", { maxAge: 50 * 1000, httpOnly: true });
res.send("服务器设置cookie");
});
app.get("/demo", (req, res, next) => {
// 获取 cookie
console.log(req.cookies);
console.log(req.cookies.username); // fct666
res.send("获取cookie");
});
app.listen(8000, () => {
console.log("服务器设置cookie的服务器启动成功!");
})
4. session
4.1 服务器端设置、获取
const express = require("express");
// 下载中间件 npm i express-session
const session = require("express-session");
const app = express();
app.use(session({
// name,返回客户端的key的名称,默认为connect.sid,也可以自己设置。
name: "session_id",
// secret,一个 String类型的字符串,作为服务器端生成session的签名。
secret: 'keyboard cat',
// resave,即使session没有被修改,也保存session值,默认为true
resave: false,
// saveUninitialized:强制未初始化的session保存到数据库
saveUninitialized: true
}));
// 设置 session
app.get("/login", (req, res, next) => {
req.session.userinfo = "付常涛";
req.session.user = { id: 123, name: "fct" };
res.send('登录成功');
});
// 获取 session
app.get("/", (req, res, next) => {
console.log(req.session.user);// { id: 123, name: 'fct' }
if (req.session.userinfo) {
res.send('你好,' + req.session.userinfo + '欢迎回来');
} else {
res.send('未登录');
}
});
app.listen(8000, () => {
console.log("设置 session 的服务器启动成功!");
})
5. session和cookie 的缺点
- Cookie 会被附加在每个HTTP请求中所以无形中增加了流量(事实上某些请求是不需要的);
- Cookie 是明文传递的,所以存在安全性的问题;
- Cookie的大小限制是 4KB ,对于复杂的需求来说是不够的;
- 对于浏览器外的其他客户端(比如 iOS、Android ),必须手动设置 cookie 和session ;
- 对于分布式系统和服务器集群中如何可以保证其他系统也可以正确的解析session ?
6. Token
由于session和cookie的缺点,在目前前后端分离的开发过程中,多使用Token进行身份验证。
token
可以翻译为令牌;- 在验证了用户账号和密码正确的情况,给用户颁发一个令牌;
- 这个令牌作为后续用户访问一些接口或者资源的凭证;
- 我们可以根据这个令牌凭证来判断用户是否有权限来访问;
Token的使用分为两部分:
- 生成 token :登录的时候,颁发token ;
- 验证 token :访问某些资源或者接口时,验证token ;
1. JWT实现Token机制
JWT(JSON WEB TOKEN)生成的 Token 由三部分组成:
1.1 header
alg
:采用的加密算法,默认是 HMAC SHA256 ( HS256),采用同一个密钥进行加密和解密(对称加密);typ
:JWT,固定值,通常都写成JWT即可;- 会通过 base64Url 算法进行编码。
1.2 payload
- 携带的数据,比如我们可以将用户的 id 和 name 放到 payload 中;
- 默认也会携带
iat
( issued at ),令牌的签发时间; - 我们也可以设置过期时间:
exp
( expiration time ) ; - 会通过 base64Url 算法进行编码。
1.3 signature
- 设置一个
secretKey
,通过将前两个的结果合并后进行 HMACSHA256 的算法; HMACSHA256(base64Url(header) + . + base64Url(payload), secretKey)
;- 但是如果
secretKey
暴露是一件非常危险的事情,因为之后就可以模拟颁发 token ,也可以解密 token ;
代码实现(使用默认HS256加密算法)对称加密:
const express = require("express");
// 引入 jsonwebtoken
const jwt = require('jsonwebtoken');
const app = express();
// 对称加密
const SECRET_KEY = "nAnafct";
// 登录,颁发 token令牌
app.get("/login", (req, res, next) => {
const user = { id: 123, name: "fct" };
const token = jwt.sign(user, SECRET_KEY, {
expiresIn: 50 // 50s 后过期
});
res.end(token);
});
// 读取token,验证token
app.get("/home", (req, res, next) => {
const authorization = req.headers.authorization;
const token = authorization.replace("Bearer ", "");
// 验证 token 是否有效
try {
const result = jwt.verify(token, SECRET_KEY);
res.send(result);// {"id": 123,"name": "fct","iat": 1638958655,"exp": 1638958705}
} catch (error) {
res.send({
status: 400,
message: "用户认证失效"
});
}
});
app.listen(8000, () => {
console.log("服务器设置cookie的服务器启动成功!");
})
2. 非对称加密
HS 256加密算法是对称加密算法。当密钥暴露时,每一个都可以颁发令牌,非常危险!因此我们可以使用非对称加密,RS 256。
- 私钥:用于发布令牌;
- 公钥:用于验证令牌;
使用OpenSSL产生私钥和公钥:
windows中使用git bash
终端窗口。mac电脑自带终端即可。
> openssl # 进入openssl
# 生成私钥
OpenSSL> genrsa -out private.key 1024
# 生成对应的公钥
OpenSSL> rsa -in private.key -pubout -out public.key
使用非对称加密:
const express = require("express");
// 引入 jsonwebtoken
const jwt = require('jsonwebtoken');
const fs = require("fs");
const app = express();
// 读取私钥,公钥
const PRIVATE_KEY = fs.readFileSync("./keys/private.key");
const PUBLIC_KEY = fs.readFileSync("./keys/public.key");
// 登录,颁发 token令牌
app.get("/login", (req, res, next) => {
const user = { id: 123, name: "fct" };
const token = jwt.sign(user, PRIVATE_KEY, {
expiresIn: 50, // 50s 后过期
algorithm: "RS256", // 指定加密算法
});
res.end(token);
});
// 读取token,验证token
app.get("/home", (req, res, next) => {
const authorization = req.headers.authorization;
const token = authorization.replace("Bearer ", "");
try {
const result = jwt.verify(token, PUBLIC_KEY, {
algorithms: ["RS256"]
});
res.send(result);
/* {
"id": 123,
"name": "fct",
"iat": 1638960914,
"exp": 1638960964
}*/
} catch (error) {
res.send({
status: 400,
message: "用户认证失效"
});
}
});
app.listen(8000, () => {
console.log("token服务器启动成功!");
})
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器