黑马程序员项目-黑马点评

黑马点评1

短信登录

基于Session实现登录流程

发送验证码:
用户在提交手机号后,会校验手机号是否合法,如果不合法,则要求用户重新输入手机号
如果手机号合法,后台此时生成对应的验证码,同时将验证码进行保存,然后再通过短信的方式将验证码发送给用户
短信验证码登录、注册:
用户将验证码和手机号进行输入,后台从session中拿到当前验证码,然后和用户输入的验证码进行校验,如果不一致,则无法通过校验,如果一致,则后台根据手机号查询用户,如果用户不存在,则为用户创建账号信息,保存到数据库,无论是否存在,都会将用户信息保存到session中,方便后续获得当前登录信息
校验登录状态:
用户在请求时候,会从cookie中携带者JsessionId到后台,后台通过JsessionId从session中拿到用户信息,如果没有session信息,则进行拦截,如果有session信息,则将用户信息保存到threadLocal中,并且放行
1653066208144.png

  • 发送验证码
    @Override
    public Result sendCode(String phone, HttpSession session) {
        //校验 手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            //不符合
            return Result.fail("手机号格式不正确");
        }
        //生成验证码
        String code = RandomUtil.randomNumbers(6);
        //保存到session
        session.setAttribute("code", code);
        //发送验证码
        log.debug("手机号:{},验证码:{}", phone, code);
        return Result.ok();
    }
  • 用户登录
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //校验手机号
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            //不符合
            return Result.fail("手机号格式不正确");
        }
        //校验验证码
        Object hasSendCode = session.getAttribute("code");
        String code = loginForm.getCode();
        if (hasSendCode == null || !hasSendCode.toString().equals(code)) {
            return Result.fail("验证码不正确");
        }
        //校验通过,查询用户
        User user = this.query().eq("phone", phone).one();
        if (user == null) {
            user = createUserWithPhone(phone);
        }
        //保存用户信息到session
        session.setAttribute("user", user);
        return Result.ok();
    }

    private User createUserWithPhone(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        this.save(user);
        return user;
    }

此时可以正常登录

手机号格式:

  • 手机号以 “1” 开头。
  • 第二位是一个特定的数字,可以是 3、4、5、6、7、8、9 中的一个。
  • 接下来的 9 位数字可以是 0 到 9 的任意数字。

登录拦截

  • 添加拦截器设置
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取Session
        HttpSession session = request.getSession();
        //获取用户信息
        Object user = session.getAttribute("user");
        if (user == null) {
            //不存在,拦截,返回401状态码
            response.setStatus(401);
            return false;
        }
        //存在,保存到ThreadLocal中
        UserHolder.saveUser((User) user);
        return true;
    }
}
  • 配置拦截器生效
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns("/shop/**",
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                );
    }
}

用户信息脱敏

登录的时候修改:

        //保存用户信息到session
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        session.setAttribute("user", userDTO);
        return Result.ok();

拦截器修改:

        //存在,保存到ThreadLocal中
        UserHolder.saveUser((UserDTO) user);

UserHolder修改 :

public class UserHolder {
    private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();

    public static void saveUser(UserDTO user){
        tl.set(user);
    }

    public static UserDTO getUser(){
        return tl.get();
    }

    public static void removeUser(){
        tl.remove();
    }
}

Session共享

session会存入到tomcat服务器 中,但是后端如果有多个tomcat服务器,就不好 实现数据共享,早期的办法是将 session拷贝到不同 的tomcat服务器上面,现在有了redis,可以直接使用redis来解决session共享的问题,两种存储方式

  • 使用string存储
  • 使用哈希存储

1653319261433.png
这里选择使用哈希存储

设计key的时候可以随机 生成一个token给 前端,让前端带着token来访问后端
整体流程如下:
1653319474181.png

  • 修改发送验证码逻辑,保存到redis
    @Override
    public Result sendCode(String phone, HttpSession session) {
        //校验 手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            //不符合
            return Result.fail("手机号格式不正确");
        }
        //生成验证码
        String code = RandomUtil.randomNumbers(6);
        //保存到session
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        //发送验证码
        log.debug("手机号:{},验证码:{}", phone, code);
        return Result.ok();
    }

修改登录逻辑,从redis读数据:

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //校验手机号
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            //不符合
            return Result.fail("手机号格式不正确");
        }
        //从redis中获取验证码
        String hasSendCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        if (hasSendCode == null || !hasSendCode.toString().equals(code)) {
            return Result.fail("验证码不正确");
        }
        //校验通过,查询用户
        User user = this.query().eq("phone", phone).one();
        if (user == null) {
            user = createUserWithPhone(phone);
        }
        //保存用户信息到redis中
        //生成token
        String token = UUID.randomUUID().toString();
        //将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())
        );
        //存储
        String toKenKey = LOGIN_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(toKenKey, userMap);
        //设置有效期
        stringRedisTemplate.expire(toKenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return Result.ok(token);
    }

解决登录状态刷新问题

目前的拦截机制:
1653320822964.png

如果访问不需要拦截的路径,这个拦截器不会生效,此时不会刷新令牌

优化方案:
再添加一个拦截器,拦截所有请求
1653320764547.png

具体代码以及解释 如下 :

public class RefreshTokenInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    //这里的构造方法是为了注入StringRedisTemplate,
    // 因为 RefreshTokenInterceptor 是自己 new 出来的,不是 Spring 管理的
    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取请求头中的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;
        }
        //获取redis中的用户
        String key = LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        if (userMap.isEmpty()) {
            //用户不存在
            return true;
        }
        //将hash数据转为UserDTO
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //保存到ThreadLocal中
        UserHolder.saveUser(userDTO);
        //刷新token有效期
        stringRedisTemplate.expire(key, LOGIN_USER_TTL, java.util.concurrent.TimeUnit.DAYS);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //清空ThreadLocal中的数据  防止内存泄漏
        UserHolder.removeUser();
    }
}

配置类里面注入StringRedisTemplate:

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate))
                .addPathPatterns("/**");
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                );
    }
}

登录拦截器修改如下 :

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (UserHolder.getUser() == null) {
            //不存在,拦截,返回401状态码
            response.setStatus(401);
            return false;
        }
        //有用户
        return true;
    }
}

至此,黑马点评的登录逻辑完成。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

论文阅读—— UniDetector(cvpr2023)

arxiv&#xff1a;https://arxiv.org/abs/2303.11749 github&#xff1a;https://github.com/zhenyuw16/UniDetector 一、介绍 通用目标检测旨在检测场景那种的一切目标。现有的检测器依赖于大量数据集 通用的目标检测器应该有两个能力&#xff1a;1、可以利用多种来…

C# 发送邮件

1.安装 NuGet 包 2.代码如下 SendMailUtil using MimeKit; using Srm.CMER.Application.Contracts.CmerInfo; namespace Srm.Mail { public class SendMailUtil { public async static Task<string> SendEmail(SendEmialDto sendEmialDto,List<strin…

WiFi模块在智能家居中的应用与优化

智能家居技术的迅速发展已经改变了我们对家庭的定义。WiFi模块作为智能设备连接的核心&#xff0c;扮演着连接和控制智能家居生态系统的关键角色。本文将深入研究WiFi模块在智能家居中的应用&#xff0c;同时探讨如何通过优化来提升其性能和用户体验。 1. 智能家居中WiFi模块的…

Premiere Pro 2024 v24.0

adobe Premiere Pro 2024 Mac版发布了吗&#xff1f;无论您是编辑社交媒体视频还是大电影&#xff0c;Premiere Pro 都可以帮助您借助工具精心创作有意义的故事。导入和编辑&#xff0c;添加效果&#xff0c;然后将素材导出到任何目标。无论您要创作什么内容&#xff0c;它都可…

微信支付更换证书最详细方法

6、在【商户平台】&#xff0c;输入操作密码&#xff0c;安全验证后生成证书串 7、在【商户平台】&#xff0c;复制证书串 8、在【证书工具】&#xff0c;粘贴证书串&#xff0c;点击下一步&#xff0c;申请证书成功 &#xff08;若提示"证书与本地公私钥不匹配&qu…

高清Logo素材无忧:这5个网站解决所有问题!

今天给大家分享几个素材网站&#xff0c;基本上可以下载各大企业的 Logo&#xff0c;而且还是矢量格式哦~ 即时设计 即时设计是一款国产免费的 Logo 在线设计制作工具&#xff0c;浏览器内打开即用&#xff0c;对于使用系统没有任何限制。在即时设计&#xff0c;你可以从 0 到…

Hive 解析 JSON 字符串数据的实现方式

文章目录 通过方法解析现实示例 通过序列化实现示例 通过方法解析现实 在 Hive 中提供了直接解析 JSON 字符串数据的方法 get_json_object(json_txt, path)&#xff0c;该方法参数解析如下&#xff1a; json_txt&#xff1a;顾名思义&#xff0c;就是 JSON 字符串&#xff1b;…

C++常用格式化输出

在C语言中可以用printf以一定的格式打印字符&#xff0c;C当然也可以。 输入输出及命名空间还不太了解的小伙伴可以看一看C入门讲解第一篇。  在C中&#xff0c;可以用流操作符&#xff08;stream manipulators&#xff09;控制数据的输出格式&#xff0c;这些流操作符定义在2…

RFID管理方案有效提升电力物资管理效率与资产安全

在电力行业&#xff0c;电力资产的管理是一项重要的任务&#xff0c;为了实现对电力资产的精细化管理、入出库监控管理、盘点管理和巡查管理等&#xff0c;电力公司多采用电力资产RFID管理系统&#xff0c;该系统能够实时监控出入库过程&#xff0c;有效防止出入库错误&#xf…

TCP三次握手四次挥手深入

TCP工作在网络协议栈的传输层&#xff0c;在这一层上传输的数据叫段&#xff08;Segment&#xff09; 我们应用程序的数据会先打包到传输层&#xff0c;传输层再交给下层网际层&#xff0c;再交给下层数据链路层 上图中有四个东西是非常重要的&#xff1a; 序号&#xff1a;…

测试常见异常总结

为了更好地保障测试质量&#xff0c;除了测试正向场景&#xff0c;也需要验证软件在异常情况下的行为和反应。本文分享一些测试过程中常见的异常。 通过模拟和触发各种异常情况&#xff0c;测试人员可以验证软件对异常的处理是否符合预期&#xff0c;是否能够正确地处理和恢复。…

Shopee买家通系统一款全自动操作虾皮买家号的软件

Shopee买家通系统可以全自动批量注册虾皮买家号&#xff0c;注册时可以自动调用手机号、自动接收短信验证、自动绑地址及支付卡&#xff0c;注册成功后还能自动绑定邮箱进行验证。 软件支持5个国家使用&#xff0c;越南、泰国、菲律宾、印度尼西亚、马来西亚。 内置防指纹技术 …

AD7792/AD7793 备忘

AD7792/AD7793 是一款 ∑-Δ ADC&#xff0c;3 通道、低噪声&#xff0c;内部集成仪表放大器和参考源。AD7792 为 16 位&#xff0c;AD7793为 24 位。 供电电压&#xff1a;2.7 ~ 5.25 V&#xff0c;并不支持负电压。转换速率&#xff1a;4.17 Hz ~ 470 Hz内置参考基准&#x…

[移动通讯]【Carrier Aggregation-12】【LTE Carrier Aggregation basics 】

前言&#xff1a; 参考&#xff1a; RF Wireless World&#xff1a; 《LTE Carrier Aggregation basics》 目录&#xff1a; Why Carrier Aggregation Carrier Aggregation in TDD LTE Carrier Aggregation in FDD LTE Carrier Aggregation frequency ba…

Win10 + VS017 编译SQLite3.12.2源码

参考&#xff1a; [1] WIN10 VS2019下编译GDAL3.0PROJ6SQLite_gdal 3 win10编译-CSDN博客 [2] 如何编译SQLite-How To Compile SQLite-CSDN博客 如何生成静态库&#xff1a; 参考&#xff1a; WIN10 VS2019下编译GDAL3.0PROJ6SQLite_gdal 3 win10编译-CSDN博客 如何生成exe:…

JavaEE-部署项目到服务器

本部分内容为&#xff1a;安装依赖&#xff1a;JDK&#xff0c;Tomcat&#xff0c;Mysql&#xff1b;部署项目到服务器 什么是Tomcat Tomcat简单的说就是一个运行JAVA的网络服务器&#xff0c;底层是Socket的一个程序&#xff0c;它也是JSP和Serlvet的一个容器。 为什么我们需要…

vue二维码生成插件qrcodejs2-fix、html生成图片插件html2canvas、自定义打印内容插件print-js的使用及问题总结

一、二维码生成插件qrcodejs2-fix 1.安装命令 npm i qrcodejs2-fix --save2.页面使用 import { nextTick } from vue; import QRCode from qrcodejs2-fix; nextTick(() > {let codeView document.querySelector("#codeView");codeView.innerHTML ""…

【3D图像分割】基于Pytorch的VNet 3D 图像分割5(改写数据流篇)

在这篇文章&#xff1a;【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割2&#xff08;基础数据流篇&#xff09; 的最后&#xff0c;我们提到了&#xff1a; 在采用vent模型进行3d数据的分割训练任务中&#xff0c;输入大小是16*96*96&#xff0c;这个的裁剪是放到Dataset类…

Crypto(8) BUUCTF-bbbbbbrsa1

题目描述&#xff1a; from base64 import b64encode as b32encode from gmpy2 import invert,gcd,iroot from Crypto.Util.number import * from binascii import a2b_hex,b2a_hex import randomflag "******************************"nbit 128p getPrime(nbit)…

学习视频剪辑:巧妙运用中画、底画,制作画中画,提升视频效果

随着数字媒体的普及&#xff0c;视频剪辑已经成为一项重要的技能。在视频剪辑过程中&#xff0c;制作画中画可以显著提升视频效果、信息传达和吸引力。本文讲解云炫AI智剪如何巧妙运用中画、底画批量制作画中画来提升视频剪辑水平&#xff0c;提高剪辑效率。 操作1、先执行云…
最新文章