滑动过期机制——延长 Token有效期
文章目录
- 1. Flask 后端代码(支持 WebSocket)
- 2. Android Studio Java 前端代码(使用 Socket.IO)
- 代码说明
- 后端
- 前端
- 注意事项
前端使用 Android Studio(Java)和 Socket.IO 库,后端使用 Flask。
1. Flask 后端代码(支持 WebSocket)
为了支持 WebSocket,我们需要使用 Flask-SocketIO 扩展:
# 导入所需的库
from flask import Flask, request, jsonify
from flask_socketio import SocketIO, emit
import jwt
import datetime
import loggingapp = Flask(__name__)
socketio = SocketIO(app)# 密钥,用于JWT的签名和验证,需要保证安全
SECRET_KEY = "your_secret_key"# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)# 生成JWT Token的函数
def generate_token(user_id):try:# 设置Token的有效期为1小时expiration_time = datetime.datetime.utcnow() + datetime.timedelta(hours=1)# 使用JWT库生成Tokentoken = jwt.encode({"user_id": user_id, "exp": expiration_time}, SECRET_KEY, algorithm="HS256")return tokenexcept Exception as e:logger.error(f"Error generating token: {e}")return None# 验证JWT Token的函数
def verify_token(token):try:# 解码Token,验证签名和过期时间payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])return payloadexcept jwt.ExpiredSignatureError:logger.warning("Token has expired")return Noneexcept jwt.InvalidTokenError:logger.warning("Invalid token")return None# 登录接口,生成Token
@app.route('/login', methods=['POST'])
def login():try:# 获取用户ID(这里简化为直接从请求中获取)user_id = request.json.get('user_id')if not user_id:return jsonify({"error": "Invalid user ID"}), 400# 生成Tokentoken = generate_token(user_id)if token:return jsonify({"token": token})else:return jsonify({"error": "Failed to generate token"}), 500except Exception as e:logger.error(f"Error in login route: {e}")return jsonify({"error": "Internal server error"}), 500# WebSocket事件处理
@socketio.on('protected_request')
def handle_protected_request(data):try:# 从客户端发送的数据中获取Tokentoken = data.get('token')if not token:emit('protected_response', {"error": "Token is missing"})return# 验证Tokenpayload = verify_token(token)if not payload:emit('protected_response', {"error": "Invalid or expired token"})return# 获取Token的过期时间exp_time = payload['exp']# 计算距离过期的时间remaining_time = exp_time - datetime.datetime.utcnow().timestamp()# 如果距离过期时间小于10分钟(600秒),生成新的Tokenif remaining_time < 600:new_token = generate_token(payload['user_id'])if new_token:emit('protected_response', {"message": "Access granted", "new_token": new_token})else:emit('protected_response', {"error": "Failed to generate new token"})else:emit('protected_response', {"message": "Access granted"})except Exception as e:logger.error(f"Error in protected_request: {e}")emit('protected_response', {"error": "Internal server error"})if __name__ == '__main__':socketio.run(app, debug=True)payload = verify_token(token)if payload:# 获取Token的过期时间exp_time = payload['exp']# 计算距离过期的时间remaining_time = exp_time - datetime.datetime.utcnow().timestamp()# 如果距离过期时间小于10分钟(600秒),生成新的Tokenif remaining_time < 600:new_token = generate_token(payload['user_id'])emit('protected_response', {"message": "Access granted", "new_token": new_token})else:emit('protected_response', {"message": "Access granted"})else:emit('protected_response', {"error": "Invalid or expired token"})else:emit('protected_response', {"error": "Token is missing"})if __name__ == '__main__':socketio.run(app, debug=True)
2. Android Studio Java 前端代码(使用 Socket.IO)
以下是使用 Socket.IO 库的前端代码:
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import com.github.nkzawa.emitter.Emitter;
import com.github.nkzawa.socketio.client.IO;
import com.github.nkzawa.socketio.client.Socket;import org.json.JSONObject;public class MainActivity extends AppCompatActivity {private TextView tokenTextView;private RequestQueue requestQueue;private Socket socket;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化请求队列requestQueue = Volley.newRequestQueue(this);// 初始化Socket.IOtry {socket = IO.socket("http://10.0.2.2:5000");} catch (Exception e) {Log.e("SocketIOError", "Error initializing Socket.IO", e);Toast.makeText(this, "Error initializing Socket.IO", Toast.LENGTH_SHORT).show();return;}// 获取显示Token的TextViewtokenTextView = findViewById(R.id.tokenTextView);// 登录按钮Button loginButton = findViewById(R.id.loginButton);loginButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 模拟用户登录,发送请求获取TokenloginRequest();}});// 受保护的接口按钮Button protectedButton = findViewById(R.id.protectedButton);protectedButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 发送请求到受保护的接口protectedRequest();}});// 监听从服务器返回的消息socket.on("protected_response", onProtectedResponse);socket.connect();}// 监听从服务器返回的消息private Emitter.Listener onProtectedResponse = new Emitter.Listener() {@Overridepublic void call(Object... args) {MainActivity.this.runOnUiThread(new Runnable() {@Overridepublic void run() {try {JSONObject response = (JSONObject) args[0];if (response.has("new_token")) {String newToken = response.getString("new_token");// 更新显示的TokentokenTextView.setText(newToken);Toast.makeText(MainActivity.this, "Token refreshed", Toast.LENGTH_SHORT).show();} else if (response.has("message")) {Toast.makeText(MainActivity.this, response.getString("message"), Toast.LENGTH_SHORT).show();} else if (response.has("error")) {Toast.makeText(MainActivity.this, response.getString("error"), Toast.LENGTH_SHORT).show();}} catch (Exception e) {Log.e("SocketResponseError", "Error processing response", e);Toast.makeText(MainActivity.this, "Error processing response", Toast.LENGTH_SHORT).show();}}});}};// 登录请求方法private void loginRequest() {// 构造登录请求的URLString loginUrl = "http://10.0.2.2:5000/login";// 构造请求体,包含用户IDJSONObject requestBody = new JSONObject();try {requestBody.put("user_id", "12345");} catch (Exception e) {Log.e("JSONError", "Error creating JSON request body", e);Toast.makeText(this, "Error creating JSON request body", Toast.LENGTH_SHORT).show();return;}// 创建JSON请求JsonObjectRequest loginRequest = new JsonObjectRequest(Request.Method.POST, loginUrl, requestBody,new Response.Listener<JSONObject>() {@Overridepublic void onResponse(JSONObject response) {try {// 获取返回的TokenString token = response.getString("token");// 显示TokentokenTextView.setText(token);Toast.makeText(MainActivity.this, "Token received", Toast.LENGTH_SHORT).show();} catch (Exception e) {Log.e("LoginResponseError", "Error processing login response", e);Toast.makeText(MainActivity.this, "Error processing login response", Toast.LENGTH_SHORT).show();}}}, new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {Log.e("LoginError", "Error in login request", error);Toast.makeText(MainActivity.this, "Login failed: " + error.getMessage(), Toast.LENGTH_SHORT).show();}});// 将请求加入队列requestQueue.add(loginRequest);}// 受保护接口的请求方法private void protectedRequest() {// 获取当前显示的TokenString token = tokenTextView.getText().toString();if (token.isEmpty()) {Toast.makeText(MainActivity.this, "No token available", Toast.LENGTH_SHORT).show();return;}// 构造发送到WebSocket的数据JSONObject data = new JSONObject();try {data.put("token", token);} catch (Exception e) {Log.e("JSONError", "Error creating JSON data for WebSocket", e);Toast.makeText(MainActivity.this, "Error creating JSON data for WebSocket", Toast.LENGTH_SHORT).show();return;}// 发送数据到WebSocketif (socket.connected()) {socket.emit("protected_request", data);} else {Log.e("SocketError", "Socket is not connected");Toast.makeText(MainActivity.this, "Socket is not connected", Toast.LENGTH_SHORT).show();}}@Overrideprotected void onDestroy() {super.onDestroy();// 断开Socket连接if (socket != null) {socket.off("protected_response", onProtectedResponse);socket.disconnect();}}
}
代码说明
后端
- 使用 Flask-SocketIO 实现 WebSocket 支持。
/login接口用于生成 Token。- WebSocket 事件
protected_request用于验证 Token,并在 Token 即将过期时生成新的 Token。 - 日志记录:使用
logging模块记录错误和警告信息,方便调试和排查问题。 - 异常处理:在生成 Token 和验证 Token 的函数中添加了异常捕获。在登录接口和 WebSocket 事件处理中添加了异常捕获,确保服务器不会因为未处理的异常而崩溃。
- 安全性:使用环境变量或配置文件管理密钥(
SECRET_KEY),避免直接写在代码中。对输入数据进行验证,确保用户 ID 不为空。
前端
- 使用 Socket.IO 客户端库(
com.github.nkzawa.socketio.client)。 - 初始化 Socket 并连接到后端 WebSocket 服务器。
- 通过
socket.emit发送请求到后端,并通过socket.on监听服务器返回的消息。 - 在
onProtectedResponse中处理服务器返回的消息,更新 Token 或显示消息。 - 异常处理:在初始化 Socket.IO 和发送请求时添加了异常捕获,避免程序崩溃。
- 在处理服务器返回的消息时添加了异常捕获。
- 安全性:对用户输入和服务器返回的数据进行验证,确保数据的完整性和合法性。在发送 WebSocket 请求之前,检查 Socket 是否已连接。
- 用户体验:在出现错误时,通过
Toast提示用户,确保用户能够了解问题所在。
注意事项
- 确保后端服务运行在
http://10.0.2.2:5000(这是 Android 模拟器访问本机的地址)。 - 替换
SECRET_KEY为实际的安全密钥。
相关文章:
滑动过期机制——延长 Token有效期
文章目录 1. Flask 后端代码(支持 WebSocket)2. Android Studio Java 前端代码(使用 Socket.IO)代码说明后端前端 注意事项 前端使用 Android Studio(Java)和 Socket.IO 库,后端使用 Flask。 1…...
《JVM考古现场(二十三):归零者·重启奇点的终极奥义》
目录 楔子:归零者文明觉醒 上卷十维弦理论破译 第一章:JVM弦论代码考古 第二章:超膜引用解析算法 第三章:量子真空涨落监控 中卷归零者心法实战 第四章:宇宙重启倒计时引擎 第五章:内存奇点锻造术 第…...
k8s中sidecar死循环
序言 怎么发现我的同事们很上进呢,估计做了下贱的事儿吧。 伤不到我,不代表不疼! sidecar产生的问题 1 背景 在k8s的环境中,pod的使用越来越多了,也就产生了sidecar容器,在现在的环境中,一个pod…...
Linux `init 4` 相关命令的完整使用指南
Linux init 4 相关命令的完整使用指南—目录 一、init 系统简介二、init 4 的含义与作用三、不同 Init 系统下的 init 4 行为1. SysVinit(如 CentOS 6、Debian 7)2. systemd(如 CentOS 7、Ubuntu 16.04)3. Upstart(如 …...
Java Web 之 简介 100问
DAO 层的作用是什么? DAO 层作用: 与数据库直接交互,封装所有数据访问的细节(即CRUD操作),不包含业务逻辑,只关注数据的持久化。 DAO的全拼是什么 Data Access Object,数据连接实…...
06-libVLC的视频播放器:推流RTMP
创建媒体对象 libvlc_media_t* m = libvlc_media_new_path(m_pInstance, inputPath.toStdString().c_str()); if (!m) return -1; // 创建失败返回错误 libvlc_media_new_path:根据文件路径创建媒体对象。注意:toStdString().c_str() 在Qt中可能存在临时字符串析构问题,建议…...
【物联网】基于LORA组网的远程环境监测系统设计
基于LORA组网的远程环境监测系统设计 演示视频: 简介: 1.本系统有一个主机,两个从机。 2.一主多从的LORA组网通信,主机和两个从机都配备了STM32F103单片机与 LoRa 模块,主机作为中心设备及WIFI网关,负责接收和发送数据到远程物联网平台和手机APP,两个从机则负责采集数…...
少儿编程路线规划
少儿编程路线规划—一文写明白 现在有很多的编程机构,五花八门的。我有幸也见识到了大家的营销策略。这些策略有黑有白吧,从业几年,沉淀下来一些客户角度的干货,分享给大家。 如果是想以很远很远的就业为目的,毕业就…...
第3章 垃圾收集器与内存分配策略《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》
第3章 垃圾收集器与内存分配策略 3.2 对象已死 Java世界中的所有对象实例,垃圾收集器进行回收前就是确定对象哪些是活着的,哪些已经死去。 3.2.1 引用计数算法 常见的回答是:给对象中添加一个引用计数器,有地方引用࿰…...
Docker Overlay 网络的核心工作(以跨节点容器通信为例)
Docker 的 overlay 网络是一种基于 VXLAN(Virtual Extensible LAN)的多主机网络模式,专为 Docker Swarm 集群设计,用于实现跨节点的容器通信。它通过虚拟二层网络,允许容器在不同主机上像在同一局域网内一样通信。Dock…...
用 R 语言打造交互式叙事地图:讲述黄河源区生态变化的故事
目录 🌟 项目背景:黄河源头的生态变迁 🧰 技术栈介绍 🗺️ 最终效果预览 💻 项目构建步骤 1️⃣ 数据准备 2️⃣ 构建 Leaflet 地图 3️⃣ 使用 scrollama 实现滚动触发事件 4️⃣ 使用 R Markdown / Quarto 打包发布 🎬 效果展示截图 📦 完整代码仓库 …...
Java Stream常见误区解析:五大错误与规避方法
Java Stream API以函数式编程风格提供了一种强大的数据处理方式,使代码更简洁和可读。然而,误用Stream可能导致性能低下、错误频发或代码难以维护。本文将探讨开发者在使用Java Stream时最常见的五种错误,并提供规避方法。 1. 在Stream处理中…...
【树莓派Pico FreeRTOS】-中断服务与二值信号量
中断服务与二值信号量 RP2040 由 Raspberry Pi 设计,具有双核 Arm Cortex-M0+ 处理器和 264KB 内部 RAM,并支持高达 16MB 的片外闪存。 广泛的灵活 I/O 选项包括 I2C、SPI 和独特的可编程 I/O (PIO)。 FreeRTOS 由 Real Time Engineers Ltd. 独家拥有、开发和维护。FreeRTO…...
构建灵活可扩展的接口抽象层:支持多种后端数据存取的最佳实践
构建灵活可扩展的接口抽象层:支持多种后端数据存取的最佳实践 在现代应用开发中,后端数据存取的需求可能非常多样化:本地数据库、云存储服务、REST API,甚至是文件系统。因此,设计一套支持多种后端数据存取的接口抽象层是提高系统灵活性和可维护性的关键。本文将详细探讨…...
Scade 语言词法介绍
Scade 6 是一种具备形式化语法与形式化语义的领域特定语言(注1)。自2008年发布(注5)起,在 Scade Suite 产品系列中语言定义方面到目前未产生重要的改变(注2)。在下面的内容中将介绍Scade 语言的词法(注3)。 注1&#x…...
如何配置环境变量HADOOP_HOMEM、AVEN_HOME?不配置会怎么样
以下是在不同操作系统中配置 HADOOP_HOME 和 JAVA_HOME 环境变量的方法,以及不配置可能产生的后果: 配置 HADOOP_HOME - Windows系统:下载并解压Hadoop安装包,然后右键“此电脑”,选择“属性”,点击“高级…...
YOLO学习笔记 | 基于YOLOv8的植物病害检测系统
以下是基于YOLOv8的植物病害检测系统完整技术文档,包含原理分析、数学公式推导及代码实现框架。 基于YOLOv8的智能植物病害检测系统研究 摘要 针对传统植物病害检测方法存在的效率低、泛化性差等问题,本研究提出一种基于改进YOLOv8算法的智能检测系统。通过设计轻量化特征提…...
在已有的vue项目中使用vuex
介绍 Vuex 是一个用于 Vue.js 应用程序的状态管理模式 库。它充当应用程序中所有组件的集中存储,其规则确保状态只能以可预测的方式进行更改。 专门在vue中实现集中式状态(数据)管理的一个插件对vue应用中多个组件的共享状态进行集中式的管…...
基于uniapp的鸿蒙APP大数据量性能优化
文章目录 一、问题诊断与性能瓶颈分析1.1 大数据场景下的典型性能问题1.2 性能监测工具使用1.2.1 HBuilderX内置分析器1.2.2 鸿蒙DevEco工具链1.2.3 自制性能埋点 二、数据加载优化方案2.1 分页加载实现(带错误重试机制)2.2 数据流优化策略2.2.1 数据压缩…...
C++ 面向对象关键语法详解:override、虚函数、转发调用和数组引用传参-策略模式
int A(参数...) override { return 某个对象.A(参数...);} 一.目标 本文将用一个简单的“数学运算器”例子,从零解释以下 C 语法特性: virtual 虚函数 override 重写关键字 函数体内部的“转发调用” 数组引用作为函数参数 适合初学者和希望加深…...
山东科技大学深度学习考试回忆
目录 一、填空(五个空,十分) 二、选择题(五个,十分) 三、判断题(五个,五分) 四、论述题(四个,四十分) 五、计算题(二个ÿ…...
sql server 学习计划
目标定位(适用于开发人员、架构师、DBA) 精通 SQL Server 的数据建模、T-SQL 编程、并发控制、性能优化、索引策略 掌握事务、锁机制、统计信息、执行计划 能独立完成复杂系统的数据库设计、调优与可用性设计 具备解决大数据量、高并发、长事务、数据…...
宇树机器狗go2—slam建图(1)点云格式
0.前言 上一篇番外文章教大家如何在宇树机器狗go2的gazebo仿真环境中实现简单的导航运动,本期文章会教大家如何让宇树的机器狗go2在仿真环境中进行slam建图时经常会遇到的一些点云格式,在后续的slam建图和slam算法解析的时候会经常与这些点云信息打交道…...
致远OA——自定义开发rest接口
文章目录 :apple: 业务流程 🍎 业务流程 代码案例: https://pan.quark.cn/s/57fa808c823f 官方文档: https://open.seeyoncloud.com/seeyonapi/781/https://open.seeyoncloud.com/v5devCTP/39/783.html 登录系统 —— 后台管理 —— 切换系…...
No package docker-ce available问题的解决
安装docker时提示 rootk8s-node3 ~]# yum install -y docker-ce docker-ce-cli containerd.io Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * base: mirrors.aliyun.com * extras: mirrors.aliyun.com * updates: mirrors.aliyun.com No packag…...
群晖威联通飞牛等nas如何把宿主机硬盘挂接到可道云docker容器中
可道云系统是用户常用的一款面向个人用户的轻量级私有云存储工具,以高效管理和安全存储为核心,打造便捷的数字化办公体验。但是用户希望把原有其他磁盘中文件挂接到这个新系统中有很大的难度,主要是对linux文件系统理解有很大的误区,认为目录结构是固定的…...
使用docker该怎么做:从公有仓库拉取镜像并上传到私有仓库
在容器化部署中,将公有镜像仓库(如Docker Hub)的镜像迁移到私有仓库(如Harbor、Nexus)是常见需求。 一、为什么需要将镜像从公有仓库传到私有仓库? 网络连通性:公有仓库依赖公网访问ÿ…...
软件开发指南——GUI 开发方案推荐
1. LVGL (Light and Versatile Graphics Library) 适用场景:嵌入式设备、资源受限环境 优势: 专为嵌入式设计的开源 GUI 库,内存占用极小(最低仅需 64KB RAM)支持触摸屏、硬件加速(如 STM32 的 LTDC&…...
使用 Azure AKS 保护 Kubernetes 部署的综合指南
企业不断寻求增强其软件开发和部署流程的方法。DevOps 一直是这一转型的基石,弥合了开发与运营之间的差距。然而,随着安全威胁日益复杂,将安全性集成到 DevOps 流水线(通常称为 DevSecOps)已变得势在必行。本指南深入探讨了如何使用 Azure Kubernetes 服务 (AKS) 来利用 D…...
C#: 用Libreoffice实现Word文件转PDF
现实场景中要实现Word格式转PDF格式还是比较常见的。 如果要用开源的组件,只有用Libreoffice了。 一、下载安装Libreoffice 先进入如下链接,找到最新版本和匹配的操作系统来安装。 官网试过,下载是能下载,但安装了用不了&…...
