【Redis_2】短信登录

server/2025/2/4 8:08:00/

一、基于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/server/164836.html

相关文章

对顾客行为的数据分析:融入2+1链动模式、AI智能名片与S2B2C商城小程序的新视角

摘要&#xff1a;随着互联网技术的飞速发展&#xff0c;企业与顾客之间的交互方式变得日益多样化&#xff0c;移动设备、社交媒体、门店、电子商务网站等交互点应运而生。这些交互点不仅为顾客提供了便捷的服务体验&#xff0c;同时也为企业积累了大量的顾客行为数据。本文旨在…

【含文档+PPT+源码】基于大数据的交通流量预测系统

项目介绍 本课程演示的是一款基于Python的图书管理系统的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 带你从零开始部署运行本套系统 该项目附…

git基础使用--4---git分支和使用

文章目录 git基础使用--4---git分支和使用1. 按顺序看2. 什么是分支3. 分支的基本操作4. 分支的基本操作4.1 查看分支4.2 创建分支4.3 切换分支4.4 合并冲突 git基础使用–4—git分支和使用 1. 按顺序看 -git基础使用–1–版本控制的基本概念 -git基础使用–2–gti的基本概念…

python Flask-Redis 连接远程redis

当使用Flask-Redis连接远程Redis时&#xff0c;首先需要安装Flask-Redis库。可以通过以下命令进行安装&#xff1a; pip install Flask-Redis然后&#xff0c;你可以使用以下示例代码连接远程Redis&#xff1a; from flask import Flask from flask_redis import FlaskRedisa…

基于物联网技术的实时数据流可视化研究(论文+源码)

1系统方案设计 根据系统功能的设计要求&#xff0c;展开基于物联网技术的实时数据流可视化研究设计。如图2.1所示为系统总体设计框图&#xff0c;系统以STM32单片机做为主控制器&#xff0c;通过DHT11、MQ-2、光照传感器实现环境中温湿度、烟雾、光照强度数据的实时检测&#x…

Vue3 结合 .NetCore WebApi 前后端分离跨域请求简易实例

1、本地安装Vue3环境 参考&#xff1a;VUE3中文文档-快速上手 注意&#xff1a;初始安装vue时&#xff0c;需要安装router&#xff0c;否则后续也要安装 2、安装axios组件 比如&#xff1a;npm install axioslatest 或 pnpm install axioslatest 3、设置跨域请求代理 打开…

Java程序设计:掌握核心语法与经典案例

一、引言 Java作为一种广泛使用的编程语言&#xff0c;以其简洁、高效、跨平台等特性深受开发者喜爱。掌握Java的核心语法是成为一名优秀Java程序员的基础。本文将从Java的基本语法入手&#xff0c;逐步深入到经典案例的分析&#xff0c;帮助读者快速掌握Java程序设计的关键要…

A7. Jenkins Pipeline自动化构建过程,可灵活配置多项目、多模块服务实战

服务容器化构建的环境配置构建前需要解决什么下面我们带着问题分析构建的过程:1. 如何解决jenkins执行环境与shell脚本执行环境不一致问题?2. 构建之前动态修改项目的环境变量3. 在通过容器打包时避免不了会产生比较多的不可用的镜像资源,这些资源要是不及时删除掉时会导致服…