【Redis_2】短信登录

embedded/2025/2/6 19:25:49/

一、基于Session实现登录

RegexUtils:是定义的关于一些格式的正则表达式的工具箱

package com.hmdp.utils;import cn.hutool.core.util.StrUtil;public class RegexUtils {/*** 是否是无效手机格式* @param phone 要校验的手机号* @return true:符合,false:不符合*/public static boolean isPhoneInvalid(String phone){return mismatch(phone, RegexPatterns.PHONE_REGEX);}/*** 是否是无效邮箱格式* @param email 要校验的邮箱* @return true:符合,false:不符合*/public static boolean isEmailInvalid(String email){return mismatch(email, RegexPatterns.EMAIL_REGEX);}/*** 是否是无效验证码格式* @param code 要校验的验证码* @return true:符合,false:不符合*/public static boolean isCodeInvalid(String code){return mismatch(code, RegexPatterns.VERIFY_CODE_REGEX);}// 校验是否不符合正则格式private static boolean mismatch(String str, String regex){if (StrUtil.isBlank(str)) {return true;}return !str.matches(regex);}
}

Result:返回的结果类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.List;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {private Boolean success;private String errorMsg;private Object data;private Long total;public static Result ok(){return new Result(true, null, null, null);}public static Result ok(Object data){return new Result(true, null, data, null);}public static Result ok(List<?> data, Long total){return new Result(true, null, data, total);}public static Result fail(String errorMsg){return new Result(false, errorMsg, null, null);}
}

(一)发送短信验证码

在这里插入图片描述
在这里插入图片描述
userContorller:

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate IUserService userService;@Resourceprivate IUserInfoService userInfoService;/*** 发送手机验证码*/@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {// 发送短信验证码并保存验证码return userService.sendCode(phone, session);}}

userServiceImpl:

    @Overridepublic Result sendCode(String phone, HttpSession session) {//1、校验手机号if(RegexUtils.isPhoneInvalid(phone)){//2、如果不符合,返回错误信息return Result.fail("手机号格式错误");}//3、符合,生成验证码(随机生成器hutool)String code = RandomUtil.randomNumbers(6);//4、保存验证码到sessionsession.setAttribute("code" ,code);//5、发送验证码log.info("短信验证码发送成功,验证码:{}",code);//6、返回okreturn  Result.ok();}

(二)短信验证码登录、注册

在这里插入图片描述
在这里插入图片描述

import lombok.Data;@Data
public class LoginFormDTO {private String phone;private String code;private String password;
}
import lombok.Data;
//用作在控制台输出时显示少量信息
//避免显示敏感信息@Data
public class UserDTO {private Long id;private String nickName;private String icon;
}

userController中:

    /*** 登录功能* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码*/@PostMapping("/login")public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){// TODO 实现登录功能return userService.login(loginForm,session);}

userServiceImpl中:

    @Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1、校验手机号String phone = loginForm.getPhone();if(RegexUtils.isPhoneInvalid(phone)){return Result.fail("手机号格式错误!");}//2、校验验证码Object cacheCode = session.getAttribute("code");String code = loginForm.getCode();//比较验证码是否一致if(cacheCode == null || !cacheCode.toString().equals(code)){//3、不一致报错return Result.fail("验证码错误!");}//4、一致,根据手机号查询用户 select * form tb_user where phone = User user = query().eq("phone", phone).one();//5、判断用户是否存在if(user == null){//不存在,就创建一个新用户并保存user = createUserWithPhone(phone);}//保存用户在session中session.setAttribute("user", BeanUtil.copyProperties(user,UserDTO.class));return Result.ok();}private User createUserWithPhone(String phone) {//创建用户User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomNumbers(10));//保存用户save(user);return user;}

(三)校验登录状态

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
拦截器

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import java.util.Map;
import java.util.concurrent.TimeUnit;import static net.sf.jsqlparser.util.validation.metadata.NamedObject.user;public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1 获取sessionHttpSession session = request.getSession();//2 获取session中的用户Object user = session.getAttribute("user");//3 判断用户是否存在if(user == null){//4 不存在,拦截,返回401状态码response.setStatus(401);return false;}//5 存在,保存用户信息到ThreadlocalUserHolder.saveUser((UserDTO) user);//放行return true;}}

配置拦截器:

package com.hmdp.config;import com.hmdp.utils.LoginInterceptor;
import com.hmdp.utils.RefreshTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;
/*
* 配置拦截器
* */
@Configuration
public class MvcConfig implements WebMvcConfigurer {@Override//添加拦截器public void addInterceptors(InterceptorRegistry registry) {//登录拦截器registry.addInterceptor(new LoginInterceptor())//拦截部分.excludePathPatterns("/shop/**","/shop-type/**","/upload/**","/voucher/**","/blog/hot","/user/code","/user/login","/voucher/seckill");}}

二、基于Redis实现共享Session登录

集群的Session共享问题:多台Tomcat不共享session存储空间,切换tomcat时会导致数据丢失的问题。
在这里插入图片描述
在这里插入图片描述
常量类:

package com.hmdp.utils;public class RedisConstants {public static final String LOGIN_CODE_KEY = "login:code:";public static final Long LOGIN_CODE_TTL = 2L;public static final String LOGIN_USER_KEY = "login:token:";public static final Long LOGIN_USER_TTL = 30L;public static final Long CACHE_NULL_TTL = 2L;public static final Long CACHE_SHOP_TTL = 30L;public static final String CACHE_SHOP_KEY = "cache:shop:";public static final String LOCK_SHOP_KEY = "lock:shop:";public static final Long LOCK_SHOP_TTL = 10L;public static final String SECKILL_STOCK_KEY = "seckill:stock:";public static final String BLOG_LIKED_KEY = "blog:liked:";public static final String FEED_KEY = "feed:";public static final String SHOP_GEO_KEY = "shop:geo:";public static final String USER_SIGN_KEY = "sign:";
}

userServiceImpl:

package com.hmdp.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import javax.servlet.http.HttpSession;import java.util.HashMap;
import java.util.Map;import java.util.concurrent.TimeUnit;import static com.hmdp.utils.RedisConstants.*;
import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;/*** <p>* 服务实现类* </p>**/
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result sendCode(String phone, HttpSession session) {// 1.校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}// 3.符合,生成验证码String code = RandomUtil.randomNumbers(6);// 4.保存验证码到 redisstringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);// 5.发送验证码log.debug("发送短信验证码成功,验证码:{}", code);// 返回okreturn Result.ok();}@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 1.校验手机号String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}// 3.从redis获取验证码并校验String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);String code = loginForm.getCode();if (cacheCode == null || !cacheCode.equals(code)) {// 不一致,报错return Result.fail("验证码错误");}// 4.一致,根据手机号查询用户 select * from tb_user where phone = ?User user = query().eq("phone", phone).one();// 5.判断用户是否存在if (user == null) {// 6.不存在,创建新用户并保存user = createUserWithPhone(phone);}// 7.保存用户信息到 redis// 7.1.随机生成token,作为登录令牌String token = UUID.randomUUID().toString(true);// 7.2.将User对象转为HashMap存储UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));// 7.3.存储String tokenKey = LOGIN_USER_KEY + token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);// 7.4.设置token有效期stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.返回tokenreturn Result.ok(token);}/*@Overridepublic Result sendCode(String phone, HttpSession session) {//1、校验手机号if(RegexUtils.isPhoneInvalid(phone)){//2、如果不符合,返回错误信息return Result.fail("手机号格式错误");}//3、符合,生成验证码(随机生成器hutool)String code = RandomUtil.randomNumbers(6);//4、保存验证码到sessionsession.setAttribute("code" ,code);//5、发送验证码log.info("短信验证码发送成功,验证码:{}",code);//6、返回okreturn  Result.ok();}*//*@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1、校验手机号String phone = loginForm.getPhone();if(RegexUtils.isPhoneInvalid(phone)){return Result.fail("手机号格式错误!");}//2、校验验证码Object cacheCode = session.getAttribute("code");String code = loginForm.getCode();//比较验证码是否一致if(cacheCode == null || !cacheCode.toString().equals(code)){//3、不一致报错return Result.fail("验证码错误!");}//4、一致,根据手机号查询用户 select * form tb_user where phone = User user = query().eq("phone", phone).one();//5、判断用户是否存在if(user == null){//不存在,就创建一个新用户并保存user = createUserWithPhone(phone);}//保存用户在session中session.setAttribute("user", BeanUtil.copyProperties(user,UserDTO.class));return Result.ok();}*/private User createUserWithPhone(String phone) {//创建用户User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomNumbers(10));//保存用户save(user);return user;}
}

拦截器

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import java.util.Map;
import java.util.concurrent.TimeUnit;import static net.sf.jsqlparser.util.validation.metadata.NamedObject.user;public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;/* public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/*        //1 获取sessionHttpSession session = request.getSession();//2 获取session中的用户Object user = session.getAttribute("user");//3 判断用户是否存在if(user == null){//4 不存在,拦截,返回401状态码response.setStatus(401);return false;}//5 存在,保存用户信息到ThreadlocalUserHolder.saveUser((UserDTO) user);//放行return true;*/
//----------------------------------------------------------redis-------------------------------------------------------------//1 获取请求头中的tokenString token = request.getHeader("authorization");if(StrUtil.isBlank(token)){//不存在,拦截,返回401状态码response.setStatus(401);return false;}//2 基于TOKEN获取redis中的用户String key = RedisConstants.LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);//3 判断用户是否存在if(userMap.isEmpty()){//4 不存在,拦截,返回401状态码response.setStatus(401);return false;}//5 将查询到的hash数据转化为UserDTOUserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//6 存在,保存用户信息到ThreadlocalUserHolder.saveUser(userDTO);//7 刷新token 有效期stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);//8 放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//移除用户UserHolder.removeUser();}
}

调用拦截器

package com.hmdp.config;import com.hmdp.utils.LoginInterceptor;
import com.hmdp.utils.RefreshTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;
/*
* 配置拦截器
* */
@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Override//添加拦截器public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns("/shop/**","/shop-type/**","/upload/**","/voucher/**","/blog/hot","/user/code","/user/login");}}

http://www.ppmy.cn/embedded/160109.html

相关文章

git-secret 使用教程

以下是一份详细的 git-secret 使用教程&#xff0c;包含常见场景的 Bash 代码示例&#xff1a; 1. 安装 git-secret # Ubuntu/Debian sudo apt-get install git-secret# macOS (Homebrew) brew install git-secret# 其他 Linux (Snap) sudo snap install git-secret# 验证安装…

25.2.4(学习记录)

今天学的东西太散了&#xff0c;感觉还没摸着头脑&#xff0c;但目前要解决的问题是map这个数据结构。今天学习的时候看相关博客&#xff0c;大部分都不是C语言的实现&#xff0c;而且又提到映射&#xff0c;二叉树的一种红黑树等等。 其实还是因为在写洛谷这道题-------->…

Native Memory Tracking 与 RSS的差异问题

一 问题现象 前一段时间用nmt查看jvm进程的栈区占用的内存大小。测试代码如下 public class ThreadOOM {public static void main(String[] args) {int i 1;while (i < 3000) {Thread thread new TestThread();thread.start();System.out.println("thread : "…

BUU19 [BJDCTF2020]Easy MD51

题目 当点进去不知道干啥的时候&#xff1a;1.看源代码 2.抓包 3.看网络请求 F12 用bp抓包&#xff0c;在response消息头中有hint提示&#xff1a; select * from admin where passwordmd5($pass,true) 如果md5($pass,true)后是 or 1 那么整句话就是password or 1&a…

联想拯救者开机进入bios

如果你的联想拯救者&#xff08;Lenovo Legion&#xff09;笔记本电脑开机后直接进入 BIOS 设置界面&#xff0c;可能是以下原因之一导致的。以下是解决方法&#xff1a; 1. 检查启动顺序 进入 BIOS 后&#xff0c;找到 Boot&#xff08;启动&#xff09;选项卡。检查启动顺序…

【算法设计与分析】实验5:贪心算法—装载及背包问题

目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 掌握贪心算法求解问题的思想&#xff1b;针对不同问题&#xff0c;会利用贪心算法进行问题建模、求解以及时间复杂度分析&#x…

MySQL 进阶专题:索引(索引原理/操作/优缺点/B+树)

在数据库的秋招面试中&#xff0c;索引&#xff08;Index&#xff09;是一个经典且高频的题目。索引的作用类似于书中的目录&#x1f4d6;&#xff0c;它能够显著加快数据库查询的速度。本文将深入探讨索引的概念、作用、优缺点以及背后的数据结构&#xff0c;帮助你从原理到应…

「全网最细 + 实战源码案例」设计模式——桥接模式

核心思想 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;将抽象部分与其实现部分分离&#xff0c;使它们可以独立变化。降低代码耦合度&#xff0c;避免类爆炸&#xff0c;提高代码的可扩展性。 结构 1. Implementation&#xff08;实现类…