文章目录
大佬们👦好,前后端身份是如何认证的?为什么你登录了一次之后再访问,会显示你在登录? 可能这就是
session
和token
的魅力。请慢慢来了解;希望多多支持!🕢 🔥
前后端的身份验证
身份认证
通过一定的手段,完成对用户身份的确认。
不同开发模式下的身份认证
- 服务端渲染推荐 Session 认证机制
- 前后端分离推荐使用 JWT 认证机制
Session 认证机制
-
http 协议的无状态性
http 协议,指的是客户端的每次 http 请求都是独立的,连续多个请求之间没有直接关系,服务器不会主动保留每次 http 请求的状态。
-
Cookie
Cookie 是存储在用户浏览器中的一段不超过 4kb 的字符串,它有一个名称、一个值和其他用于控制 Cookie 有效期、安全期、使用范围的可选属性组成。
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送给服务器
特性:
- 自动发送
- 域名独立
- 过期时限
- 4kb 限制
Cookie 不具备安全性,可以伪造,因此,不建议服务器将重要的隐私数据,通过 Cookie 的形式发送
在 express 使用 Session 认证
- 安装 express-session 中间件
npm i express-session
- 配置 express-session 中间件
// 导入
const session = require("express-session");
// 配置中间件
app.use(
session({
secret: "keyboard cat", //secret 属性的值可以是任意字符串
resave: false, // 固定写法
saveUninitialized: true, // 固定写法
})
);
- 使用 session
const express = require("express");
const app = express();
// 导入
const session = require("express-session");
const port = 3000;
// 配置
app.use(
session({
secret: "itheima",
resave: false,
saveUninitialized: true,
})
);
// api接口
app.post("/api/login", function (req, res) {
// 判断用户登录信息是否正确
if (req.body.username !== "admin" || req.body.password !== "00000") {
// 失败
return res.send({ status: 1, msg: "登录失败" });
}
// 成功
req.session.user = req.body;
req.session.isLogin = true;
});
// 从session中取数据
// 获取username的接口
app.get("/api/username", (req, res) => {
if (!req.session.isLogin) {
// 失败
return res.send({ status: 1, msg: "fail" });
}
// 成功
res.send({
status: 1,
msg: "success",
username: req.session.user.username,
});
});
app.listen(port, () => {
console.log(`server port running at ${port}`);
});
JWT 认证机制
Session 需要配合 Cookie 才能实现,由于 cookie 不支持跨域,当涉及到前端跨域请求后端接口的时候,需要做很多额外的配置,才能实现跨域 Session 认证。
注意:
- 当前段请求后端接口不存在跨域问题的时候,推荐使用 Session 身份认证机制。
- 当前端需要
跨域
请求后端接口时候,推荐使用 JWT 人生机制。
组成部分
Header(头部)、Payload(有效负荷)、Signature(签名)
格式
Header.Payload.Signature;
Payload 部分才是真正的用户信息,它是用户信息经过加密之后生成的字符串
Header 和 Signature 是安全性相关的部分,知识为了保障 Token 的安全性
使用方式
客户端收到服务器返回的 JWT 之后,通常会将它存储在 localStorage
或 sessionStorage
中;
此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串。从而进行身份验证,推荐的做法是把 JWT 方在 http 请求头的 Authorization
字段中
格式
Authorization:Bearer <token>
安装 JWT 相关的包
npm i jsonwebtoken express-jwt
jsonwebtoken: 用于生成 JWT 字符串
express-jwt:用于将 JWT 字符串解析还原成 JSON 对象
// 导入
const jwt = require("jsonwebtoken");
const expressJWT = require("express-jwt");
定义 secret 秘钥
- 为了保证 JWT 字符串的安全性,防止 JWT 字符串在网络传输过程中被别人破解,需要定义一个用于加密和解密的 secret 秘钥
- 当生成 JWT 字符串时,需要使用 secret 对用户信息进行加密。
- 当把字符串解析还原成 JSON 对象时,需要用 secret 秘钥解密
// 定义secret秘钥
const secretKey = "itheima No1";
在登录后生成 JWT 字符串
调用 jsonwebtoken 包提供的 sign()方法,将用户的信息加密成 JWT 字符串,响应给客户端。
参数:
- 用户信息对象
- 加密秘钥
- 配置对象
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, {
expiresIn: "30s",
});
接口
app.post("/api/login", (req, res) => {
res.send({
status: 200,
message: "success",
token: tokenStr,
});
});
将 JWT 字符串还原为 JSON 对象
客户端每次在访问那些有权限接口时,都要主动通过请求头中的 authorization 字段,将 token 字符串发送到服务器进行身份验证。
此时,服务器可以通过 express-jwt 这个中间件,自动将客户端发送过来的 token 解析还原成 JSON 对象
// 使用app.use()注册中间件
// expressJWT({secret:secretKey}) 用来解析token中间件
// .unless({path:[/^\/api\//]}) 用来指定哪些接口不需要访问权限
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }));
使用 token
const express = require("express");
const app = express();
const port = 3000;
// 导入
// 加密
const jwt = require("jsonwebtoken");
// 解密
// ****** express-jwt 要安装7.0版本以下的 不然会报错 建议 安装6.1.1
const expressJWT = require("express-jwt");
// 定义secret秘钥
const secretKey = "itheima No1";
// 注册express-jwt中间件
app.use(
// algorithms: ["HS256"] 防止express-jwt版本过高报错
expressJWT({ secret: secretKey, algorithms: ["HS256"] }).unless({
path: [/^\/api\//],
})
);
// 解析 post 表单数据的中间件
const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));
// 允许跨域
const cors = require("cors");
app.use(cors());
// 登录接口
app.post("/api/login", function (req, res) {
// 将 req.body 请求体中的数据,转存为 userinfo 常量
const userinfo = req.body;
// 登录失败
if (userinfo.username !== "admin" || userinfo.password !== "000000") {
return res.send({
status: 400,
message: "登录失败!",
});
}
// 登录成功
// TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
// 参数1:用户的信息对象
// 参数2:加密的秘钥
// 参数3:配置对象,可以配置当前 token 的有效期
// 记住:千万不要把密码加密到 token 字符中
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, {
expiresIn: "100s",
});
res.send({
status: 200,
message: "登录成功!",
token: tokenStr, // 要发送给客户端的 token 字符串
});
});
app.post("/admin/getinfo", function (req, res) {
// token会直接挂载到req的user属性上
console.log(req.user);
res.send({
status: 200,
message: "获取成功",
data: req.user,
});
});
// 捕获失败的中间件
app.use((err, req, res, next) => {
// 这次错误是由 token 解析失败导致的
if (err.name === "UnauthorizedError") {
return res.send({
status: 401,
message: "无效的token",
});
}
res.send({
status: 500,
message: "未知的错误",
});
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
捕获解析 jwt 失败后产生的错误
如果客户端发过来的 token 自飞船过期或不合法,会产生一个解析错误,可以通过express的错误中间件
,捕获错误并处理
app.use((err, req, res, next) => {
if (err.name == "UnauthorizedError") {
return res.send({
status: 401,
message: "无效的token",
});
}
// 其他原因
res.send({ status: 500, message: "未知错误" });
});
🔥🔥🔥但行前路,不负韶华!努力学习👍 🔥🔥