JWT是什么
json web token,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准
基于token的鉴权机制也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程
- 用户使用用户名密码来请求服务器
- 服务器进行验证用户的信息
- 服务器通过验证发送给用户一个token
- 客户端存储token,并在每次请求时附送上这个token值
- 服务端验证token值,并返回数据
Token 和 JWT 的区别
相同:
-
都是访问资源的令牌
-
都可以记录用户的信息
-
都是使服务端无状态化
-
都是只有验证成功后,客户端才能访问服务端上受保护的资源
-
区别:
-
Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
-
JWT: 将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
常见的前后端鉴权方式
- Session-Cookie
- Token 验证(包括 JWT,SSO)
- OAuth2.0(开放授权)
egg-jwt后端实现
安装插件
npm i egg-jwt
配置插件
// /config/plugin.js
jwt : {
enable: true,
package: "egg-jwt"
}
// /config/config.default.js
config.jwt = {
secret: "tara" //加密字符串,自定义任意的字符串会自动和用户的账号密码进行融合进行加密,解密时使用该字符串解密
};
async denglu() {
// 验证验证码是否正确,取出储存的验证码信息
let ver = this.ctx.cookies.get("verif");
let ziduan = this.ctx.request.body;
if (ziduan.verif != ver) {
this.ctx.body = { info: "验证码输入有误", code: 4001 };
return;
}
//查询账号
let sqlselect = `SELECT phonenumber FROM user WHERE phonenumber=${ziduan.phonenumber}`;
let sqlselres = await this.app.mysql.query(sqlselect);
//返回的是数组,数组取值取到了,表示数据库中有该账号,没有取到值表示未注册
if (sqlselres[0]) {
//账号存在,查询密码并判断前端用户输入的是否一致匹配
let sqlselpwd = `SELECT * FROM user WHERE phonenumber=${ziduan.phonenumber} AND pwd="${ziduan.pwd}"`;
let sqlselpwdres = await this.app.mysql.query(sqlselpwd);
if (sqlselpwdres[0]) {
// 账号和密码都匹配
// 把账号密码加密为暗文
let phonenumber = ziduan.phonenumber;
// 生成token
const token = this.app.jwt.sign({ phonenumber: phonenumber }, this.app.config.jwt.secret);
// 将token码发送给前端
this.ctx.body = { info: '登录成功', code: 2003, token }
}else{
this.ctx.body = { info: '密码错误', code: 4003}
}
}else{
this.ctx.body = { info: "账号不存在", code: 4002 };
}
}
//this.app.config.jwt.secret就是设置的加密字符串
//{ phonenumber:phonenumber }这个对象,其属性值就是前端发送的字段即用户的账号和加密字符串融合进行加密生成token
async islogin(str) {
// console.log(str,1111);
if (!str) {
return false
} else {
// 把前端发送的token解密为原来的数据
let data = this.app.jwt.verify(str, this.app.config.jwt.secret);
console.log(data,111);
}
}
// 路由守卫
router.beforeEach((to,from,next)=>{
if(to.path=='/mycenter'){
if(localStorage.getItem('token')){
// 有token放行
next();
}else{
// 没有token,跳转到登录页面
next('/');
}
}else{
next();
}
})
//登陆成功时前端存储token
async denglu() {
if (this.isshow1.includes(false)) {
this.$alert("表单信息有误", "无法登录", {
confirmButtonText: "确定",
callback: (action) => {
this.$message({
type: "success",
message: `action: ${action}`,
});
},
});
} else {
let res = await this.$axios.post("/denglu", {
phonenumber: this.phonenumber,
pwd: this.pwd,
verif: this.verif,
});
console.log(res)
if (res.data.code == 4001) {
this.$alert("验证码输入有误", "登录不成功", {
confirmButtonText: "确定",
callback: (action) => {
this.$message({
type: "success",
message: `action: ${action}`,
});
},
});
}else if(res.data.code == 4002){
this.$alert("账号未注册", "登录不成功", {
confirmButtonText: "确定",
callback: (action) => {
this.$message({
type: "success",
message: `action: ${action}`,
});
},
});
}else if(res.data.code == 4003){
this.$alert('<strong>请重新输入密码</strong>', '密码错误', {
dangerouslyUseHTMLString: true
});
this.pwd='';
}else if(res.data.code == 2003){
// 将token存储在客户端
localStorage.setItem('token',res.data.token);
this.$router.push('/home');
}
}
}
//在需要用户相关信息的页面取出token,发送token给后端
//后端通过解密token去获取数据库中用户相关信息
async mounted() {
// 取出token
let token=localStorage.getItem('token');
// 用token请求后端的相关数据
let res=await this.$axios.get('/personinfo',{params:{token}});
console.log(res);
}