Redis——某马点评day01——短信登录

2023-12-03 05:15

本文主要是介绍Redis——某马点评day01——短信登录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

项目介绍

导入黑马点评项目

项目架构

 

 

基于Session实现登录

基本流程

实现发送短信验证码功能

 controller层中

    /*** 发送手机验证码*/@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {//  发送短信验证码并保存验证码return userService.sendCode(phone,session);}

Service层中

真的发送的话要接入阿里云或腾讯云的短信发送功能,这里假装发送成功。

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic Result sendCode(String phone, HttpSession session) {//1.校验手机号if(RegexUtils.isPhoneInvalid(phone)){//2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}//3.符合,生成验证码String code = RandomUtil.randomNumbers(6);//4.保存验证码session.setAttribute("code",code);//5.发送验证码log.debug("发送短信验证码成功,验证码:{}",code);return Result.ok();}
}

再次点击发送就可以看见如下:

 

实现验证码登录和注册功能

Controller层中

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

Service层中

    @Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1.验证手机号String phone=loginForm.getPhone();if(RegexUtils.isPhoneInvalid(phone)){//1.1不符合,返回报错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 * from tb_user where phone=?User user = query().eq("phone", phone).one();//5.判断用户是否存在if(user==null){//6.不存在,创建新用户并保存user=   createUserWithPhone(phone);}//7.存在,保存用户信息到session中session.setAttribute("user",user);return Result.ok();}private User createUserWithPhone(String phone) {//1.创建用户User user=new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));//2.保存save(user);return user;}

这里的session之所以能够获取到发送验证码请求时存入的验证码是因为这是在同一个会话当中,session会被返回给前端,下一次请求也会携带同一个session过来。所以才有之前保存的信息在里面。

实现登录校验拦截器

 

 接口多了之后就要拦截器进行统一校验,然后为了传递用户信息给后序的业务,需要将用户信息存入Threadlocal里面的.Threadlocal是每个请求线程的一个独立保存空间。

在拦截器的最后一个方法中,清空thread local的信息,第一可以做到退出登录,第二可以防止内存泄露

新建一个拦截器

public class LoginInterceptor implements HandlerInterceptor {@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.不存在,拦截response.setStatus(401);return false;}//5.存在,保存用户信息到ThreadLocalUserHolder.saveUser((UserDTO) user);//6.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//移除用户UserHolder.removeUser();}
}

 新建配置项

将上面的拦截器投入使用,并且指定不拦截的请求。

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/shop-type/**","/upload/**","/blog/hot","/user/code" ,"/user/login");}
}

登录校验接口

    @GetMapping("/me")public Result me(){// 获取当前登录的用户并返回UserDTO user= UserHolder.getUser();return Result.ok(user);}

隐藏用户敏感信息

在前面代码里返回给前端的是用户完整信息,这是不合理的,所以改成如下。

在session存的时候就只存部分信息。然后上面登录校验逻辑因为黑马资料的问题已经是隐藏之后的了的,所以只改这里一个地方即可。

    @Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {........//7.存在,保存用户信息到session中session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));return Result.ok();}

集群的session共享问题

使用Redis解决Session共享问题。 

基于Redis实现共享session登录

业务流程

基于redis实现短信登录

发送短信验证码部分的代码修改:

生成验证码之后将验证码存入Redis

    @Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result sendCode(String phone, HttpSession session) {//1.校验手机号...//3.符合,生成验证码...//4.保存验证码到RedisstringRedisTemplate.opsForValue().set("login:code:"+phone,code,2, TimeUnit.MINUTES);//5.发送验证码...}

短信验证码登录,注册功能部分的代码修改:

    @Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1.验证手机号...//  2.从redis获取 校验验证码String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);...//4.一致,根据手机号查询用户 select * from tb_user where phone=?...//5.判断用户是否存在...//7.存在,保存用户信息到Redis中//7.1.随机生成token,作为登录令牌String token = UUID.randomUUID().toString(true);//7.2将User对象转为Hash存储UserDTO userDTO=BeanUtil.copyProperties(user, UserDTO.class);Map<String, Object> userMap = BeanUtil.beanToMap(userDTO);//7.3存储String tokenKey="login::token:"+token;stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);//7.4设置token有效期stringRedisTemplate.expire(tokenKey,30,TimeUnit.MINUTES);//8.返回tokenreturn Result.ok(token);}

登录拦截器代码修改


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.获取请求头中的tokenString token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {//4.不存在,拦截response.setStatus(401);return false;}//2.基于token获取redis中的用户String tokenKey=RedisConstants.LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);//3.判断用户是否存在if(userMap.isEmpty()){//4.不存在,拦截response.setStatus(401);return false;}//5.将查询到的Hash数据转为UserDTO对象UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//6.存在,保存用户信息到ThreadLocalUserHolder.saveUser( userDTO);//7.刷新token有效期stringRedisTemplate.expire(tokenKey,RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);//8.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//移除用户...}
}

因为这里不能用注解注入stringRedisTemplate,要在MVC配置器哪里进行注入

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns("/shop/**","/shop-type/**","/upload/**","/blog/hot","/user/code" ,"/user/login");}
}

出现Long转String的报错 

在登录逻辑里面将对象转为Map时做多点工作

        //7.2将User对象转为Hash存储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()));

 可以看见对应东西

 

解决登录状态刷新问题

登录状态的刷新只会在访问需要拦截的请求时才刷新,如果是不需要拦截的请求就不会刷新.

为了拦截所有请求,这里新加一个拦截器

新增刷新拦截器:

public class RefreshTokenInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate=stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取请求头中的tokenString token = request.getHeader("authorization");if (StrUtil.isBlank(token)) {return true;}//2.基于token获取redis中的用户String tokenKey=RedisConstants.LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);//3.判断用户是否存在if(userMap.isEmpty()){return true;}//5.将查询到的Hash数据转为UserDTO对象UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//6.存在,保存用户信息到ThreadLocalUserHolder.saveUser( userDTO);//7.刷新token有效期stringRedisTemplate.expire(tokenKey,RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);//8.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//移除用户UserHolder.removeUser();}
}

 修改原本的登录拦截器

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.判断是否需要拦截(ThreadLocal中是否有用户)if(UserHolder.getUser()==null){//没有,需要拦截response.setStatus(401);return false;}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//移除用户UserHolder.removeUser();}
}

MVC配置类当中

需要添加order保证刷新拦截器先执行。

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//登录拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/shop/**","/shop-type/**","/upload/**","/blog/hot","/user/code" ,"/user/login").order(1);//刷新拦截器registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);}
}

这篇关于Redis——某马点评day01——短信登录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/448192

相关文章

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

Redis的持久化之RDB和AOF机制详解

《Redis的持久化之RDB和AOF机制详解》:本文主要介绍Redis的持久化之RDB和AOF机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述RDB(Redis Database)核心原理触发方式手动触发自动触发AOF(Append-Only File)核

Redis分片集群、数据读写规则问题小结

《Redis分片集群、数据读写规则问题小结》本文介绍了Redis分片集群的原理,通过数据分片和哈希槽机制解决单机内存限制与写瓶颈问题,实现分布式存储和高并发处理,但存在通信开销大、维护复杂及对事务支持... 目录一、分片集群解android决的问题二、分片集群图解 分片集群特征如何解决的上述问题?(与哨兵模

SpringBoot连接Redis集群教程

《SpringBoot连接Redis集群教程》:本文主要介绍SpringBoot连接Redis集群教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 依赖2. 修改配置文件3. 创建RedisClusterConfig4. 测试总结1. 依赖 <de

SpringBoot+Redis防止接口重复提交问题

《SpringBoot+Redis防止接口重复提交问题》:本文主要介绍SpringBoot+Redis防止接口重复提交问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录前言实现思路代码示例测试总结前言在项目的使用使用过程中,经常会出现某些操作在短时间内频繁提交。例

Redis 配置文件使用建议redis.conf 从入门到实战

《Redis配置文件使用建议redis.conf从入门到实战》Redis配置方式包括配置文件、命令行参数、运行时CONFIG命令,支持动态修改参数及持久化,常用项涉及端口、绑定、内存策略等,版本8... 目录一、Redis.conf 是什么?二、命令行方式传参(适用于测试)三、运行时动态修改配置(不重启服务

CSS3打造的现代交互式登录界面详细实现过程

《CSS3打造的现代交互式登录界面详细实现过程》本文介绍CSS3和jQuery在登录界面设计中的应用,涵盖动画、选择器、自定义字体及盒模型技术,提升界面美观与交互性,同时优化性能和可访问性,感兴趣的朋... 目录1. css3用户登录界面设计概述1.1 用户界面设计的重要性1.2 CSS3的新特性与优势1.

浅析如何保证MySQL与Redis数据一致性

《浅析如何保证MySQL与Redis数据一致性》在互联网应用中,MySQL作为持久化存储引擎,Redis作为高性能缓存层,两者的组合能有效提升系统性能,下面我们来看看如何保证两者的数据一致性吧... 目录一、数据不一致性的根源1.1 典型不一致场景1.2 关键矛盾点二、一致性保障策略2.1 基础策略:更新数

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

Springboot整合Redis主从实践

《Springboot整合Redis主从实践》:本文主要介绍Springboot整合Redis主从的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言原配置现配置测试LettuceConnectionFactory.setShareNativeConnect