SpringBoot | 使用jwt令牌实现登录认证,使用Md5加密实现注册
对于登录认证中的令牌,其实就是一段字符串,那为什么要那么麻烦去用jwt令牌?其实对于登录这个业务,在平常我们实现这个功能时,可能大部分都是通过比对用户名和密码,只要正确,就登录成功;而并不会考虑到使用jwt令牌。
但是其实使用jwt令牌的好处是:
- 它可以承载业务数据,减少后续请求查询数据库的次数;
- 并且可以防篡改,保证信息的合法性和有效性。
前提:
pom.xml文件添加以下依赖:
<!-- java-jwt坐标--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version></dependency><!-- 单元测试的坐标--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>compile</scope></dependency>
测试代码:
package com.xu;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.jupiter.api.Test;import java.util.Date;
import java.util.HashMap;
import java.util.Map;public class JwtTest {@Testpublic void testGen(){Map<String,Object> claims=new HashMap<>();claims.put("id",1);claims.put("username","张三");//生成jwt的代码String token= JWT.create().withClaim("user",claims)//添加载荷.withExpiresAt(new Date(System.currentTimeMillis()+1000*60*60*3)).sign(Algorithm.HMAC256("xu"));//指定算法,配置密钥System.out.println(token);}@Testpublic void testParse(){//定义字符串,模拟用户传递过来的tokenString token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE3MTk0ODgxMjR9.RtU7DxCKObLO2MyVlh0g7GbwxCBI2H23y9kn0kt34Lc";JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256("xu")).build();DecodedJWT decodedJWT= jwtVerifier.verify(token);//验证token,生成一个解析后的jwt对象Map<String, Claim> claims=decodedJWT.getClaims();System.out.println(claims.get("user"));}}
jwt令牌的生成:

验证jwt令牌:

使用jwt令牌实现登录功能,并且进入需要登录为前提的页面进行判断
项目框架目录:

pojo层:
package com.xu.pojo;
import lombok.Data;import java.time.LocalDateTime;
//lombok 在编译阶段,为实体类自动生成setter getter toString
// pom文件中引入依赖 在实体类上添加注解
@Data
public class User {private Integer id;//主键IDprivate String username;//用户名private String password;//密码private String nickname;//昵称private String email;//邮箱private String userPic;//用户头像地址private LocalDateTime createTime;//创建时间private LocalDateTime updateTime;//更新时间
}
package com.xu.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;//统一响应结果@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {private Integer code;//业务状态码 0-成功 1-失败private String message;//提示信息private T data;//响应数据//快速返回操作成功响应结果(带响应数据)public static <E> Result<E> success(E data) {return new Result<>(0, "操作成功", data);}//快速返回操作成功响应结果public static Result success() {return new Result(0, "操作成功", null);}public static Result error(String message) {return new Result(1, message, null);}
}
service层:
package com.xu.service;import com.xu.pojo.User;
import org.springframework.stereotype.Service;@Service
public interface UserService {//根据用户名查询用户User findByUserName(String username);//注册void register(String username, String password);
}
package com.xu.service.impl;import com.xu.mapper.UserMapper;
import com.xu.pojo.User;
import com.xu.service.UserService;
import com.xu.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic User findByUserName(String username) {User u=userMapper.findByUserName(username);return u;}@Overridepublic void register(String username, String password) {//加密String md5String= Md5Util.getMD5String(password);//添加userMapper.add(username,md5String);}
}
mapper层:
package com.xu.mapper;import com.xu.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {//根据用户名查询用户@Select("select * from user where username=#{username}")User findByUserName(String username);//添加@Insert("insert into user(username,password,create_time,update_time)"+" values(#{username},#{password},now(),now())")void add(String username, String password);
}
utils层:
实现登录的密码加密功能:
package com.xu.utils;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;public class Md5Util {/*** 默认的密码字符串组合,用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合*/protected static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};protected static MessageDigest messagedigest = null;static {try {messagedigest = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException nsaex) {System.err.println(Md5Util.class.getName() + "初始化失败,MessageDigest不支持MD5Util。");nsaex.printStackTrace();}}/*** 生成字符串的md5校验值** @param s* @return*/public static String getMD5String(String s) {return getMD5String(s.getBytes());}/*** 判断字符串的md5校验码是否与一个已知的md5码相匹配** @param password 要校验的字符串* @param md5PwdStr 已知的md5校验码* @return*/public static boolean checkPassword(String password, String md5PwdStr) {String s = getMD5String(password);return s.equals(md5PwdStr);}public static String getMD5String(byte[] bytes) {messagedigest.update(bytes);return bufferToHex(messagedigest.digest());}private static String bufferToHex(byte bytes[]) {return bufferToHex(bytes, 0, bytes.length);}private static String bufferToHex(byte bytes[], int m, int n) {StringBuffer stringbuffer = new StringBuffer(2 * n);int k = m + n;for (int l = m; l < k; l++) {appendHexPair(bytes[l], stringbuffer);}return stringbuffer.toString();}private static void appendHexPair(byte bt, StringBuffer stringbuffer) {char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>>// 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换stringbuffer.append(c0);stringbuffer.append(c1);}}
实现注册的jwt令牌功能:
package com.xu.utils;import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;import java.util.Date;
import java.util.Map;public class JwtUtil {private static final String KEY = "xu";//接收业务数据,生成token并返回public static String genToken(Map<String, Object> claims) {return JWT.create().withClaim("claims", claims).withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12)).sign(Algorithm.HMAC256(KEY));}//接收token,验证token,并返回业务数据public static Map<String, Object> parseToken(String token) {return JWT.require(Algorithm.HMAC256(KEY)).build().verify(token).getClaim("claims").asMap();}}
controller层:
package com.xu.controller;import com.xu.pojo.Result;
import com.xu.pojo.User;
import com.xu.service.UserService;
import com.xu.utils.JwtUtil;
import com.xu.utils.Md5Util;
import jakarta.validation.constraints.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
@RequestMapping("/user")
@Validated
public class UserController {@Autowiredprivate UserService userService;@PostMapping("register")public Result register(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password){//查询用户User u=userService.findByUserName(username);if(u==null){//没有占用,注册userService.register(username,password);return Result.success();}else {//占用return Result.error("用户名已被占用");}}@PostMapping("login")public Result<String> login(@Pattern(regexp = "^\\S{5,16}$")String username, @Pattern(regexp = "^\\S{5,16}$")String password){//根据用户名查询用户User loginUser=userService.findByUserName(username);//判断用户是否存在if(loginUser==null){return Result.error("用户名错误");}//判断密码是否正确if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){//登录成功Map<String,Object> claims=new HashMap<>();claims.put("id",loginUser.getId());claims.put("username",loginUser.getUsername());String token= JwtUtil.genToken(claims);return Result.success(token);}return Result.error("密码错误");}
}
使用localhost:8080/article/list测试登录中jwt令牌的功能:
package com.xu.controller;import com.xu.pojo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/article")
public class ArticleController {@GetMapping("/list")public Result<String> list(){return Result.success("所有文章数据");}
}
interceptors层(令牌验证):
package com.xu.interceptors;import com.xu.pojo.Result;
import com.xu.utils.JwtUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import java.util.Map;@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//令牌验证String token=request.getHeader("Authorization");//验证tokentry {Map<String,Object> claims= JwtUtil.parseToken(token);//放行return true;}catch (Exception e){response.setStatus(401);//不放行return false;}}
}
exception层:
package com.xu.exception;import com.xu.pojo.Result;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public Result handleException(Exception e){e.printStackTrace();return Result.error(StringUtils.hasLength(e.getMessage())?e.getMessage():"操作失败");}
}
config层(没有header的“令牌”,不拦截登录,注册页面):
package com.xu.config;import com.xu.interceptors.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//登录接口和注册接口不拦截registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");}
}
pom.xml文件:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>3.1.3</version></parent><groupId>com.xu</groupId><artifactId>big-news</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><name>big-news</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies>
<!-- web依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
<!-- <version>3.3.0</version>--></dependency><!-- mybatis依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.0</version></dependency>
<!-- mysql驱动依赖--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId>
<!-- <version></version>--></dependency><!-- lombok依赖--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- validation依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!-- java-jwt坐标--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version></dependency><!-- 单元测试的坐标--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>compile</scope></dependency></dependencies>
</project>
分析:
- 用户登录:
用户登录成功后,系统会自动下发JWT令牌,然后在后续的每次请求中,浏览器都需要在请求头header中携带到服务端,请求头的名称为 Authorization,值为true 登录时下发的JWT令牌。
- 用户注册:
用户注册使用Md5加密,防止数据库的密码被泄露,并且在用户注册时使用validation校验参数。
测试结果:
使用Postman测试:
登录功能:

登录成功后,获取jwt令牌,进入localhost:8080/article/list:

修改Authorization里面的字符串,没有获取jwt令牌:

注册功能:
注册成功(输入内容字符串在5~16个里面):


注册失败(输入用户名或者密码,字符串不在5~16个里面):




相关文章:
SpringBoot | 使用jwt令牌实现登录认证,使用Md5加密实现注册
对于登录认证中的令牌,其实就是一段字符串,那为什么要那么麻烦去用jwt令牌?其实对于登录这个业务,在平常我们实现这个功能时,可能大部分都是通过比对用户名和密码,只要正确,就登录成功ÿ…...
Springboot基于Redis的高性能分布式缓存数据库的实现与实例
一、引言 在现代的分布式系统和高并发应用中,缓存机制显得尤为重要。Redis作为一种开源(BSD许可)的内存键值存储,因其高性能、丰富的数据结构和多样化的应用场景,成为开发者们的首选。在这篇博客中,我们将…...
防止多次点击,vue的按钮上做简易的防抖节流处理
话不多说,上个视频,看看是不是你要的效果 防抖节流 1.创建一个directive.js // directive.js export default {install(Vue) {// 防重复点击(指令实现)Vue.directive(repeatClick, {inserted(el, binding) {el.addEventListener(click, () > {if (!el.disabled) {el.disabl…...
云计算【第一阶段(21)】Linux引导过程与服务控制
目录 一、linux操作系统引导过程 1.1、开机自检 1.2、MBR引导 1.3、GRUB菜单 1.4、加载 Linux 内核 1.5、init进程初始化 1.6、简述总结 1.7、初始化进程centos 6和7的区别 二、排除启动类故障 2.1、修复MBR扇区故障 2.1.1、 实验 2.2、修复grub引导故障 2.2.1、实…...
Google 发布最新开放大语言模型 Gemma 2,现已登陆 Hugging Face Hub
Google 发布了最新的开放大语言模型 Gemma 2,我们非常高兴与 Google 合作,确保其在 Hugging Face 生态系统中的最佳集成。你可以在 Hub 上找到 4 个开源模型 (2 个基础模型和 2 个微调模型) 。发布的功能和集成包括: Hub 上的模型https://hf.…...
智能分析赋能等保:大数据技术在安全审计记录中的应用
随着信息技术的飞速发展,大数据技术在各行各业中的应用愈发广泛,特别是在网络安全领域,大数据技术为安全审计记录提供了强有力的支撑。本文将深入探讨智能分析如何赋能等保(等级保护),以及大数据技术在安全…...
Django中,update_or_create()
在Django中,可以使用update_or_create()方法来更新现有记录或创建新记录。该方法接受一个字典作为参数,用于指定要更新或创建的字段和对应的值。 update_or_create()方法的语法如下: 代码语言:python obj, created Model.obje…...
每日一学(1)
目录 1、ConCurrentHashMap为什么不允许key为null? 2、ThreadLocal会出现内存泄露吗? 3、AQS理解 4、lock 和 synchronized的区别 1、ConCurrentHashMap为什么不允许key为null? 底层 putVal方法 中 如果key || value为空 抛出…...
SpringMVC(1)——入门程序+流程分析
MVC都是哪三层?在Spring里面分别对应什么?SpringMVC的架构是什么? 我们使用Spring开发JavaWeb项目,一般都是BS架构,也就是Browser(浏览器)-Server(服务器)架构 这种架构…...
成绩发布背后:老师的无奈与痛点
在教育的广阔天地里,教师这一角色承载着无数的期望与责任。他们不仅是知识的传播者,更是学生心灵的引路人。而对于班主任老师来说,他们的角色更加多元,他们不仅是老师,还必须是“妈妈”。除了像其他老师一样备课、上课…...
MySQL 索引之外的相关查询优化总结
在这之前先说明几个概念: 1、驱动表和被驱动表:驱动表是主表,被驱动表是从表、非驱动表。驱动表和被驱动表并非根据 from 后面表名的先后顺序而确定,而是根据 explain 语句查询得到的顺序确定;展示在前面的是驱动表&am…...
EE trade:贵金属投资的优点及缺点
贵金属(如黄金、白银、铂金和钯金)一直以来都是重要的投资和避险工具。它们具有独特的物理和化学特性,广泛应用于各种行业,同时也被视为财富储备。在进行贵金属投资时,了解其优点和缺点对于做出明智的投资决策至关重要。 一、贵金属投资的优…...
python工作目录与文件目录
工作目录 文件目录:文件所在的目录 工作目录:执行python命令所在的目录 D:. | main.py | ---data | data.txt | ---model | | model.py | | train.py | | __init__.py | | | ---nlp | | | bert.py | …...
可信和可解释的大语言模型推理-RoG
大型语言模型(LLM)在复杂任务中表现出令人印象深刻的推理能力。然而,LLM在推理过程中缺乏最新的知识和经验,这可能导致不正确的推理过程,降低他们的表现和可信度。知识图谱(Knowledge graphs, KGs)以结构化的形式存储了…...
秋招季的策略与行动指南:提前布局,高效备战,精准出击
6月即将进入尾声,一年一度的秋季招聘季正在热火进行中。对于即将毕业的学生和寻求职业发展的职场人士来说,秋招是一个不容错过的黄金时期。 秋招的序幕通常在6月至9月间拉开,名企们纷纷开启网申的大门。在此期间,求职备战是一个系…...
Java并发编程-wait与notify详解及案例实战
文章目录 概述wait()notify()作用注意事项用wait与notify手写一个内存队列wait与notify的底层原理:monitor以及wait_setMonitor(监视器)Wait Set(等待集合)Wait() 原理Notify() / NotifyAll() 原理注意事项wait与notify在代码中使用时的注意事项总结案例实战:基于wait与not…...
204.贪心算法:分发饼干(力扣)
以下来源于代码随想录 class Solution { public:int findContentChildren(vector<int>& g, vector<int>& s) {// 对孩子的胃口进行排序sort(g.begin(), g.end());// 对饼干的尺寸进行排序sort(s.begin(), s.end());int index s.size() - 1; // 从最大的饼…...
AI奥林匹克竞赛:Claude-3.5-Sonnet对决GPT-4o,谁是最聪明的AI?
目录 实验设置 评估对象 评估方法 结果与分析 针对学科的细粒度分析 GPT-4o vs. Claude-3.5-Sonnet GPT-4V vs. Gemini-1.5-Pro 结论 AI技术日新月异,Anthropic公司最新发布的Claude-3.5-Sonnet因在知识型推理、数学推理、编程任务及视觉推理等任务上设立新…...
【C++】const修饰成员函数
const修饰成员函数 常函数: 成员函数后加const后我们称为这个函数为常函数 常函数内不可以修改成员属性 成员属性声明时加关键字mutable后,在常函数中依然可以修改 class Animal { public:void fun1(){//这是一个普通的成员函数 }void fun2…...
基于模糊神经网络的时间序列预测(以hopkinsirandeath数据集为例,MATLAB)
模糊神经网络从提出发展到今天,主要有三种形式:算术神经网络、逻辑模糊神经网络和混合模糊神经网络。算术神经网络是最基本的,它主要是对输入量进行模糊化,且网络结构中的权重也是模糊权重;逻辑模糊神经网络的主要特点是模糊权值可…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
AI语音助手的Python实现
引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...
Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考
目录 lua脚本 记录流水 记录流水的作用 流水什么时候删除 我们在做库存扣减的时候,显示基于Lua脚本和Redis实现的预扣减 这样可以在秒杀扣减的时候保证操作的原子性和高效性 lua脚本 // ... 已有代码 ...Overridepublic InventoryResponse decrease(Inventor…...
