前言
前后端鉴权是保障系统安全性和数据隐私的重要环节
前端鉴权主要侧重于在用户界面上进行初步的验证和授权操作
后端鉴权主要是对前端传来的身份验证信息进行严格的验证和授权
本文分别讲解一下前后端的鉴权
完成效果
-
Token不存在
-
Token不正确
前端鉴权(路由守卫)
前端鉴权主要是防君子不防小人
当用户访问需要登录权限的页面地址时,前端判断本地是否存在用户信息,如果存在,则直接跳转页面,否则跳转登录页面
这里我们通过为vue项目添加路由守卫去实现这个效果
js
const whiteList = ["/login", "/register"];
router.beforeEach((to, from, next) => {
// 如果是白名单的路由就不需要检查本地存储是否有用户数据,其他路由需要有用户数据,否则跳转登录
if (whiteList.includes(to.path)) {
next();
} else {
if (localStorage.getItem("userInfo")) {
next();
} else {
next("/login");
}
}
});
以下是对这段代码的分析:
- 定义了一个白名单数组
whiteList
,其中包含了不需要检查用户登录状态的路由路径。 - 使用
router.beforeEach()
方法注册了一个全局前置守卫,它会在每次路由跳转前被调用。 - 在守卫函数中,首先判断当前要跳转的路由是否在白名单中。
- 如果在白名单中,则直接允许路由跳转,不需要检查用户登录状态。
- 如果不在白名单中,则检查本地存储
localStorage
是否存在名为"userInfo"
的数据。 - 如果存在
"userInfo"
数据,则允许路由跳转。 - 如果不存在
"userInfo"
数据,则强制跳转到/login
路由,要求用户登录。
效果展示:
可以看到效果为我们在没有登陆的状态下,url地址输入其他的url地址,我们就会自动跳到登录页面
但是这里有一个缺陷,我们只是通过去判断本地存储是否有userInfo数据,有就放行,这是不严谨的,因为正常用户也许确实可以防止,但是如果是程序员自己手动在本地存储放入了userInfo数据,这就会导致这个拦截失效
可见这种方式只能防止普通用户,如果要加强防护接下来就要使用到后端鉴权了
后端鉴权(Token)
后端鉴权则更为关键和复杂,本文的后端采用的是Node.js
后端需要对前端传来的身份验证信息进行严格的验证和授权。这包括验证令牌的有效性、检查用户的权限级别,以确定用户是否有权访问特定的资源或执行特定的操作
这里我们做一个简单的小demo
根据前端传过来的数据,生成一个jwt,然后要求前端在访问其他需要鉴权的请求时需要带上这个token
首先我们需要安装生成jwt的依赖
js
npm i jsonwebtoken
接下来封装一个方法用于专门生成jwt
- jwt.js
js
const jwt = require('jsonwebtoken');
function sign(option){
return jwt.sign(option, 'secret', { expiresIn: '24' });
}
module.exports = {
sign
}
然后在登录接口中成功验证完账户和密码之后添加这个jwt的生成,并将jwt返回给前端
js
const { sign } = require("../utils/jwt.js");
let data = {
id: res[0].id,
nickname: res[0].nickname,
username: res[0].username,
};
let token = sign(data);
ctx.body = {
code: 8000,
msg: "登录成功",
data,
token: token,
};
-
从
../utils/jwt.js
文件中导入sign
函数,该函数用于生成 JWT 令牌。 -
从数据库查询到的用户信息中,提取出用户的
id
、nickname
和username
等关键信息,存储在data
对象中。 -
调用
sign
函数,使用data
对象作为载荷,生成一个 JWT 令牌,并存储在token
变量中。 -
在响应数据中,返回以下信息:
code
: 返回状态码,这里是8000
msg
: 返回信息,这里是 "登录成功"data
: 用户信息对象token
: 生成的 JWT 令牌
现在我们打印查看我们生成到的token
可以看到前端也同样拿到了token
在之后的请求中我们就需要在后端添加一个拦截器,对于需要鉴权的请求都进行拦截,同时拿到token去进行权限的鉴定,只有token是合法的且有效的,才能够放行请求
继续我们代码的书写
现在我们就需要一个拦截器去在每次发送请求时带上Token
- @api/index.js
js
// 添加请求拦截器
axios.interceptors.request.use(function (req) {
// 在发送请求之前添加token
const userInfo = localStorage.getItem("userInfo");
if (userInfo) {
const UserInfo = JSON.parse(userInfo);
req.headers.Authorization = UserInfo.token;
}
return req;
});
既然有生成Token的方法,我们就需要解开Token的方法
- jwt.js
js
// 验证token
function verify() {
return (ctx, next) => {
const token = ctx.header.authorization;
if (token) {
console.log(token);
// 验证token合法性
try {
const dexoded = jwt.verify(token, "secret");
console.log(dexoded, "////");
if (dexoded.id) {
next();
}
} catch (err) {
ctx.body = {
code: 401,
msg: "token失效",
data: "error",
};
}
} else {
ctx.body = {
code: 401,
msg: "token未提供",
data: "error",
};
}
};
}
-
定义了一个
verify
函数,它返回一个中间件函数。 -
在中间件函数中,首先从请求头中获取
authorization
字段,该字段通常包含了 JWT 令牌。 -
如果
authorization
字段存在,则将其打印到控制台进行验证。 -
接下来,使用
jwt.verify()
函数验证 JWT 令牌的合法性。该函数需要传入两个参数:token
: 要验证的 JWT 令牌"secret"
: 用于签名 JWT 令牌的密钥
-
如果 JWT 令牌验证成功,则说明用户已经登录,可以允许访问后续的资源。此时,中间件会调用
next()
函数,让请求继续向下执行。 -
如果 JWT 令牌验证失败,则说明令牌已经失效或不合法。此时,中间件会返回一个 HTTP 状态码为 401 的响应,表示未授权访问。响应内容包括错误码
401
、错误信息"token失效"
和错误数据"error"
。 -
如果
authorization
字段不存在,则说明用户没有提供 JWT 令牌。此时,中间件会返回一个 HTTP 状态码为 401 的响应,表示未授权访问。响应内容包括错误码401
、错误信息"token未提供"
和错误数据"error"
。
将方法添加到需要使用Token的接口中
js
// 测试token
router.post("/home", verify(), async (ctx) => {
ctx.body = {
code: 8000,
msg: "访问成功",
data: "succeed",
};
});
-
定义了一个 POST 请求路由
/home
。 -
在路由处理程序中,首先调用了
verify()
中间件函数。这个中间件函数会验证请求头中是否包含有效的 JWT 令牌。 -
如果 JWT 令牌验证成功,则允许访问该路由,并返回一个包含以下内容的响应:
code
: 返回状态码,这里是8000
msg
: 返回信息,这里是 "访问成功"data
: 返回数据,这里是 "succeed"
-
如果 JWT 令牌验证失败,则中间件函数会返回一个包含错误信息的响应,阻止访问该路由。
只有经过身份验证的用户,才能访问这个 /home
路由并获取相应的数据
这样后端就成功的通过验证Token来判断用户的状态
由于当Token失效或者没有的时候会返回401的状态码
这个时候前端收到了401的状态码就说明Token异常了,就需要跳转到登录页面去重新登陆
继续修改前端代码,为其添加一个响应拦截器
js
// 添加响应拦截器
axios.interceptors.response.use(function (res) {
if (res.status !== 200) {
// 程序错误
console.log(res.status);
showToast("服务器异常");
return Promise.reject(res);
} else {
console.log(res.status);
if (res.data.code === 401) {
showToast("登录过期,正在跳转至登录页···");
setTimeout(() => {
router.push("/login");
}, 1500);
return Promise.reject(res);
}
if (res.data.code !== 8000) {
// 业务逻辑错误
showToast(res.data.msg);
return Promise.reject(res);
}
return res.data;
}
});
- 使用
axios.interceptors.response.use()
方法注册一个响应拦截器。 - 在响应拦截器中,首先检查 HTTP 状态码是否为 200。如果不是 200,则说明程序出现了错误,会在控制台输出状态码,并弹出一个吐司提示 "服务器异常"。之后会直接 reject 这个响应,不再执行后续的业务逻辑。
- 如果 HTTP 状态码为 200,则进一步检查响应数据中的
code
字段。 - 如果
code
值为 401,则说明用户的登录凭证已经过期。此时会弹出一个吐司提示 "登录过期,正在跳转至登录页···",并在 1.5 秒后跳转到登录页面。之后会直接 reject 这个响应,不再执行后续的业务逻辑。 - 如果
code
值不为 8000,则说明存在业务逻辑错误。此时会弹出一个吐司提示,显示响应数据中的msg
字段。之后会直接 reject 这个响应,不再执行后续的业务逻辑。 - 如果
code
值为 8000,则说明请求成功,可以直接返回响应数据中的data
字段。
最终我们就成功的实现了效果
总结
本文讲解了基础的前后端鉴权方式,分析了他们的差异性以及重要性
希望本文对你能够有所帮助!!!!