如何实现无感刷新(附前后端实现)?

news2024/11/28 22:56:43

无感刷新的核心思路:

在这里插入图片描述

无感刷新机制的目的是在用户不知情的情况下,自动更新其认证令牌(通常是Access Token),以保证用户的会话不会中断。这通常涉及到两种类型的令牌:

  1. Access Token:它是用户进行认证后得到的令牌,允许用户访问服务器的受保护资源。它有一个较短的有效期。

  2. Refresh Token:它是在同一时间发放给用户的另一个令牌,用于在Access Token过期时获取一个新的Access Token。它的有效期比Access Token长。

当Access Token即将过期或已经过期时,客户端会使用Refresh Token向认证服务器请求一个新的Access Token。如果Refresh Token仍然有效,认证服务器则发放一个新的Access Token给客户端,并且可能会同时发放一个新的Refresh Token。这个过程对用户来说是没有感知的,因此被称为“无感”刷新。

在项目中实施无感刷新:

后端实施步骤:
  1. 认证端点设置

    • 设计一个认证API端点,当用户初次登录时,返回Access Token和Refresh Token。
    • 设计一个Token刷新API端点,只接受Refresh Token并返回新的Access Token(可选地返回新的Refresh Token)。
  2. Token管理

    • Access Token应有一个短暂的生命周期,例如15分钟。
    • Refresh Token应有一个长期的生命周期,例如7天或更长,且应该存储在一个安全的存储中。
  3. 安全考虑

    • 对Refresh Token进行旋转(每次使用后就废弃旧的Refresh Token并发放一个新的)。
    • 通过HTTPS交换所有Token。
    • 应用适当的加密措施来保护Token的安全。
前端实施步骤:
  1. 存储Token

    • 在客户端安全地存储Access Token和Refresh Token(例如使用Web的localStorage或SecureStorage)。
  2. 拦截请求和响应

    • 使用拦截器监视所有出站请求和进站响应。
    • 在请求头中自动加入Access Token。
  3. 处理过期的Access Token

    • 当接收到表示Token过期的HTTP状态码(例如401)时,暂停发出的请求。
    • 使用Refresh Token请求新的Access Token。
  4. 处理新的Access Token

    • 更新存储中的Access Token。
    • 重新发送之前因Token过期而暂停的请求。
  5. 处理Refresh Token过期

    • 如果Refresh Token也过期或无效,引导用户重新登录。
无感刷新机制的大概思路就是这些,下面是具体的示例,分为简化版和完整版,简化版目的是更好的了解无感刷新的原理,而完整版就要考虑一些其他问题,比如说安全问题。

下面是实现无感刷新机制的具体示例(简化版)

这个例子涵盖前端(使用JavaScript)和后端(Node.js环境下使用Express框架)

后端(Node.js/Express)

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const accessTokenSecret = 'YOUR_ACCESS_TOKEN_SECRET';
const refreshTokenSecret = 'YOUR_REFRESH_TOKEN_SECRET';
let refreshTokens = [];

app.post('/login', (req, res) => {
    // 用户登录逻辑,验证用户凭证
    const { username, password } = req.body;

    // 这里应该有逻辑来验证用户凭证
    // 如果验证成功:
    const accessToken = jwt.sign({ username }, accessTokenSecret, { expiresIn: '15m' });
    const refreshToken = jwt.sign({ username }, refreshTokenSecret);
    refreshTokens.push(refreshToken);

    res.json({
        accessToken,
        refreshToken
    });
});

app.post('/refresh', (req, res) => {
    // 用户发送refresh token来获取新的access token
    const { refreshToken } = req.body;
    if (!refreshToken || !refreshTokens.includes(refreshToken)) {
        return res.sendStatus(403);
    }

    jwt.verify(refreshToken, refreshTokenSecret, (err, user) => {
        if (err) {
            return res.sendStatus(403);
        }

        const newAccessToken = jwt.sign({ username: user.username }, accessTokenSecret, { expiresIn: '15m' });

        res.json({
            accessToken: newAccessToken
        });
    });
});

app.listen(3000, () => {
    console.log('Authentication service started on port 3000');
});

前端(JavaScript/AJAX)

假设使用了axios作为HTTP客户端,可以设置拦截器来自动处理Token刷新。

axios.interceptors.response.use(response => {
    return response;
}, error => {
    const originalRequest = error.config;
    if (error.response.status === 401 && !originalRequest._retry) {
        originalRequest._retry = true;
        return axios.post('/refresh', {
            refreshToken: localStorage.getItem('refreshToken')
        }).then(res => {
            if (res.status === 200) {
                localStorage.setItem('accessToken', res.data.accessToken);
                axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('accessToken');
                return axios(originalRequest);
            }
        });
    }

    return Promise.reject(error);
});

这段代码中,我们在响应拦截器中检查任何错误的响应。如果我们得到一个401响应,并且这是第一次重试,我们就发送一个带有refreshToken的请求到/refresh端点。如果刷新Token成功,我们就保存新的accessToken,更新请求头,并重新发起失败的请求。

完整示例

实现非简化的登录无感刷新机制通常会涉及更详细的认证流程、错误处理、日志记录和安全性措施。这包括使用数据库存储Refresh Tokens、自动撤销机制、双因素认证等。

后端实现(Node.js/Express + MongoDB)

首先,你需要设置一个数据库来存储Refresh Tokens。出于安全考虑,每个Refresh Token都应该与一个用户账户关联,并且能够被追踪和撤销。

const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const User = require('./models/User'); // 导入User模型
const RefreshToken = require('./models/RefreshToken'); // 导入RefreshToken模型
const app = express();

// ...其他必要的中间件和数据库连接代码...

app.post('/login', async (req, res) => {
    // 用户登录逻辑,验证用户凭证
    const { username, password } = req.body;
    const user = await User.findOne({ username });

    if (user && bcrypt.compareSync(password, user.password)) {
        const accessToken = jwt.sign({ userId: user._id }, accessTokenSecret, { expiresIn: '15m' });
        const refreshToken = jwt.sign({ userId: user._id }, refreshTokenSecret);

        // 保存refresh token到数据库
        const newRefreshToken = new RefreshToken({ token: refreshToken, user: user._id });
        await newRefreshToken.save();

        res.json({ accessToken, refreshToken });
    } else {
        res.status(401).send('Username or password incorrect');
    }
});

app.post('/refresh', async (req, res) => {
    // 用户发送refresh token来获取新的access token
    const { refreshToken } = req.body;
    const dbToken = await RefreshToken.findOne({ token: refreshToken });

    if (!dbToken) {
        return res.status(403).send('Refresh token not found');
    }

    jwt.verify(refreshToken, refreshTokenSecret, async (err, decoded) => {
        if (err) {
            return res.status(403).send('Refresh token invalid');
        }

        // 生成新的access token和refresh token
        const newAccessToken = jwt.sign({ userId: decoded.userId }, accessTokenSecret, { expiresIn: '15m' });
        const newRefreshToken = jwt.sign({ userId: decoded.userId }, refreshTokenSecret);

        // 更新数据库中的refresh token
        dbToken.token = newRefreshToken;
        await dbToken.save();

        res.json({ accessToken: newAccessToken, refreshToken: newRefreshToken });
    });
});

app.listen(3000, () => {
    console.log('Authentication service started on port 3000');
});

在此示例中,我们使用了MongoDB来存储用户和他们的Refresh Tokens。登录时,我们验证用户凭证,并且如果认证成功,我们就生成Access Token和Refresh Token,然后将Refresh Token保存到数据库。在无感刷新流程中,我们验证提供的Refresh Token是否存在于数据库中,并且是否有效,然后发放新的Access Token。

前端实现(JavaScript/AJAX + Local Storage)

在前端实现中,我们需要确保存储Token的方法是安全的。在生产环境中,你可能需要考虑使用更安全的存储方式,如HTTPOnly Cookies或Secure Local Storage。

axios.interceptors.response.use(
  response => response,
  error => {
    const originalRequest = error.config;

    // 检测token过期的错误代码,比如401
    if (error.response.status === 401 && originalRequest.url === '/refresh') {
      // 刷新Token失败,直接登出用户
      logoutUser();
      return Promise.reject(error);
    }

    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      return axios.post('/refresh', { refreshToken: localStorage.getItem('refreshToken') })
        .then(res => {
          if (res.status === 200) {
            // 将新token设置到本地存储和axios默认头部
            localStorage.setItem('accessToken', res.data.accessToken);
            localStorage.setItem('refreshToken', res.data.refreshToken);
            axios.defaults.headers.common['Authorization'] = 'Bearer ' + res.data.accessToken;

            // 更新失败请求的头部并重新发送


            originalRequest.headers['Authorization'] = 'Bearer ' + res.data.accessToken;
            return axios(originalRequest);
          }
        })
        .catch(error => {
          // 任何错误都直接登出用户
          logoutUser();
          return Promise.reject(error);
        });
    }

    return Promise.reject(error);
  }
);

function logoutUser() {
  // 清除本地存储和状态,重定向到登录页面
  localStorage.removeItem('accessToken');
  localStorage.removeItem('refreshToken');
  // ...重定向到登录页的代码...
}

在前端的实现中,我们创建了一个axios拦截器,它会在遇到401未授权的响应时自动尝试刷新Token。如果刷新Token请求也失败,则触发用户登出的逻辑。

注意:此功能一定要注意其安全性,具体的话要结合实际的项目,以下是我的一些建议:
安全方面通常包括以下几个方法:

1. 使用HTTPS来加密所有的通信。
2. 安全地存储Access Token和Refresh Token,如使用httpOnly和Secure属性的Cookies。
3. 对Token进行定期轮换,特别是Refresh Token。
4. 在服务器端验证Token的签名。
5. 设置适当的Token过期时间。
6. 实现Token撤销逻辑,以便在检测到异常时能够立即废弃使用。
7. 避免在客户端暴露敏感的认证逻辑。
8. 对所有的认证请求和Token刷新请求进行率限制和异常监测。
9. 使用跨站请求伪造(CSRF)保护措施。
10. 确保客户端和服务端都有充分的错误处理和日志记录机制。

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

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

相关文章

C++:if语句(20)

介绍 c最基本的三种程序运行模式为:顺序执行,判断执行,循环执行 顺序执行:前面我们的代码都是从上往下,依次执行的,这就是按顺序依次执行 判断执行:通过我们写的判断条件,去判断要不…

算法:完全背包问题dp

文章目录 一、完全背包问题的特征二、定义状态三、状态转移四、降维优化五、参考例题5.1、Acwing:3.完全背包问题5.2、Acwing:900. 整数划分 一、完全背包问题的特征 完全背包问题是动态规划中的一种经典问题,它的主要特征可以总结如下&…

重学SpringBoot3-事件与监听器

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-事件与监听器 事件和监听器的基本概念Spring Boot中的事件类型1. ApplicationStartingEvent2. ApplicationEnvironmentPreparedEvent3. ApplicationCont…

【InternLM 实战营第二期笔记】使用茴香豆搭建你的RAG智能助理

RAG RAG是什么 RAG(Retrieval Augmented Generation)技术,通过检索与用户输入相关的信息片段,并结合外部知识库来生成更准确、更丰富的回答。解决 LLMs 在处理知识密集型任务时可能遇到的挑战, 如幻觉、知识过时和缺乏透明、可追…

2024年阿里云优惠券(代金券)在哪里领取?

阿里云作为国内领先的云计算服务提供商,不仅提供了稳定、高效的云服务,还时常推出各种优惠活动,以此来吸引用户上云。其中,阿里云优惠券就是一种常见的优惠方式。那么,在2024年,我们该如何领取阿里云优惠券…

在自定义数据集上微调 YOLOv9 模型

在自定义数据集上微调 YOLOv9模型可以显着提高目标检测性能,但这种改进有多显着呢?在这次全面的探索中,YOLOv9在SkyFusion数据集上进行了微调,分为三个不同的类别:飞机、船舶和车辆。通过一系列广泛的实验,包括修改学习率、图像大小和战略性冻结主干网,已经实现了令人印…

5. python练习题5-存款、收益率计算

5. python练习题5-存款、收益率计算 【目录】 文章目录 5. python练习题5-存款、收益率计算1. 目标任务2. 解题思路3. 知识回顾3.1 Python赋值运算符3.2 列表的append函数3.3 round()函数—保留小数点位数3.4 f格式化字符串3.5 列表求和方法 4. 程序代码4.1 使用循环结构实现4…

【强化学习的数学原理-赵世钰】课程笔记(二)贝尔曼公式

【强化学习的数学原理-赵世钰】课程笔记(二)贝尔曼公式 一. 内容概述 1. 第二章主要有两个内容 (1)一个核心概念:状态值(state value):从一个状态出发,沿着一个策略我…

linux安装dubboAdmin

1.环境准备: jdk-8u391-linux-x64apache-maven-3.9.6apache-tomcat-8.5.100 2.安装注册中心zookeeper zookeeper的安装看我的另一篇文章,安装完成后保持启动状态 linux安装Zookeeper的详细步骤-CSDN博客 3.安装dubboadmin 源码下载地址:R…

隔离放大器 分类 及说明

无源传感器隔离配电及信号采集传输 ●前置放大、电桥等电路配置电源方便采集信号 ●PLC、DCS现场模拟信号隔离、采集 ●直流电流/电压信号的隔离、转换及放大 ●模拟信号地线干扰抑制及数据隔离、采集 ●工业现场信号隔离及长线传输 ●仪器仪表与传感器信号收发 ●电力监控、医…

【Django开发】0到1美多商城项目md教程第6篇:账号登录,1. 用户名登录逻辑分析【附代码文档】

美多商城完整教程(附代码资料)主要内容讲述:欢迎来到美多商城!,项目准备。展示用户注册页面,创建用户模块子应用。用户注册业务实现,用户注册前端逻辑。图形验证码,图形验证码接口设…

Windows:IntelliJ IDEA Ultimate 安装 PHP 插件

在 IntelliJ IDEA Ultimate 中安装 PHP 插件,支持PHP开发调试 首先,进入File > Setting: 再次选择Plugins,然后选择上面的 Marketplace。 在搜索栏中输入 PHP,然后单击左侧的 Install 进行安装就可以了。 安装成功…

SD-WAN组成及关键技术解析

云桥通SD-WAN作为下一代网络连接技术,由控制平面、数据平面和管理平面三部分组成,共同构建了灵活、可管理和高效的网络架构。 1、控制平面: 控制平面负责决策制定和流量管理,在云桥通SD-WAN网络中扮演关键角色。基于软件定义网络…

转圈游戏——快速幂

目录 题目 思路 代码 题目 思路 每个小朋友移动一次的位置为,移动 q 次的位置则为。那么题目要求移动 ,最后的位置为 。 但 的范围是,而总的移动次数是 。时间复杂度是在,因此是一定不能硬算的,肯定会超时。那么该…

【架构七】Hadoop

什么是Hadoop? Hadoop是一个存储系统计算框架的软件大数据处理框架。 Hadoop的核心,说白了就是HDFS和MapReduce,HDFS为海量数据提供了存储,而MapReduce为海量数据提供了计算框架。HBase是一个分布式的、面向列的开源数据库。属于Nosql。 …

微信小程序之事件

1、什么是事件 UI界面的程序需要和用户互动,例如用户可能会点击你界面上某个按钮,又或者长按某个区域,这类反馈应该通知给开发者的逻辑层,需要将对应的处理状态呈现给用户。 有些时候程序上的“行为反馈”不一定是用户主动触发的&…

详解简单的shell脚本 --- 命令行解释器【Linux后端开发】

首先附上完整代码 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> //命令行解释器 //shell 运行原理&#xff1a;通过让子进程执行命令&#xff0c;父进…

计算机体系结构(1) 介绍和基础

为了跟上我们组学习的进度&#xff0c;打好体系结构的基础&#xff0c;接下来我会持续的学习计算机体系结构的知识。参考的课程是 苏黎世联邦理工 ETH Zurich&#xff1a;Digital Design and Computer Architect Lecture 1: Introduction and Basics_哔哩哔哩_bilibili 这一…

BioXCell,1H6--InVivoMAb anti-canine CD34

1H6单克隆抗体与犬CD34发生反应。CD34是一种I型单体唾液酸粘蛋白样糖蛋白&#xff0c;存在于许多干细胞群体中。CD34由骨髓和外周血中的造血祖细胞以及一些间充质干细胞、基质细胞、胚胎成纤维细胞、肿瘤细胞和成人血管内皮细胞表达。CD34经常被用作量化用于造血干细胞移植后该…

根证书和中间证书安装配置

下载根证书 根证书是建立信任链的基础。一旦客户端安装了根证书&#xff0c;它即可验证由该根证书签发的所有证书。这使得客户端可以信任与该根证书相关的所有服务器和应用程序&#xff0c;从而建立起一个完整的信任链。 如果您的业务用户通过浏览器访问您的Web业务&#xff…