黑马点评-用户登录

news2025/5/19 18:22:02

文章目录

        • 用户登录
          • 发送短信验证码
          • 注册/登录
          • 校验登录

用户登录
发送短信验证码
public Result sendCode(String phone, HttpSession session) {
        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        // 3.符合,生成验证码
        String code = RandomUtil.randomNumbers(6);

        // 4.保存验证码到 reids
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);

        // 5.发送验证码
        log.debug("发送短信验证码成功,验证码:{}", code);
        // 返回ok
        return Result.ok();
    }

基本逻辑就是:

用户提交手机号,首先校验手机号格式是否正确,正确则生成验证码(模拟发送成功)并将该验证码保存在Redis中,用于用户登录时取出来对比是否相同。

注册/登录
public 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)//忽略值为 null 的字段
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));//将每个字段的值转换为字符串类型(需要每个字段重写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.返回token
        return Result.ok(token);
    }

基本逻辑:

用户点击“登录”,我们这里拿到他的手机号和验证码,再次进行手机号格式和验证码(从redis里取出来)校验,通过后,我们要知道这个人到底注册过没有,从而进行相应的注册或登录。

我们从数据库根据手机号查找,如果没有,我们就根据手机号创建用户,保存在数据库中,同时,生成随机token作为key,也将用户信息也保存在redis中,这里redis采用Hash结构(Redis 的 Hash 结构中,键和值都是字符串类型,需要进行类型转换)。

校验登录
//拦截器1
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取请求头里的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;//直接放行
        }

        //根据token获取redis用户数据
        String key = RedisConstants.LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
                .entries(key);

        //判断用户是否存在
        if (userMap.isEmpty()) {
            return true;//直接放行
        }

        //将查询到的数据转为userDto对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);

        //保存用户信息到ThreadLocal
        UserHolder.saveUser(userDTO);

        //刷新token有效期30分钟
        stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);

        return true;
    }

基本逻辑:

就是设置一个全局拦截器,用户想进行各种操作之前,先校验用户登录状态,也就是根据token从redis中看有没有这个用户,有则保存用户到ThreadLocal,无则拦截结束。

而为什么要用到ThreadLocal呢?

因为拦截器拦到的用户信息,我们也要想办法将它们传递给Controller层,就要使用到ThreadLocal(保存每个用户信息,互不干扰)。

同时,只要用户在不断访问,就需要不断更新redis的有效期30分钟。超过时间段内没有访问,则redis就要清空这个记录。

不过呢,这里存在一个问题,比如说用户登录后,但他一直访问的是不需要拦截的页面,这里就没有进行更新redis有效期操作,那用户30分钟后就被清空了redis数据,这不合常理。

上面那个拦截器我们将它设置为拦截所有请求路径,重新再设置一个拦截器,这个拦截器只拦截需要登录的

//拦截器2
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断是否需要拦截(ThreadLocal是否有用户)
        if(UserHolder.getUser()==null){
            //没有就拦截
            response.setStatus(401);
            return false;
        }
        //有用户,就放行
        return true;
    }

这样重新设置两个拦截器,拦截器1(拦截所有)的功能就是:

1.获取token

2.查询redis用户

3.保存到ThreadLocal

4.刷新token有效期

5.放行

拦截器2(拦截需要登录的)的功能就是:

查询ThreadLocal:不存在,就拦截,存在就继续。

在这里插入图片描述
感谢大家的点赞支持>W<

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十二、Hive 函数

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月1日 专栏&#xff1a;Hive教程 在数据处理的广阔天地中&#xff0c;我们常常需要对数据进行转换、计算、清洗或提取特定信息。Hive 提供了强大的内置运算符和丰富的内置函数库&#xff0c;它们就像魔法师手中的魔法棒&…

No More Adam: 新型优化器SGD_SaI

一.核心思想和创新点 2024年12月提出的SGD-SaI算法&#xff08;Stochastic Gradient Descent with Scaling at Initialization&#xff09;本质上是一种在训练初始阶段对不同参数块&#xff08;parameter block&#xff09;基于**梯度信噪比&#xff08;g-SNR, Gradient Signa…

JSP链接MySQL8.0(Eclipse+Tomcat9.0+MySQL8.0)

所用环境 Eclipse Tomcat9.0 MySQL8.0.21(下载&#xff1a;MySQL Community Server 8.0.21 官方镜像源下载 | Renwole&#xff09; mysql-connector-java-8.0.21&#xff08;下载&#xff1a;MySQL :: Begin Your Download&#xff09; .NET Framework 4.5.2&#xff08;下…

SEO长尾词与关键词优化实战

内容概要 在SEO优化体系中&#xff0c;长尾关键词与核心关键词的协同作用直接影响流量获取效率与用户转化路径。长尾词通常由3-5个词组构成&#xff0c;搜索量较低但意图明确&#xff0c;能精准触达细分需求用户&#xff1b;核心关键词则具备高搜索量与广泛覆盖能力&#xff0…

机器学习-人与机器生数据的区分模型测试-数据处理1

附件为训练数据&#xff0c;总体的流程可以作为参考。 导入依赖 import pandas as pd import os import numpy as np from sklearn.model_selection import train_test_split,GridSearchCV from sklearn.ensemble import RandomForestClassifier,VotingClassifier from skle…

HelloWorld

HelloWorld 新建一个java文件 文件后缀名为 .javahello.java【注意】系统可能没有显示文件后缀名&#xff0c;我们需要手动打开 编写代码 public class hello {public static void main(String[] args) {System.out.print(Hello,World)} }编译 javac java文件&#xff0c;会生…

SEO 优化实战:ZKmall模板商城的 B2C商城的 URL 重构与结构化数据

在搜索引擎算法日益复杂的今天&#xff0c;B2C商城想要在海量信息中脱颖而出&#xff0c;仅靠优质商品和营销活动远远不够。ZKmall模板商城以实战为导向&#xff0c;通过URL 重构与结构化数据优化两大核心策略&#xff0c;帮助 B2C 商城实现从底层架构到搜索展示的全面升级&…

数字万用表与指针万用表使用方法及注意事项

在电子测量领域&#xff0c;万用表是极为常用的工具&#xff0c;数字万用表和指针万用表各具特点。熟练掌握它们的使用方法与注意事项&#xff0c;能确保测量的准确性与安全性。下面为您详细介绍&#xff1a; 一 、数字万用表按钮功能 > 进入及退出手动量程模式 每 按 […

【读代码】端到端多模态语言模型Ultravox深度解析

一、项目基本介绍 Ultravox是由Fixie AI团队开发的开源多模态大语言模型,专注于实现音频-文本的端到端实时交互。项目基于Llama 3、Mistral等开源模型,通过创新的跨模态投影架构,绕过了传统语音识别(ASR)的中间步骤,可直接将音频特征映射到语言模型的高维空间。 核心优…

RabbitMQ工作流程及使用方法

一、什么是RabbitMQ RabbitMQ 是一款基于 ‌AMQP&#xff08;高级&#xff0c;消息队列协议&#xff09;‌ 的开源消息中间件&#xff0c;专为分布式系统设计&#xff0c;用于实现应用程序间的异步通信&#xff0c;其核心功能是通过 ‌消息代理&#xff08;Message Broker&…

算法:分治法

实验内容 在一个2kⅹ2k个方格组成的棋盘中&#xff0c;若恰有一个方格与其他方格不同&#xff0c;则称该方格为特殊方格&#xff0c;且称该棋盘为一特殊棋盘。 显然&#xff0c;特殊方格出现的位置有4k 种情况&#xff0c;即k>0,有4k 种不同的特殊棋盘 棋盘覆盖&#xff1a…

MySQL初阶:sql事务和索引

索引&#xff08;index&#xff09; 可以类似理解为一本书的目录&#xff0c;一个表可以有多个索引。 索引的意义和代价 在MySQL中使用select进行查询时会经过&#xff1a; 1.先遍历表 2.将条件带入每行记录中进行判断&#xff0c;看是否符合 3.不符合就跳过 但当表中的…

docker部署第一个Go项目

1.前期准备 目录结构 main.go package mainimport ("fmt""github.com/gin-gonic/gin""net/http" )func main() {fmt.Println("\n .::::.\n .::::::::.\n :::::::::::\n …

Visual Studio2022跨平台Avalonia开发搭建

由于我已经下载并安装了 VS2022版本&#xff0c;这里就跳过不做阐述。 1.安装 Visual Studio 2022 安装时工作负荷Tab页勾选 ‌“.NET 桌面开发”‌ 和“Visual Studio扩展开发”‌ &#xff0c;这里由于不是用的微软的MAUI&#xff0c;所以不用选择其他的来支持跨平台开发&a…

css iconfont图标样式修改,js 点击后更改样式

背景&#xff1a; 在vue项目中&#xff0c;通过点击/鼠标覆盖&#xff0c;更改选中元素的样式&#xff0c;可以通过js逻辑&#xff0c;也可以根据css样式修改。包括以下内容&#xff1a;iconfont图标的引入以及使用&#xff0c;iconfont图标样式修改【导入文件是纯白&#xff0…

开源项目实战学习之YOLO11:12.4 ultralytics-models-sam-memory_attention.py源码分析

👉 点击关注不迷路 👉 点击关注不迷路 👉 另外,前些天发现了一个巨牛的AI人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。感兴趣的可以点击相关跳转链接。 点击跳转到网站。 ultralytics-models-sam 1.sam-modules-memory_attention.pyblocks.py: 定义模…

【沉浸式求职学习day42】【算法题:滑动窗口】

沉浸式求职学习 长度最小的子数组水果成篮 关于算法题&#xff1a;滑动窗口的几个题目 长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 s &#xff0c;找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组&#xff0c;并返回其长度。如果不存在符合条件的子数组…

LIIGO ❤️ RUST 12 YEARS

LIIGO &#x1f496; RUST 12 YEARS 今天是RUST语言1.0发布十周年纪念日。十年前的今天&#xff0c;2015年的今天&#xff0c;Rust 1.0 正式发行。这是值得全球Rust支持者隆重纪念的日子。我借此机会衷心感谢Rust语言创始人Graydon Hoare&#xff0c;Mozilla公司&#xff0c;以…

Linux基础开发工具二(gcc/g++,自动化构建makefile)

3. 编译器gcc/g 3.1 背景知识 1. 预处理&#xff08;进行宏替换/去注释/条件编译/头文件展开等) 2. 编译&#xff08;生成汇编) 3. 汇编&#xff08;生成机器可识别代码&#xff09; 4. 连接&#xff08;生成可执行文件或库文件) 3.2 gcc编译选项 格式 &#xff1a; gcc …

全局异常处理:如何优雅地统一管理业务异常

在软件开发中&#xff0c;异常处理是保证系统健壮性的重要环节。一个良好的异常处理机制不仅能提高代码的可维护性&#xff0c;还能为使用者提供清晰的错误反馈。本文将介绍如何通过全局异常处理和业务异常统一处理来编写更加优雅的代码。 一、传统异常处理的痛点 1.1 典型问…