前言
在浏览器和服务端的交互中,http请求是无状态的,那在web项目中准确无误的保存用户的登录状态是前后端交互中必须要解决的问题。目前在web端解决登录认证的问题分为三种base64 session/cookie token 下面介绍下token中jwt实现登录验证的方式。
基本概念
了解下几种不同的认证方式
- base64:这种应该是早期用的比较多,适用于安全性要求不高的网站,通过对客户端的用户名/密码进行base64编码后然后放到服务器
- session/cookie:服务器会在用户登录时创建一个Session,其中包含用户的身份信息,服务器将Session ID 发送给客户端,客户端在后续请求中通过Cookie来传递该Session ID,服务器通过该Session ID 来识别用户身份。
- token:可以称为令牌,一种通用的术语,这种方式是目前使用最广泛的,他也根据不同的业务场景发展出不同的使用方法,目前常用的token方式有oauth和jwt。
- json web token(jwt):jwt是token认证的一种,它使用 JSON 对象来存储令牌的信息,并使用数字签名或加密等方式保证令牌的可靠性和安全性。jwt由三个部分组成分别为:header payload signature。
- 使用json对象存储
- 数字签名和加密
- 三个部分
- oauth(没用过简道介绍下):是一种开发标准,用于授权第三方应用程序访问用户数据,而不必想第三方应用程序公开用户凭据。例如我们常登陆一些网站可以用微信或支付宝登录,虽然登录了但是并没有使用到微信或支付宝的用户名密码。oauth一般在大型应用下有多个子应用的项目中使用比较广泛。
- 以下几种方式的对比 身份校验 | 安全性 | 实现难度 | -------------- | --- | --- | OAuth2 | 高 | 高 | JWT | 中 | 中 | base64 | 中 | 低 | Session/Cookie |低 | 低 以上是目前常用的前后端认证方式,下面我们来详细介绍jwt认证方式的实现。
jwt验证流程
- 客户端认证:一般是客户端用户输入用户名密码。
- 服务端验证身份:如果校验客户端传递的用户名密码通过,则生成jwt token返回给客户端。
- 客户端保存:客户端获取到服务端返回的jwt token,之后的每次请求中在请求头中带jwt token
- 服务端验证token:服务端收到jwt token后,解析jwt token并验证其有效性,如果验证通过则返回对应的请求操作。验证失败则返回401并重新认证。
实现
所需依赖介绍
- express-jwt:express用来解析jwt的中间件,使用方式很简单只用把其加入到请求前。express-jwt会自动将jwt的payload部分赋值给req.user
const expressJwt = require('express-jwt');
app.use(expressJwt({ secret: 'secret_key', algorithms: ['HS256'] }));
- jsonwebtoken:用来在验证身份通过后,生成jwt token
const userInfo = {name:"**",phone:"***"}
//jwt token可以存入用户信息 secret需要保密号
jwt.sign(userInfo, 'secret', { expiresIn: '**' });
使用示例
- 登录用户时验证身份
import userModel from '../../database/model/user';
import jsonwebtoken from 'jsonwebtoken';
const login =async (req,res,next)=>{
try{
const data = req.body;
const user = data?.user;
const password = data?.password;
const userInfo = await userModel.findOne({user});
//核心逻辑:当用户名密码校验成功后生成jwt
if(userInfo?.password === password){
const secrect = '';
const token = jwt.sign({ userInfo }, secrect, { algorithm: 'HS256' });
res.json({code:200,msg:"login successfully!",token})
}else{
console.log("user or password error!");
res.json({code:0,msg:"user or password error!"})
}
}catch(e){
console.log('e: ', e);
}
}
const logout = (req,res,next)=>{
try{
console.log('===========');
}catch(e){
console.log('e: ', e);
}
}
export default {login,logout}
- 客户端获取token值后放入header中
const token = "******"
request.headers['Authorization'] = `Bearer ${token}`;
- 请求时服务端校验token
import express from 'express';
import { expressjwt } from 'express-jwt'
import config from './helpper/config';
import './database/mongo';
const env = process.env.NODE_ENV || 'dev'
const app = express();
// 核心逻辑express-jwt校验token是否合法
app.use(expressjwt({ secret: config[env].secret, algorithms: ["HS256"] }).unless({path:["/user/login"]}))
app.use(express.json());
// 404路由捕获
app.get('*', function (req, res) {
console.log('Not Found');
res.sendStatus(404);
});
// 全局错误处理
app.use((err, req, res, next) => {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
});
console.log('config[env]: ', config[env]);
app.listen(config[env]?.port, () => {
console.log("serve is starting :" + config[env]?.port);
});
总结
身份校验用来保证数据能安全准确的传递。而如何能安全方便的实现身份的校验是软件开发者一直努力的方向,从最开始的session到如今的token,我们只有保持一颗积极的学习与创新才能保证在程序员这个行业中走得的更远。