token验证概述及在React中实现token验证
Posted 橘猫吃不胖~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了token验证概述及在React中实现token验证相关的知识,希望对你有一定的参考价值。
token验证概述及在React中实现token验证
1 什么是token
token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。当用户第一次登录后,服务器生成一个token并将此token返回给客户端,
以后客户端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。
简单token的组成:①uid(用户唯一的身份标识);②time(当前时间的时间戳);③sign(签名,token的前几位以哈希算法压缩成的一定长度的十六进制字符串。为防止token泄露)。
页面请求的控制:
①第一次登录时,需要用户名和密码。登录成功后服务器端会生成token标识,该标识返回给客户端
②当用户再次发起请求时,只需要带上token标识即可,不需要用户名和密码
2 token的作用
- 防止表单重复提交。主要的理念是,客户端初始化的时候(一般就是刚刚进入页面的时候)就调用后端代码,后端代码生成一个token,返回给客户端,客户端储存token(可以在前台使用Form表单中使用隐藏域来存储这个Token,也可以使用cookie),然后就将request(请求)中的token 与(session)中的token进行比较
- 用来作身份验证。
(1)身份认证概述
由于HTTP是一种没有状态的协议,它并不知道是谁访问了我们的应用。这里把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下次这个客户端再发送请求时候,还得再验证一下。
通用的解决方法是:当用户请求登录的时候,如果没有问题,在服务端生成一条记录,在这个记录里可以说明登录的用户是谁,然后把这条记录的id 发送给客户端,客户端收到以后把这个id存储在cookie里,下次该用户再次向服务端发送请求的时候,可以带上这个cookie,这样服务端会验证一下cookie里的信息,看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。以上所描述的过程就是利用session,那个id值就是sessionid。我们需要在服务端存储为用户生成的session,这些session 会存储在内存,磁盘,或者数据库。
(2)基于token机制的身份认证:使用token机制的身份验证方法,在服务器端不需要存储用户的登录记录。流程如下:
A. 客户端使用用户名和密码请求登录。
B. 服务端收到请求,验证用户名和密码。
a. 在数据库中存放用户的信息(用户名、密码)
b. 使用客户端提交的用户名、密码查询数据,若查询到了结果则表示是合法的用户,若没有查询到结果则是非法用户
C. 验证成功后,服务端会生成一个token,然后把这个token 发送给客户端。
D. 客户端收到token后把它存储起来,可以放在cookie或者Local Storage(本地存储)里。
E. 客户端每次向服务端发送请求的时候都需要带上服务端发给的token。
F. 服务端收到请求,然后去验证客户端请求里面带着token,如果验证成功,就向客户端返回请求的数据。
3 React中实现token验证
接下来使用登录和注册的案例来说明。创建Express项目server1,在当前项目下实现后端;创建React项目demo1,在当前项目下配置前端。
数据库说明
本案例使用stu数据库中的admin表来存放用户名以及密码,stu表结构如下:
后端说明
首先安装一些模块:
# 跨域模块
npm install cors
# crypto模块,是一个加密模块,使用摘要算法(MD5)进行加密
npm install crypto
# jsonwebtoken模块:生成token信息
npm install jsonwebtoken
1、配置数据库。在当前项目下新建config文件夹,创建dbconfig.js文件,该文件用来连接mysql数据库,具体代码如下:
var Sequelize = require("sequelize");
// 参数分别是:数据库名、用户名、密码
const DB = new Sequelize("info", "root", "123456",
host: "localhost", // 主机地址
port: 3306, // 数据库端口号
dialect: "mysql", // 数据库类型
pool: // 数据库连接池
max: 5, // 最大连接数量
min: 0, // 最小连接数量
idle: 10000, // 如果10秒内没有被使用,释放该线程
)
module.exports = DB;
2、创建模型。在当前项目下新建文件夹Model,创建adminModel.js文件,该文件用来将数据库中的表映射到对象中,具体代码如下:
const DB = require("../config/dbconfig");
const Sequelize = require("sequelize");
const adminModel = DB.define("admin",
id:
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
unique: true
,
username:
type: Sequelize.STRING(30),
allowNull: false
,
password:
type: Sequelize.STRING(255),
allowNull: false
,
freezeTableName: true,
timestamps: false
)
module.exports = adminModel;
3、在bin文件夹下的www.js文件中修改端口号为8089,具体为:
// 原代码
var port = normalizePort(process.env.PORT || '3000');
// 修改端口号后
var port = normalizePort(process.env.PORT || '8089');
4、在routes文件夹下新建文件admin.js,在该文件中写注册和登录的接口,具体代码如下:
var express = require("express");
var router = express.Router(); // 使用路由模块化管理
var crypto = require("crypto"); // 导入加密模块
var jwt = require("jsonwebtoken");
var adminModel = require("../Model/adminModel"); // 导入数据库模型
/*
* 注册:http://localhost:8089/admin/register
* 步骤如下:
* (1)获取用户的用户名和密码
* (2)创建MD5摘要算法的对象,利用该对象对密码进行加密
* (3)将加密后的密码保存到数据库中
*/
router.post("/register", (req, res) =>
let username = req.body.username; // 获取用户名
let password = req.body.password; // 获取密码
// 创建MD5对象
let md5 = crypto.createHash("md5");
// 对密码进行加密,"hex"表示密码是十六进制的字符串
let newPwd = md5.update(password).digest("hex");
// 将用户名和密码保存到数据库中
adminModel.create(
username: username,
password: newPwd
).then(result => // 创建成功后传递的数据
res.json(
code: 1000,
msg: "注册成功"
)
).catch(err =>
console.log(err);
res.json(
code: 1002,
msg: "注册失败"
)
)
)
/*
* 登录:http://localhost:8089/admin/login
* 步骤如下:
* (1)获取用户输入的用户名和密码
* (2)使用MD5加密用户输入的密码
* (3)将用户名与加密后的密码与数据库中的用户名和密码进行对比
* (4)对比成功,则是合法用户,生成token,然后将token和其他的信息一起打包给客户端
* (5)对比不成功,非法用户,不生成token,相应给客户端的信息不包含token
*/
router.post("/login", (req, res) =>
// 在服务器端以对象的方式将用户名和密码接收
let user = req.body.user;
// 获取对象中的用户名和密码
let username = user.username;
let password = user.password;
// 创建MD5对象
let mds = crypto.createHash("md5");
// 对密码进行加密,密码是十六进制的字符串
let newPwd = mds.update(password).digest("hex");
// 查询
adminModel.findAll(
where: // 查找用户名
username: username
).then(data => // 用户名可能相同,因此可能查找多条记录,这时data是一个数组,因此在设计注册时,必须保证用户名不重复
if (data.length !== 0) // 查询到了数据,用户名存在
if (data[0].password == newPwd) // 密码相同
// 合法用户,生成token
// jwt.sign()传入要生成token信息的对象,其中的"jmcbp"可以让token信息更加难以破解
let newToken = jwt.sign(...data[0], "jmcbp",
expiresIn: 1440 // token的过期时间
)
// 将token和其他信息打包后相应给客户端
res.json(
code: 1000,
msg: "登录成功",
token: newToken
)
else // 密码不相同
res.json(
code: 1002,
msg: "密码错误"
)
else // 没有查询到数据
res.json(
code: 1002,
msg: "用户不存在"
)
)
)
module.exports = router;
5、在app.js文件中配置跨域模块和路由模块:
// 跨域
var cors = require("cors");
app.use(cors());
// 路由
var adminRouter = require('./routes/admin');
app.use('/admin', adminRouter);
在Postman中测试这两个接口,首先是注册接口:
登录接口,由于登录接口接收的参数是对象形式的,因此使用JSON格式传递参数,首先在Headers中设置Content-Type为application/json:
然后选择Body,选择raw,修改JSON,传入参数即可
前端说明
首先安装模块
npm install react-router-dom@5.2.0
npm install antd
1、在src文件夹下新建文件夹components,在该文件夹下新建文件admin.js,在该文件中使用antd制作一个简易的登录和注册的界面,具体代码如下:
import React from "react";
import Form, Row, Button, Input, message from "antd";
import axios from "axios";
class Login extends React.Component
constructor(props)
super(props);
this.state =
username: " ",
password: " "
loginFormRef = React.createRef(); // 与render函数中的登录Form组件进行绑定
regFormRef = React.createRef(); // 与render函数中的注册Form绑定
login = async () => // 点击登录后执行的函数
// 对表单控件进行规则验证:验证设置了rules属性的控件
await this.loginFormRef.current.validateFields().then(value =>
// 验证成功
// value存放了通过验证的控件的值
this.setState( // 将用户名和密码更新为输入的值
username: value.username,
password: value.password
)
// 向服务器发起登录请求
axios.post("http://localhost:8089/admin/login", user: value)
.then(result =>
if (result.data.code == 1000) // 登录成功
// 将用户名写入sessionStorage中,这可以制作用户名显示在页面上的效果
sessionStorage.setItem("username", value.username);
// 将token写入
sessionStorage.setItem("token", result.data.token);
// 进行页面的跳转,/home是自定义的跳转路由,在本例中不定义
this.props.history.push("/home");
else // 登录失败
message.error(result.data.msg);
return;
).catch(err =>
console.log(err);
)
)
register = async () =>
await this.regFormRef.current.validateFields().then(value =>
axios.post("http://localhost:8089/admin/register", value)
.then(result => // 注册成功
// 给出消息提示
message.success(result.data.msg);
// 接下来可以让页面跳转到登录页面,由于本案例两个页面在一起,因此这句话不使用
// this.props.history.push("/");
).catch(err =>
console.log(err);
)
)
render()
return (
<div>
<div style=margin: "10px 20px", float: "left">
<Form ref=this.loginFormRef>
<Row>
<h2>用户登录</h2>
</Row>
<Row>
<Form.Item label="用户名:" name="username" rules=[
required: true,
message: "用户名不能为空"
]>
<Input placeholder="用户名"/>
</Form.Item>
</Row>
<Row>
<Form.Item label="密 码:" name="password" rules=[
required: true,
message: "密码不能为空"
]>
<Input.Password placeholder="密码"/>
</Form.Item>
</Row>
<Row>
<Button type="primary" onClick=this.login>登录</Button>
</Row>
</Form>
</div>
<div style=margin: "10px 20px", float: "left">
<Form ref=this.regFormRef>
<Row>
<h2>用户注册</h2>
</Row>
<Row>
<Form.Item label="用户名:" name="username" rules=[
required: true,
message: "用户名不能为空"
]>
<Input placeholder="用户名"/>
</Form.Item>
</Row>
<Row>
<Form.Item label="密 码:" name="password" rules=[
required: true,
message: "密码不能为空"
]>
<Input.Password placeholder="密码"/>
</Form.Item>
</Row>
<Row>
<Form.Item label="确认密码:" name="confirmPwd" rules=[
// 两个验证要求,因此两个对象
required: true,
message: "确认密码不能为空"
,
validator: (rules, value) =>
// 获取用户输入的密码
let pwd = this.regFormRef.current.getFieldValue("password");
if (pwd && pwd !== value)
// value表示当前控件的值
return Promise.reject("两次密码不一致");
else
return Promise.resolve(); // 表示验证通过
]>
<Input.Password placeholder="密码"/>
</Form.Item>
</Row>
<Row>
<Button type="primary" onClick=this.register>注册</Button>
</Row>
</Form>
</div>
</div>
)
export default Login;
2、上面可以实现在登录后跳转到主页面的效果,但是当用户直接输入主页面的路由地址,就会跳过登录过程直接进入主页面,因此我们还需要自定义路由组件,判断用户是否登录。在src目录下新建文件夹routers,并在其中新建文件privateRouter.js,具体代码如下:
import Route, Redirect from "react-router-dom";
import Component from "react";
// 定义鉴权函数
let authenticate = () =>
// 获取页面中存储的token
let token = sessionStorage.getItem('token');
// 根据是否存在token,返回不同的值
return token ? true : false
// 定义路由组件
// ...rest将传给组件的所有参数全部解析出来
const PrivateRoute = (component: Component, ...rest) =>
return 以上是关于token验证概述及在React中实现token验证的主要内容,如果未能解决你的问题,请参考以下文章
使用 Microsoft System.IdentityModel.Tokens.Jwt 在 Asp.net WebApi 中实现 JWT 身份验证