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

黑马点评1

短信登录

基于Session实现登录流程

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

  • 发送验证码
    @Overridepublic Result sendCode(String phone, HttpSession session) {//校验 手机号if (RegexUtils.isPhoneInvalid(phone)) {//不符合return Result.fail("手机号格式不正确");}//生成验证码String code = RandomUtil.randomNumbers(6);//保存到sessionsession.setAttribute("code", code);//发送验证码log.debug("手机号:{},验证码:{}", phone, code);return Result.ok();}
  • 用户登录
    @Overridepublic 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);}//保存用户信息到sessionsession.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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取SessionHttpSession 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 {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login");}
}

用户信息脱敏

登录的时候修改:

        //保存用户信息到sessionUserDTO 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
    @Overridepublic Result sendCode(String phone, HttpSession session) {//校验 手机号if (RegexUtils.isPhoneInvalid(phone)) {//不符合return Result.fail("手机号格式不正确");}//生成验证码String code = RandomUtil.randomNumbers(6);//保存到sessionstringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);//发送验证码log.debug("手机号:{},验证码:{}", phone, code);return Result.ok();}

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

    @Overridepublic 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中//生成tokenString 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;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求头中的tokenString 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数据转为UserDTOUserDTO 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;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//清空ThreadLocal中的数据  防止内存泄漏UserHolder.removeUser();}
}

配置类里面注入StringRedisTemplate:

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic 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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (UserHolder.getUser() == null) {//不存在,拦截,返回401状态码response.setStatus(401);return false;}//有用户return true;}
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KaiwuDB 内核解析 - SQL 查询的生命周期

一、概述 KaiwuDB 内核解析系列共分上下两部分&#xff0c;本文是该系列的第一部分&#xff0c;主要涵盖了网络协议到 SQL 执行器&#xff0c;解释 KaiwuDB 如何执行 SQL 查询&#xff0c;包括系统各个组件的执行路径&#xff08;网络协议、SQL 会话管理、解析器、执行计划及优…

服务器数据恢复—Zfs文件系统下文件被误删除的如何恢复数据?

服务器故障&#xff1a; 一台zfs文件系统服务器&#xff0c;管理员误操作删除服务器上的数据。 服务器数据恢复过程&#xff1a; 1、将故障服务器所有磁盘编号后取出&#xff0c;硬件工程师检测所有硬盘后没有发现有磁盘存在硬件故障。以只读方式将全部磁盘做扇区级别的镜像备…

单链表基本操作的实现,初始化,头插,尾插,判空,获取个数,查找,删除,获取前置和后置位,清空,销毁

目录 一.单链表的设计 二.单链表的实现 三.单链表的总结 一.单链表的设计 1.单链表的结构定义: typedef struct Node{int data;//数据域struct Node* next;//后继指针}Node,*List; 2.单链表的设计示意图: 3.注意,单链表的最后一个节点的next域为NULL; 4.为什么要有一个头…

0006Java安卓程序设计-ssm基于Android的校园二手商品交易平台

文章目录 **摘** **要****目** **录**系统设计开发环境 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 摘 要 随着毕业季的来临以及当代大学生的消费力购买力的不断增强&#xff0c;我们的寝室中囤积了很多二手商品&#xff0c;有很多是…

智能电表和互感器一起安装有什么效果?

智能电表和互感器的普及&#xff0c;为用电管理提供了更为精确和便捷的方式。那么&#xff0c;当智能电表和互感器一起安装时&#xff0c;会产生怎样的"化学反应"呢&#xff1f;下面&#xff0c;小编就来为大家详细的讲解下智能电表和互感器一起安装的作用吧&#xf…

服务上千家企业,矩阵通2.0重磅上线,全链路管理新媒体矩阵

自上线以来 矩阵通已服务了上千家企业级客户 覆盖汽车、家居、媒体、金融、教育等多个行业 矩阵通1.0时代 我们以“数据”为基座打造出10功能 帮助企业轻松管理新媒体矩阵 实现账号管理、数据分析、竞对监测、 人员考核、风险监管等需求 而现在 矩阵通2.0重磅上线 新增…

Keras人工智能神经网络 Regressor 回归 神经网络搭建

前期分享了使用tensorflow来进行神经网络的回归&#xff0c;tensorflow构建神经网络 本期我们来使用Keras来搭建一个简单的神经网络 Keras神经网络可以用来模拟回归问题 (regression)&#xff0c;例如给下面一组数据&#xff0c;用一条线来对数据进行拟合&#xff0c;并可以预…

ActiveMQ是什么?-九五小庞

MQ是消息中间件&#xff0c;是一种在分布式系统中应用程序借以传递消息的媒介&#xff0c;常用的有ActiveMQ&#xff0c;RabbitMQ&#xff0c;kafka。ActiveMQ是Apache下的开源项目&#xff0c;完全支持JMS1.1和J2EE1.4规范的JMS Provider实现。特点&#xff1a;1、支持多种语言…

机器学习快速入门教程 Scikit-Learn实现

机器学习是什么? 机器学习是一帮计算机科学家想让计算机像人一样思考所研发出来的计算机理论。他们曾经说过,人和计算机其实本没有差别,同样都是一大批互相连接的信息传递和存储元素所组成的系统。所以有了这样的想法,加上他们得天独厚的数学功底,机器学习的前身也就孕育而生…

Linux学习笔记之二(环境变量)

Linux learning note 1、环境变量1.1、修好PATH环境变量 1、环境变量 环境变量(environment variables)即系统运行的一些环境参数。主要的环境变量有以下这些&#xff1a; PATH&#xff1a;决定了系统查找可执行文件的目录范围。HOME&#xff1a;指定当前用户的主目录路径。U…

python 数据挖掘库orange3 介绍

orange3 是一个非常适合初学者的data mining library. 它让使用者通过拖拽内置的组件来形成工作流。让你不需要写任何代码就可以体验到数据挖掘和可视化的魅力。 它的桌面如下&#xff0c;这里我创建了 3 个节点&#xff0c;分别是数据集、小提琴图&#xff0c;散点图 其中 …

C++笔记之vector的成员函数swap()和data()

C笔记之vector的成员函数swap()和data() 标准C中的std::vector类确实有swap()和data()这两个成员函数。下面是它们的简要描述&#xff1a; swap(): std::vector的swap()成员函数用于交换两个向量的内容&#xff0c;实现了高效的交换操作&#xff0c;不需要复制向量的元素。这…