0805登录_注册_token_用户信息_退出-网络ajax请求2-react-仿低代码平台项目

文章目录

    • 1 JWT
      • 1.1 JWT结构
      • 1.2 工作流程
      • 1.3 优点
      • 1.4 缺点
      • 1.5 安全实践
      • 1.6. 适用场景
      • 1.7 JWT与OAuth2
      • **8. 示例代码(Node.js)**
    • 2 用户mock和api
    • 3 注册
    • 4 登录
    • 5 token存储
    • 6 请求拦截器设置token
    • 6 获取用户信息
    • 7 退出登录
    • 结语

1 JWT

JSON Web Token(JWT)是一种开放标准(RFC 7519),用于在各方之间安全传输信息。它通过数字签名确保数据的完整性和可信性,常用于身份验证和授权。以下是JWT的详细介绍:


1.1 JWT结构

JWT由三部分组成,用点(.)分隔:

  • Header(头部)
    包含令牌类型(typ: "JWT")和签名算法(如alg: HS256)。
    示例:{"alg": "HS256", "typ": "JWT"} → Base64Url编码后形成第一部分。
  • Payload(载荷)
    存放声明(claims),包括预定义声明(如用户ID、过期时间)和自定义数据。
    常见预定义声明:
    • iss(签发者)、exp(过期时间)、sub(主题)、aud(受众)等。
      示例:{"sub": "123", "name": "Alice", "exp": 1516239022} → Base64Url编码后形成第二部分。
  • Signature(签名)
    对前两部分的签名,防止数据篡改。算法由Header指定(如HMAC SHA256)。
    生成方式:HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

最终JWT形式:xxxxx.yyyyy.zzzzz


1.2 工作流程

  1. 用户登录:客户端发送凭证(如用户名/密码)到服务器。
  2. 生成JWT:服务器验证凭证,生成并返回JWT。
  3. 客户端存储:客户端保存JWT(通常存在localStorage或Cookie中)。
  4. 携带令牌请求:客户端在请求头中添加Authorization: Bearer <JWT>
  5. 服务器验证:服务器检查签名有效性、过期时间等,验证通过后处理请求。

1.3 优点

  • 无状态:无需服务器存储会话信息,适合分布式系统。
  • 跨域支持:适用于API网关、单页应用(SPA)等场景。
  • 灵活性:载荷可自定义扩展,传递非敏感用户信息。

1.4 缺点

  • 不可废止:令牌到期前无法强制失效,需借助黑名单或短过期时间。
  • 存储风险:客户端存储不当可能导致XSS攻击窃取令牌。
  • 信息暴露:载荷仅Base64编码,需避免存放敏感数据。

1.5 安全实践

  • 使用HTTPS:防止令牌在传输中被截获。
  • 强签名算法:如HMAC SHA256或RSA,避免弱算法(如HS256密钥过短)。
  • 合理设置过期时间:缩短令牌有效期,减少泄露风险。
  • 敏感数据加密:必要时使用JWE(JSON Web Encryption)加密载荷。

1.6. 适用场景

  • API认证:RESTful API的无状态身份验证。
  • 单点登录(SSO):跨多个系统的用户身份共享。
  • 移动端应用:减少频繁查询数据库的开销。

1.7 JWT与OAuth2

  • JWT常用作OAuth2的Bearer Token,传递用户身份和权限。
  • OAuth2定义授权流程,JWT是实现令牌的一种方式。

8. 示例代码(Node.js)

const jwt = require('jsonwebtoken');// 生成JWT
const token = jwt.sign({ userId: 123, role: 'admin' },'your-secret-key',{ expiresIn: '1h' }
);// 验证JWT
jwt.verify(token, 'your-secret-key', (err, decoded) => {if (err) throw err;console.log(decoded); // { userId: 123, role: 'admin', iat: ..., exp: ... }
});

通过理解JWT的结构、流程及安全实践,开发者可以有效利用其在现代Web应用中实现安全、高效的身份验证。

2 用户mock和api

用户mock,user.js代码如下所示:

const Mock = require('mockjs')
const Random = Mock.Randommodule.exports = [{// 获取用户url: '/api/user/info',method: 'get',response() {return {errno: 0,data: {username: Random.title(),nickname: Random.cname(),},}}},{// 注册新用户url: '/api/user/register',method: 'post',response() {return {errno: 0}}},{// 用户登录url: '/api/user/login',method: 'post',response() {return {errno: 0,data: {token: Random.word(20)},}}},
]

前端user.ts 用户api接口代码如下所示:

 import request, { ResDataType } from "../services/request";/*** 获取用户信息* @returns  用户信息*/
export async function getUserInfoApi(): Promise<ResDataType> {const url = "/api/user/info";const data = (await request.get(url)) as ResDataType;return data;
}/*** 注册新用户* @returns  注册是否成功*/
export async function registerApi(username: string,password: string,nickname?: string
): Promise<ResDataType> {const url = "/api/user/register";const body = { username, password, nickname: nickname || username };const data = (await request.post(url, body)) as ResDataType;return data;
}/*** 用户登录* @returns  token*/
export async function loginApi(username: string,password: string
): Promise<ResDataType> {const url = "/api/user/login";const data = (await request.post(url, { username, password })) as ResDataType;return data;
}

3 注册

Register.tsx代码如下所示:

import { FC } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Typography, Space, Form, Input, Button, message } from "antd";
import { UserAddOutlined } from "@ant-design/icons";
import { useRequest } from "ahooks";import { LOGIN_PATHNAME } from "../router";
import { registerApi } from "@/api/user";import styles from "./Register.module.scss";const { Title } = Typography;const Register: FC = () => {const nav = useNavigate();const { run: handleRegister } = useRequest(async (values) => {const { username, password, nickname } = values;return await registerApi(username, password, nickname);},{manual: true,onSuccess() {message.success("注册成功");// 跳转登录页nav(LOGIN_PATHNAME);},});function onFinish(values: any) {handleRegister(values);}return (<div className={styles.container}><div><Space><Title level={2}><UserAddOutlined /></Title><Title level={2}>注册新用户</Title></Space></div><div><FormlabelCol={{ span: 6 }}wrapperCol={{ span: 16 }}onFinish={onFinish}><Form.Itemlabel="用户名"name="username"rules={[{ required: true, message: "请输入用户名" },{type: "string",min: 5,max: 20,message: "字符长度再5-20之间",},{pattern: /^\w+$/,message: "只能是字母数字下划线",},]}><Input /></Form.Item><Form.Itemlabel="密码"name="password"rules={[{ required: true, message: "请输入用户名" },{min: 8,message: "密码长度最少8位",},]}><Input.Password /></Form.Item><Form.Itemlabel="确认密码"name="confirm"dependencies={["password"]}rules={[{required: true,message: "请输入确认密码",},({ getFieldValue }) => ({validator(_, value) {if (!value || getFieldValue("password") === value) {return Promise.resolve();} else {return Promise.reject(new Error("两次密码不一致"));}},}),]}><Input.Password /></Form.Item><Form.Item label="昵称" name="nickname"><Input /></Form.Item><Form.Item wrapperCol={{ offset: 6, span: 16 }}><Space><Button type="primary" htmlType="submit">注册</Button><Link to={LOGIN_PATHNAME}>已有账户,登录</Link></Space></Form.Item></Form></div></div>);
};
export default Register;

执行注册,成功挑战登录页,如下图所示:在这里插入图片描述

4 登录

登录页Login.tsx代码如下所示:

import { FC, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Typography, Space, Form, Input, Button, Checkbox, message } from "antd";
import { UserAddOutlined } from "@ant-design/icons";
import { useRequest } from "ahooks";import { MANAGE_INDEX_PATHNAME, REGISTER_PATHNAME } from "../router";
import { loginApi } from "@/api/user";import styles from "./Register.module.scss";const { Title } = Typography;const USERNAME_KEY = "username";
const PASSWORD_KEY = "password";/*** 浏览器本地存储用户信息* @param username 用户名* @param password 密码*/
function rememberUser(username: string, password: string) {localStorage.setItem(USERNAME_KEY, username);localStorage.setItem(PASSWORD_KEY, password);
}/*** 浏览器本地删除用户信息* @param username 用户名* @param password 密码*/
function deleteUserFromStorage(username: string, password: string) {localStorage.removeItem(USERNAME_KEY);localStorage.removeItem(PASSWORD_KEY);
}/*** 浏览器本地获取用户信息*/
function getUserInfoFromStorage() {return {username: localStorage.getItem(USERNAME_KEY),password: localStorage.getItem(PASSWORD_KEY),};
}const Login: FC = () => {const nav = useNavigate()// 表单组件初始化const [form] = Form.useForm();useEffect(() => {const { username, password } = getUserInfoFromStorage();form.setFieldsValue({ username, password });// eslint-disable-next-line react-hooks/exhaustive-deps}, []);const { run: handleLogin } = useRequest(async (values) => {const { username, password } = values;return await loginApi(username, password);},{manual: true,onSuccess(res) {message.success("登录成功")// todo 存储token// 跳转我的问卷nav(MANAGE_INDEX_PATHNAME)},});function onFinish(values: any) {const { username, password, remember } = values || {};if (remember) {rememberUser(username, password);} else {deleteUserFromStorage(username, password);}handleLogin({ username, password });}return (<div className={styles.container}><div><Space><Title level={2}><UserAddOutlined /></Title><Title level={2}>用户登录</Title></Space></div><div><FormlabelCol={{ span: 6 }}wrapperCol={{ span: 16 }}onFinish={onFinish}initialValues={{ remember: true }}form={form}><Form.Itemlabel="用户名"name="username"rules={[{ required: true, message: "请输入用户名" },{type: "string",min: 5,max: 20,message: "字符长度再5-20之间",},{pattern: /^\w+$/,message: "只能是字母数字下划线",},]}><Input /></Form.Item><Form.Itemlabel="密码"name="password"rules={[{ required: true, message: "请输入用户名" },{min: 8,message: "密码长度最少8位",},]}><Input.Password /></Form.Item><Form.ItemwrapperCol={{ offset: 6, span: 16 }}name="remember"valuePropName="checked"><Checkbox>记住我</Checkbox></Form.Item><Form.Item wrapperCol={{ offset: 6, span: 16 }}><Space><Button type="primary" htmlType="submit">登录</Button><Link to={REGISTER_PATHNAME}>注册新用户</Link></Space></Form.Item></Form></div></div>);
};
export default Login;

登录成功后跳转我的问卷也,如下图所示:在这里插入图片描述

5 token存储

用户登录成功后,需要存储token,userToken.ts代码如下所示

/*** @description localStorage管理用户token* @author gaogzhen*/const KEY = "USER-TOKEN"/*** 设置token* @param token */
export function setToken(token:string) {localStorage.setItem(KEY, token)  
}/*** 获取token* @returns token*/
export function getToken() {return localStorage.getItem(KEY) || ''
}/*** 删除token*/
export function removeToken() {localStorage.removeItem(KEY)
}

登录页登录成功后,执行存储token,Login.tsx代码如下:

  const { run: handleLogin } = useRequest(async (values) => {const { username, password } = values;return await loginApi(username, password);},{manual: true,onSuccess(res) {message.success("登录成功");// 存储tokenconst { token = "" } = res;setToken(token);// 跳转我的问卷nav(MANAGE_INDEX_PATHNAME);},});

localStorage存储如下图哦所示:在这里插入图片描述

6 请求拦截器设置token

登录成功后,用户每次请求需要携带token,用户身份验证、权限验证等。这里通过请求拦截器实现,request.ts代码如下所示:

import axios from "axios";
import { message } from "antd";
import { AUTHORIZATION } from "@/constant";
import { getToken } from "@/utils/userToken";const request = axios.create({timeout: 5000,
});// request拦截:每次请求携带token
request.interceptors.request.use((config) => {// todo token 校验config.headers[AUTHORIZATION] = `Bearer ${getToken()}`;return config;
});// response 拦截:统一处理errno和msg
request.interceptors.response.use((res) => {const resData = (res.data || {}) as ResType;const { errno, data, msg } = resData;if (errno !== 0) {// 错误提示if (msg) {message.error(msg);}throw new Error(msg);}return data as any;
});
export default request;export type ResDataType = {[key: string]: any;
};export type ResType = {errno: number;data?: ResDataType;msg?: string;
};

效果如下图所示:

在这里插入图片描述

6 获取用户信息

用户登录之后,用户信息很多地方需要使用,在学习状态管理之后再处理,这里我们暂时在用户信息组件处理。

用户信息UserInfo.tsx代码如下所示:

import { FC } from "react";
import { Link } from "react-router-dom";
import { LOGIN_PATHNAME } from "../router/index";
import { useRequest } from "ahooks";
import { getUserInfoApi } from "@/api/user";
import { UserOutlined } from "@ant-design/icons";
import { Button } from "antd";const UserInfo: FC = () => {const { data } = useRequest(getUserInfoApi);const { username, nickname } = data || {};const User = (<><span style={{ color: "#e8e8e8" }}><UserOutlined />{nickname}</span><Button type="link">退出</Button></>);const Login = <Link to={LOGIN_PATHNAME}>登录</Link>;return <>{username ? User : Login}</>;
};
export default UserInfo;

效果如下图所示:

在这里插入图片描述

7 退出登录

UserInfo.tsx退出功能代码如下所示:

import { FC } from "react";
import { Link, useNavigate } from "react-router-dom";
import { useRequest } from "ahooks";
import { Button } from "antd";
import { UserOutlined } from "@ant-design/icons";import { LOGIN_PATHNAME } from "../router/index";
import { getUserInfoApi } from "@/api/user";
import { removeToken } from "@/utils/userToken";const UserInfo: FC = () => {const nav = useNavigate()const { data } = useRequest(getUserInfoApi);const { username, nickname } = data || {};function logout() {removeToken()// 跳转登录页nav(LOGIN_PATHNAME)}const User = (<><span style={{ color: "#e8e8e8" }}><UserOutlined />{nickname}</span><Button type="link" onClick={logout}>退出</Button></>);const Login = <Link to={LOGIN_PATHNAME}>登录</Link>;return <>{username ? User : Login}</>;
};
export default UserInfo;

  • 执行退出,但是右上角还是显示登录状态,后面处理

结语

❓QQ:806797785

⭐️仓库地址:https://gitee.com/gaogzhen

⭐️仓库地址:https://github.com/gaogzhen

[1]ahook官网[CP/OL].

[2]mock文档[CP/OL].

[3]Ant Design官网[CP/OL].

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/77589.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大模型入门

一、模型入门路径 1. 学习预备知识 编程语言&#xff1a;熟练掌握Python编程语言&#xff0c;包括基本语法、数据结构&#xff08;列表、字典、元组等&#xff09;、面向对象编程、文件操作等。Python有丰富的机器学习和深度学习库&#xff0c;方便进行大模型的开发和实验。数…

把dll模块注入到游戏进程的方法_插APC注入

一、概述 APC是异步过程调用,系统创建线程的时候会为线程创建一个APC队列,当线程调用SleepEx,WaitSingleObjectEx等函数时,并把线程状态被设置为可提醒状态时,线程并不会睡眠,而是检查APC队列是否为空,如果不为空,转去执行APC队列中的每一项,因此给目标进程中的线程插…

git 如何清空当前分支的历史提交记录,仅保留最后一次提交

本方法基于新建 Git 孤立分支实现&#xff1a; 1. ​​首先检出待清理的分支 在 IDEA 右下角检查当前分支名称 或执行 git branch 确认。如果不在目标分支上&#xff0c;会显示 (HEAD detached at xxxxx)&#xff0c;这时需要先切换分支&#xff1a; git checkout 原分支名 2.…

【C++】Googletest应用

Googletest 1 配置 使用cmake配置&#xff1a; 具体文件后面上传补充 ./test.out --gtest_filterXXXTest.xxx 2 gdb 为了跟踪流程&#xff0c;可以使用gdb&#xff1b; gdb ./xxx.out gdb --args ./gtest --gtest_filterxxx.xxx设置运行参数 set args --gtest_filterxxx.…

JavaScript:从DOM概述到window对象的常见事件

一、BOM概述 1.BOM的概念 BOM&#xff08;Browser Object Model&#xff09;即浏览器对象模型&#xff0c;它提供了独立于内容而与浏览器窗口进行交互的对象&#xff0c;其核心对象是window 2.BOM的构成 BOM比DOM更大&#xff0c;它包含DOM window对象是浏览器的顶级对象&a…

qobject与event事件应用

int main(int argc, char *argv[]) {QApplication a(argc, argv);MyWidget mainWidget;mainWidget.setWindowTitle("QObject与事件处理示例");mainWidget.resize(200, 200);mainWidget.show();return a.exec(); }QApplication a(argc, argv);&#xff1a;创建 QAppli…

QTableView复选框居中

目录 方法一&#xff1a;QSS方法2:自定义复选框委托类一、构造函数 CheckBoxDelegate()二、paint() 方法三、editorEvent() 方法四、关键设计要点五、扩展应用场景六、代码示例&#xff08;补充&#xff09; 方法一&#xff1a;QSS QTableView::indicator {position: relative…

基于QT的仿QQ音乐播放器

一、项目介绍 该项目是基于QT开发的⾳乐播放软件&#xff0c;界面友好&#xff0c;功能丰富&#xff0c;主要功能如下&#xff1a; 窗口hand部分&#xff1a; 点击最小化按钮&#xff0c;窗口最小化 点击最大化按钮&#xff0c;窗口最大化 点击关闭按钮&#xff0c;程序退出 …

SQL知识点合集---第二弹

数据一 <select id"listPositionAuditCheckSample" resultType"net.nxe.cloud.content.server.entity.PositionAuditCheckSample"><trim prefixOverrides"union all"><if test"userSampleCount ! null and userSampleCount…

【QT】QT控制硬件

QT控制硬件 1.上位机程序开发2.具体例子控制led灯3. linux中的函数跟QT类里面的函数同名&#xff0c;发生冲突4.示例代码 1.上位机程序开发 QT做一个上位机程序&#xff0c;控制底层的硬件设备(下位机) 总结&#xff1a; 在构造函数里面去初始化&#xff0c;打开硬件驱动在析…

Flutter介绍、Flutter Windows Android 环境搭建 真机调试

目录 Flutter介绍 Windows 环境搭建 1.安装配置JDK 2.下载安装Android Studio 3.下载配置Flutter SDK ​4.运行Flutter doctor命令检测环境是否配置成功 ​5.打开Android Studio安装Flutter/Dart 插件 ​6.插件运行Flutter项目 ​编辑 Flutter Android真机调试 Flut…

Android Studio 中使用 SQLite 数据库开发完整指南(Kotlin版本)

文章目录 1. 项目准备1.1 创建新项目1.2 添加必要依赖 2. 数据库设计3. 实现数据库3.1 创建实体类 (Entity)3.2 创建数据访问对象 (DAO)3.3 创建数据库类 4. 创建 Repository5. 创建 ViewModel6. 实现 UI 层6.1 创建笔记列表 Activityactivity_notes_list.xmlNotesListActivity…

Vue基础(7)_计算属性

计算属性(computed) 一、使用方式&#xff1a; 1.定义计算属性&#xff1a; 在Vue组件中&#xff0c;通过在 computed 对象中定义计算属性名称及对应的计算函数来创建计算属性。计算函数会返回计算属性的值。 2.在模板中使用计算属性&#xff1a; 在Vue的模板中&#xff0c;您…

辛格迪客户案例 | 华道生物细胞治疗生产及追溯项目(CGTS)

01 华道&#xff08;上海&#xff09;生物医药有限公司&#xff1a;细胞治疗领域的创新先锋 华道&#xff08;上海&#xff09;生物医药有限公司&#xff08;以下简称“华道生物”&#xff09;是一家专注于细胞治疗技术研发与应用的创新型企业&#xff0c;尤其在CAR-T细胞免疫…

[26] cuda 应用之 nppi 实现图像格式转换

[26] cuda 应用之 nppi 实现图像格式转换 讲述 nppi 接口定义通过nppi实现 bayer 格式转rgb格式官网参考信息:http://gwmodel.whu.edu.cn/docs/CUDA/npp/group__image__color__debayer.html#details1. 接口定义 官网关于转换的原理是这么写的: Grayscale Color Filter Array …

2025“钉耙编程”中国大学生算法设计春季联赛(8)10031007

题目的意思很好理解找从最左边到最右边最短路&#xff08;BFS&#xff09; #include <bits/stdc.h> using namespace std; int a[510][510]; // 存储网格中每个位置是否有障碍&#xff08;1表示有障碍&#xff0c;0表示无障碍&#xff09; int v[510][510]; // 记录每…

【Linux】第十一章 管理网络

目录 1.TCP/IP网络模型 物理层&#xff08;Physical&#xff09; 数据链路层&#xff08;Date Link&#xff09; 网络层&#xff08;Internet&#xff09; 传输层&#xff08;Transport&#xff09; 应用层&#xff08;Application&#xff09; 2. 对于 IPv4 地址&#…

python_股票月数据趋势判断

目录 前置 代码 视频&月数据 前置 1 A股月数据趋势大致判断&#xff0c;做一个粗略的筛选 2 逻辑&#xff1a; 1&#xff09;取最近一次历史最高点 2&#xff09;以1&#xff09;中最高点为分界点&#xff0c;只看右侧数据&#xff0c;取最近一次最低点 3&#xf…

Python PyAutoGUI库【GUI 自动化库】深度解析与实战指南

一、核心工作原理 底层驱动机制&#xff1a; 通过操作系统原生API模拟输入使用ctypes库调用Windows API/Mac Cocoa/Xlib屏幕操作依赖Pillow库进行图像处理 事件模拟流程&#xff1a; #mermaid-svg-1CGDRNzFNEffhvSa {font-family:"trebuchet ms",verdana,arial,sans…

Spring框架allow-bean-definition-overriding详细解释

Spring框架中&#xff0c;allow-bean-definition-overriding 是一个控制是否允许覆盖同名Bean定义的配置属性。以下是详细说明&#xff1a; ​1. 作用​ ​允许/禁止Bean定义覆盖​&#xff1a;当Spring容器中检测到多个同名的Bean定义时&#xff0c;此配置决定是否允许后续的…