实现登录功能
MD5配置
xml
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
创建util包,MD5Util
java
package com.yxl.seckill.util;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Component;
@Component
public class MD5Util
{
public static String md5(String src) {
return DigestUtils.md5Hex(src);
}
private static final String salt = "1a2b3c4d";
public static String inputPassToFormPass(String inputPass) {
System.out.println(salt.charAt(0));
System.out.println(salt.charAt(2));
System.out.println(salt.charAt(5));
System.out.println(salt.charAt(4));
System.out.println(""+salt.charAt(0)+salt.charAt(2));
String str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) +
salt.charAt(4);
System.out.println(str);
return md5(str);
}
public static String formPassToDBPass(String formPass, String salt) {
String str = ""+salt.charAt(0)+salt.charAt(2) + formPass +salt.charAt(5) +
salt.charAt(4);
return md5(str);
}
public static String inputPassToDbPass(String inputPass, String saltDB) {
String formPass = inputPassToFormPass(inputPass);
String dbPass = formPassToDBPass(formPass, saltDB);
return dbPass;
}
public static void main(String[] args) {
System.out.println(inputPassToFormPass("123456"));//d3b1294a61a07da9b49b6e22b2cbd7f9
System.out.println(formPassToDBPass(inputPassToFormPass("123456"),
"1a2b3c4d"));
System.out.println(inputPassToDbPass("123456", "1a2b3c4d"));
}
}
点击 Run运行

在测试控制器中Demo2Controller,增加对用户的List进行数据初始化,使其有一条供测试的数据信息 属性id赋值为1,name赋值为"张三",password为123456进行md5加密后处理的数据(需要引入MD5Util)。创建登录接口"/login",对前端输入的用户信息进行用户与密码信息的校验。
1.增加对用户的List进行数据初始化

java
// 静态User列表用于存储数据
private static List<User> userList = new ArrayList<>();
{
User user1 = new User();
user1.setId(1L);
user1.setName("张三");
user1.setPassword("d3b1294a61a07da9b49b6e22b2cbd7f9");
userList.add(user1);
}
2.创建登录接口"/login"
3.在接口中实现用户信息进行用户与密码信息的校验逻辑(需要引入)
java
// POST /demo2/login - 登录
@GetMapping("/login")
public CommonResult<User> login(@RequestParam String name, @RequestParam String password) {
System.out.println("参数:"+name);
System.out.println("参数:"+password);
String inputPassToFormPass = MD5Util.inputPassToFormPass(password);
System.out.println(inputPassToFormPass);
for (User user : userList) {
System.out.println(user.getName());
System.out.println(user.getPassword());
if (user.getName().equals(name) && user.getPassword().equals(inputPassToFormPass)) {
return CommonResult.success(user);
}
}
return CommonResult.error("User not found with name: " + name);
}
调用结果:失败与成功的结果反馈


用户端:PASS=MD5(明文+固定Salt)
服务端:PASS=MD5(用户输入+随机Salt)
用户端MD5加密是为了防止用户密码在网络中明文传输,服务端MD5加密是为了提高密码安全性,双
重保险。
逆向工程配置
MyBatis-Plus 官方文档 https://baomidou.com/guides/new-code-generator/
新建一个工程,只选择spring web,generator




进行配置
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.14</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.14</version>
</dependency>
<!-- freemarke 模板 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.19</version>
</dependency>
</dependencies>
将代码生成器的模板 放在此目录下

加入生成代码
java
package com.yxl.generator;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.sql.Types;
import java.util.Collections;
public class CodeGenerator
{
public static void main(String[] args) {
FastAutoGenerator.create("url", "user", "pwd")
.globalConfig(builder -> {
builder.author("yxl") // 设置作者
.enableSwagger() // 开启 swagger 模式
.outputDir("G://java"); // 指定输出目录
})
.dataSourceConfig(builder ->
builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
})
)
.packageConfig(builder ->
builder.parent("com.yxl.seckill") // 设置父包名
.moduleName("") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "G://java//com//yxl//seckill")) // 设置mapperXml生成路径
)
.strategyConfig(builder ->
builder.addInclude("t_user") // 设置需要生成的表名
.entityBuilder()
.enableLombok()
.controllerBuilder()
.enableRestStyle() // 启用 REST 风格
// .addTablePrefix("t_", "c_") // 设置过滤表前缀
)
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}

生成结果

在控制器类中对用户表进行增删改查的实现。
springboot 整合mybatis-plus
配置pom.xml
xml
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.2</version>
</dependency>
<!--mysql 依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- MyBatis-Plus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-jsqlparser -->
<!--mybatis-plus 分页插件依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>3.5.14</version>
</dependency>
Mybits-plus的yaml配置
java
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seckill?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&useSSL=false
username: root
password: 123456
hikari:
# 连接池名
pool-name: DateHikariCP
# 最小空闲连接数
minimum-idle: 5
# 空闲连接存活最大时间,默认600000(10分钟)
idle-timeout: 180000
# 最大连接数,默认10
maximum-pool-size: 10
# 从连接池返回的连接的自动提交
auto-commit: true
# 连接最大存活时间,0表示永久存活,默认1800000(30分钟)
max-lifetime: 1800000
# 连接超时时间,默认30000(30秒)
connection-timeout: 30000
# 测试连接是否可用的查询语句
connection-test-query: SELECT 1
# Mybatis-plus配置
mybatis-plus:
#配置Mapper映射文件
mapper-locations: classpath*:/mapper/*Mapper.xml
# 配置MyBatis数据返回类型别名(默认别名是类名)
type-aliases-package: com.yxl.seckill.entity
## Mybatis SQL 打印(方法接口所在的包,不是Mapper.xml所在的包)


Spring Boot 工程
- 配置 MapperScan 注解
java
package com.yxl.seckill;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.yxl.seckill.mapper")
public class SeckillApplication
{
public static void main(String[] args)
{
SpringApplication.run(SeckillApplication.class, args);
}
}
实现登录接口
创建登录参数消息体vo ,创建vo包
java
package com.yxl.seckill.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.hibernate.validator.constraints.Length;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginVo
{
private String mobile;
private String password;
}
创建登录接口 loginController
java
package com.yxl.seckill.controller;
import com.yxl.seckill.service.ITUserService;
import com.yxl.seckill.vo.CommonResult;
import com.yxl.seckill.vo.LoginVo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/")
@Slf4j
public class LoginController
{
private static final Logger log = LoggerFactory.getLogger(LoginController.class);
@Autowired
private ITUserService userService;
@RequestMapping("/login")
public CommonResult<String> login(HttpServletRequest request, HttpServletResponse response, @RequestBody LoginVo loginVo)
{
log.info(loginVo.toString());
CommonResult<String> login = userService.login(request, response, loginVo);
return login;
}
}
创建uuid,UUID(Universally Unique Identifier,通用唯一识别码)是一种在软件构建中用于确保全局唯一性的标准。它是一个128位的数字,通常表示为32个十六进制数字,并通过特定的算法生成,以确保在不同的系统和时间中都是唯一的。
java
package com.yxl.seckill.util;
import java.util.UUID;
public class UUIDUtil
{
public static String uuid() {
return UUID.randomUUID().toString().replace("-", "");
}
}
TUserServiceImpl 实现登录
java
@Override
public CommonResult<String> login(HttpServletRequest request, HttpServletResponse response, LoginVo loginVo)
{
try
{
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
TUser user = userMapper.selectById(mobile);
if (user == null)
{
return CommonResult.error(ResponseCode.LOGINVO_ERROR);
}
//校验密码
if (!MD5Util.formPassToDBPass(password, user.getSlat()).equals(user.getPassword()))
{
return CommonResult.error(ResponseCode.LOGINVO_ERROR);
}
//生成cookie
String ticket = UUIDUtil.uuid();
request.getSession().setAttribute(ticket,user);
CookieUtil.setCookie(request,response,"userTicket",ticket);
return CommonResult.success(ticket);
}
catch (Exception e)
{
e.printStackTrace();
return CommonResult.error(ResponseCode.ERROR);
}
}
CookieUtil 代码实现:Cookie 是浏览器存储在用户设备上的小型文本数据 ,它用于记录用户会话、偏好等信息。它的核心作用是:让服务器识别用户身份 。 https://juejin.cn/post/7522770423504306202
java
package com.yxl.seckill.util;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class CookieUtil
{
/**
* 得到Cookie的值, 不编码
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName)
{
return getCookieValue(request, cookieName, false);
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName,
boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
} else {
retValue = cookieList[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName,
String encodeString) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
retValue = URLDecoder.decode(cookieList[i].getValue(),
encodeString);
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
* 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse
response, String cookieName,
String cookieValue) {
setCookie(request, response, cookieName, cookieValue, -1);
}
/**
* 设置Cookie的值 在指定时间内生效,但不编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse
response, String cookieName,
String cookieValue, int cookieMaxage) {
setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
}
/**
* 设置Cookie的值 不设置生效时间,但编码
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse
response, String cookieName,
String cookieValue, boolean isEncode) {
setCookie(request, response, cookieName, cookieValue, -1, isEncode);
}
/**
* 设置Cookie的值 在指定时间内生效, 编码参数
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse
response, String cookieName,
String cookieValue, int cookieMaxage, boolean
isEncode) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage,
isEncode);
}
/**
* 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse
response, String cookieName,
String cookieValue, int cookieMaxage, String
encodeString) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage,
encodeString);
}
/**
* 删除Cookie带cookie域名
*/
public static void deleteCookie(HttpServletRequest request, HttpServletResponse
response,
String cookieName) {
doSetCookie(request, response, cookieName, "", -1, false);
}
/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxage cookie生效的最大秒数
*/
private static final void doSetCookie(HttpServletRequest request,
HttpServletResponse response,
String cookieName, String cookieValue, int
cookieMaxage, boolean isEncode) {
try {
if (cookieValue == null) {
cookieValue = "";
} else if (isEncode) {
cookieValue = URLEncoder.encode(cookieValue, "utf-8");
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
System.out.println(domainName);
if (!"localhost".equals(domainName)) {
cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxage cookie生效的最大秒数
*/
private static final void doSetCookie(HttpServletRequest request,
HttpServletResponse response,
String cookieName, String cookieValue, int
cookieMaxage, String encodeString) {
try {
if (cookieValue == null) {
cookieValue = "";
} else {
cookieValue = URLEncoder.encode(cookieValue, encodeString);
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0) {
cookie.setMaxAge(cookieMaxage);
}
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
System.out.println(domainName);
if (!"localhost".equals(domainName)) {
cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 得到cookie的域名
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;
// 通过request对象获取访问的url地址
String serverName = request.getRequestURL().toString();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
// 将url地下转换为小写
serverName = serverName.toLowerCase();
// 如果url地址是以http://开头 将http://截取
if (serverName.startsWith("http://")) {
serverName = serverName.substring(7);
}
int end = serverName.length();
// 判断url地址是否包含"/"
if (serverName.contains("/")) {
//得到第一个"/"出现的位置
end = serverName.indexOf("/");
}
// 截取
serverName = serverName.substring(0, end);
// 根据"."进行分割
final String[] domains = serverName.split("\\.");
int len = domains.length;
if (len > 3) {
// www.xxx.com.cn
domainName = domains[len - 3] + "." + domains[len - 2] + "." +
domains[len - 1];
} else if (len <= 3 && len > 1) {
// xxx.com or xxx.cn
domainName = domains[len - 2] + "." + domains[len - 1];
} else {
domainName = serverName;
}
}
if (domainName != null && domainName.indexOf(":") > 0) {
String[] ary = domainName.split("\\:");
domainName = ary[0];
}
return domainName;
}
}
登录测试 :http://localhost:8080/login?mobile=18501345678&password=d3b1294a61a07da9b49b6e22b2cbd7f9

配置验证注解pom.xml
xml
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.5.Final</version>
</dependency>
注解的应用
java
package com.yxl.seckill.vo;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
import org.hibernate.validator.constraints.Length;
@Data
public class LoginVo
{
@NotNull
private String mobile;
@NotNull
@Length(min = 32)
private String password;
}
自定义注解 手机号验证
IsMobile 是一个自定义的 Java 验证注解,用于验证手机号码格式是否正确。它包含以下四个元注解:
@Documented:表示该注解应该被 javadoc 工具记录,使注解信息出现在生成的文档中。
@Retention(RetentionPolicy.RUNTIME):指定注解的保留策略为运行时保留,这意味着注解信息会在编译后的 class 文件中存在,并且在运行时可以通过反射获取。
RetentionPolicy 的三种策略
- RetentionPolicy.SOURCE:注解只保留在源文件中,当 Java 文件编译成 class 文件时,注解被丢弃。
- RetentionPolicy.CLASS:注解被保留到 class 文件中,但 JVM 加载 class 文件时,注解不会被加载到内存中,这是默认的生命周期。
- RetentionPolicy.RUNTIME:注解不仅被保留到 class 文件中,JVM 加载 class 文件后,注解仍然存在。这使得我们可以在运行时通过反射机制读取注解信息。
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE, ElementType.TYPE_USE}):定义了注解可以应用的目标元素类型,包括方法、字段、参数、局部变量和类型使用。
@Constraint(validatedBy = {IsMobileValidator.class}):这是最重要的部分,它将注解标记为一个约束(验证规则),并指定了验证器类 IsMobileValidator 来执行实际的验证逻辑。
此外,该注解还定义了几个属性:
required():指定该字段是否必须填写,默认为 true
message():验证失败时的默认错误消息,这里是"手机号码格式错误"
groups():验证分组,默认为空
payload():有效载荷,默认为空
当使用该注解时,会调用 IsMobileValidator 类进行验证。验证器会根据 required 属性的值来决定验证逻辑:
如果 required 为 true,则必须验证手机号码格式
如果 required 为 false,则允许为空,但如果填写了则必须符合手机号码格式
具体的手机号码格式验证由 ValidatorUtil.isMobile() 方法完成,使用正则表达式 1 (3-9) 0-9{9}$ 来验证手机号码是否符合中国手机号码的格式。
java
package com.yxl.seckill.validator;
import com.yxl.seckill.util.ValidatorUtil;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.util.StringUtils;
public class IsMobileValidator implements ConstraintValidator<IsMobile, String>
{
private boolean required = false;
@Override
public void initialize(IsMobile constraintAnnotation) {
required = constraintAnnotation.required();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (required){
return ValidatorUtil.isMobile(value);
}else {
if (StringUtils.isEmpty(value)){
return true;
}else {
return ValidatorUtil.isMobile(value);
}
}
}
}
ConstraintValidator 是一个泛型接口,专门用于实现自定义约束验证逻辑。在我们的项目中,IsMobileValidator 类实现了这个接口来验证手机号码格式。
- 基本结构
ConstraintValidator 接口有两个泛型参数:
第一个参数:对应的注解类型(在我们项目中是 IsMobile)
第二个参数:被验证的数据类型(在我们项目中是 String)
- 核心方法
ConstraintValidator 接口定义了两个重要方法:
initialize() - 初始化方法:
在验证开始前执行一次
用于获取注解中的配置参数
在我们的例子中,获取 required 属性值
isValid() - 验证方法:
执行实际的验证逻辑
返回 true 表示验证通过,false 表示验证失败
在我们的例子中,根据 required 值和手机号格式进行验证
浏览器会话
HttpSession ,通常称为session ,是JavaWeb中用于表示会话的机制,它是一个存储在Web服务器端的Java对象,代表用户和Web服务器的一次会话。会话的本质是从session对象 的创建到最终超时销毁的过程。HttpSession 对象可以将会话状态保存在服务器端,而Cookie则可以将会话状态保存在客户端浏览器中。

JSESSIONID 是一种用于在客户端和服务器之间维持会话状态的机制。由于 HTTP 协议是无状态的,每次请求都是独立的,因此需要一种方法来识别同一个用户的多个请求。JSESSIONID 就是用来解决这个问题的。
存在问题
之前的代码在我们之后一台应用系统,所有操作都在一台Tomcat上,没有什么问题。当我们部署多台系统,配合Nginx的时候会出现用户登录的问题。
Web 服务器: 一台 2 核 4G 的服务器可能能够处理几千个并发连接,具体取决于 Web 应用程序的复杂程度和请求类型。 数据库服务器: 一台 2 核 4G 的服务器可能能够处理几百个并发连接,具体取决于数据库类型和查询复杂程度。
当部署在多台服务器上时,由于 Nginx 使用默认负载均衡策略(轮询),请求将会按照时间顺序逐一分发到后端应用上。也就是说刚开始我们在 Tomcat1 登录之后,用户信息放在 Tomcat1 的 Session 里。过了一会,请求又被 Nginx 分发到了 Tomcat2 上,这时 Tomcat2 上 Session 里还没有用户信息,于是又要登录。

使用Redis进行解决
使用Redis中间件进行session缓存。
Redis安装 https://download.csdn.net/blog/column/10939729/115372992
安装完之后 进行访问 _redis-cli.exe -h 127.0.0.1 -p 6379 _
查看键值 keys *
Redis需要进行Config配置, \xAC\xED\x00\x05t\x00是Redis键值对中出现的前缀,它是由于RedisTemplate的配置问题导致的。不影响程序运行。
java
package com.yxl.seckill.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig
{
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
//key序列器
redisTemplate.setKeySerializer(new StringRedisSerializer());
//value序列器
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//Hash类型 key序列器
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//Hash类型 value序列器
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
Springboot整合Redis
- 添加全局pom.xml配置
json
<!-- spring data redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 引入hutool工具
json
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.38</version>
</dependency>
- 进行application.yml文件配置
json
redis:
#超时时间
timeout: 10000ms
#服务器地址
host: 127.0.0.1
#服务器端口
port: 6379
#数据库
database: 0
#密码
# password: root
lettuce:
pool:
#最大连接数,默认8
max-active: 1024
#最大连接阻塞等待时间,默认-1
max-wait: 10000ms
#最大空闲连接
max-idle: 200
#最小空闲连接
min-idle: 5
在TUserServiceImpl注入 使用

json
@Autowired
private RedisTemplate redisTemplate;


在TUserServiceImpl 进行登录操作改写,将userTiket从session缓存改到redis中
java
package com.yxl.seckill.service.impl;
import com.yxl.seckill.entity.TUser;
import com.yxl.seckill.mapper.TUserMapper;
import com.yxl.seckill.service.ITUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yxl.seckill.util.CookieUtil;
import com.yxl.seckill.util.JsonUtil;
import com.yxl.seckill.util.MD5Util;
import com.yxl.seckill.util.UUIDUtil;
import com.yxl.seckill.vo.CommonResult;
import com.yxl.seckill.vo.LoginVo;
import com.yxl.seckill.vo.ResponseCode;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* <p>
* 服务实现类
* </p>
*
* @author yxl
* @since 2025-09-23
*/
@Service
public class TUserServiceImpl extends ServiceImpl<TUserMapper, TUser> implements ITUserService {
@Autowired
private TUserMapper userMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public CommonResult<String> login(HttpServletRequest request, HttpServletResponse response, LoginVo loginVo)
{
try
{
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
TUser user = userMapper.selectById(mobile);
if (user == null)
{
return CommonResult.error(ResponseCode.LOGINVO_ERROR);
}
//校验密码
if (!MD5Util.formPassToDBPass(password, user.getSlat()).equals(user.getPassword()))
{
return CommonResult.error(ResponseCode.LOGINVO_ERROR);
}
//生成cookie
String ticket = UUIDUtil.uuid();
// request.getSession().setAttribute(ticket,user);
// redisTemplate.opsForValue().set("user:" + ticket,
// JsonUtil.object2JsonStr(user), 60 * 2, java.util.concurrent.TimeUnit.SECONDS);
redisTemplate.opsForValue().set("user:" + ticket,
JsonUtil.object2JsonStr(user));
CookieUtil.setCookie(request,response,"userTicket",ticket);
return CommonResult.success(ticket);
}
catch (Exception e)
{
e.printStackTrace();
return CommonResult.error(ResponseCode.ERROR);
}
}
@Override
public TUser getByUserTicket(String userTicket, HttpServletRequest request, HttpServletResponse response)
{
if (!StringUtils.hasText(userTicket)) {
return null;
}
String userJson = (String) redisTemplate.opsForValue().get("user:" +
userTicket);
if (!StringUtils.hasText(userJson)) {
return null;
}
TUser user = JsonUtil.jsonStr2Object(userJson, TUser.class);
if (null != user) {
CookieUtil.setCookie(request,response,"userTicket",userTicket);
}
return user;
}
}
getByUserTicket 改写
json
@Override
public TUser getByUserTicket(String userTicket, HttpServletRequest request, HttpServletResponse response)
{
if (!StringUtils.hasText(userTicket)) {
return null;
}
String userJson = (String) redisTemplate.opsForValue().get("user:" +
userTicket);
if (!StringUtils.hasText(userJson)) {
return null;
}
TUser user = JsonUtil.jsonStr2Object(userJson, TUser.class);
if (null != user) {
CookieUtil.setCookie(request,response,"userTicket",userTicket);
}
return user;
}
MD5Util.javaUUIDUtil.javaValidatorUtil.javaCookieUtil.javaJsonUtil.java
课下练习
Nginx web服务器入门
\[2.1 springboot 快速整合mybits\]
\[2.2 redis 安装\]