黑马的redis实战篇-短信登录

这篇具有很好参考价值的文章主要介绍了黑马的redis实战篇-短信登录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

四、实战篇-短信登录

4.1 导入黑马点评项目

1、后端:

2、前端

4.2 基于Session实现登录

1、发送验证码

2、短信验证码登录注册

3、校验登录状态

4.3 集群的session共享问题

4.4 基于Redis实现共享session登录

1、发送验证码

2、短信验证码登录注册

3、校验登录状态


四、实战篇-短信登录

4.1 导入黑马点评项目

项目的架构:

黑马的redis实战篇-短信登录

 

1、后端:

  • 导入项目后后端,创建数据库,加载sql

  • 修改配置文件

    黑马的redis实战篇-短信登录

     

  • 启动

    启动后,访问http://localhost:8081/shop-type/list

    黑马的redis实战篇-短信登录

     

2、前端

在nginx所在目录下打开一个CMD窗口,输入命令:

start nginx.exe

黑马的redis实战篇-短信登录

 

4.2 基于Session实现登录

1、发送验证码

黑马的redis实战篇-短信登录

 

@Override
    public Result sendCode(String phone, HttpSession session) {
        //1.验证手机号是否合法
        if(RegexUtils.isPhoneInvalid(phone)) {
​
            //2.如果不符合,返回错误信息
            return Result.fail("手机号不合法");
        }
​
        //3.符合,生成验证码
        String code = RandomUtil.randomNumbers(6);
​
        //4.将验证码保存到session中
        session.setAttribute(CODE+phone,code);
​
        //5.发送验证码  真实是调用阿里云等平台短信服务的API
        log.info("发送验证码成功,验证码为: {}",code);
​
        //6.结束 返回ok
        return Result.ok();
    }
​

2、短信验证码登录注册

黑马的redis实战篇-短信登录

 

@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1.校验手机号是否格式是否正确
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式不正确");
        }
​
        //2.从session中取出验证码,校验验证码是否正确
        Object attribute = session.getAttribute(CODE+phone);
​
        if (attribute == null || !attribute.toString().equals(loginForm.getCode())) {
            //3.验证码不正确,直接返回
            return Result.fail("验证码不正确");
        }
​
        //4.通过,通过手机号查询用户是否存在
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getPhone,phone);
        User user = baseMapper.selectOne(lambdaQueryWrapper);
​
        //5.不存在进行注册
        if (user == null) {
            user = registerUser(phone);
        }
​
        //6.存在 保证用户到session中
          //注意这里需要进行脱敏
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        session.setAttribute(USER_INFO,userDTO);
        return Result.ok();
    }

3、校验登录状态

黑马的redis实战篇-短信登录

 

上面的逻辑,不光光是一个Controller要进行校验,其他Controller也需要校验,因此我们将其业务写入到拦截器中,如果有用户就放行,没有就不放行,然后在各自需要的Controller中获取用户,返回用户信息即可。如图所示

黑马的redis实战篇-短信登录

 

  • 创建拦截器类实现HandlerInterceptor,

    重写 preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

    和afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

    package com.hmdp.utils;
    ​
    import com.hmdp.dto.UserDTO;
    import org.springframework.web.servlet.HandlerInterceptor;
    ​
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    ​
    import static com.hmdp.utils.SystemConstants.USER_INFO;
    ​
    /**
     * @packageName: com.hmdp.utils
     * @author: winter
     * @date: 2023/4/7 17:15
     * @version: 1.0
     * @email 1660420659@qq.com
     * @description: 拦截器
     */
    public class LoginInterceptor implements HandlerInterceptor {
        /**
         * 请求进入preHandle之前执行
         * @param request
         * @param response
         * @param handler
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
           //1.通过session,获取用户
            Object attribute = request.getSession().getAttribute(USER_INFO);
    ​
            if (attribute == null) {
                //2.用户不存在,拦截器拦截
                response.setStatus(401);
                return false;
            }
    ​
           //3.将用户保证在ThreadLocal中  拦截器放行
            UserHolder.saveUser((UserDTO) attribute);
            return true;
        }
    ​
    ​
        /**
         * 响应结束后,执行这个方法
         * @param request
         * @param response
         * @param handler
         * @param ex
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            //移除 防止内存泄漏
            UserHolder.removeUser();
        }
    }
  • 配置Mvc配置类,拦截器的白名单

    package com.hmdp.config;
    ​
    import com.hmdp.utils.LoginInterceptor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    ​
    /**
     * @packageName: com.hmdp.config
     * @author: winter
     * @date: 2023/4/7 17:39
     * @version: 1.0
     * @email 1660420659@qq.com
     * @description: mvc配置类
     */
    @Configuration
    public class MvcConfig implements WebMvcConfigurer {
    ​
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
           registry.addInterceptor(new LoginInterceptor())
                   .addPathPatterns("/**")
                   .excludePathPatterns(
                           "/user/login",
                           "/user/code",
                            "/shop/**",
                           "/shop-type/**",
                           "/voucher/**",
                           "/upload/**",
                           "/blog/hot"
                   );
        }
    }
  • 将UserController中的me方法进行获取用户,返回信息

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

4.3 集群的session共享问题

session共享问题:当并发访问量多的时候,我们的解决办法可以说添加tomcat集群,但是就会出现问题

多态tomcat并不共享session存储空间,当请求切换到不同tomcat服务时导致数据丢失问题

黑马的redis实战篇-短信登录

 

如图所示,因为使用nginx轮训方式,可能一个用户两次访问的tomcat并不是同一个,并且session不共享,会出现登录进去后,往往提示还需登录,用户的体验感极差,因此出现了解决方案,将数据都存储到redis集群中

4.4 基于Redis实现共享session登录

1、发送验证码

黑马的redis实战篇-短信登录

 

public static final String LOGIN_CODE_KEY = "login:code:";
public static final Long LOGIN_CODE_TTL = 2L;
 @Resource
    private StringRedisTemplate stringRedisTemplate;
​
    @Override
    public Result sendCode(String phone, HttpSession session) {
        //1.验证手机号是否合法
        if(RegexUtils.isPhoneInvalid(phone)) {
​
            //2.如果不符合,返回错误信息
            return Result.fail("手机号不合法");
        }
​
        //3.符合,生成验证码
        String code = RandomUtil.randomNumbers(6);
​
        //4.将验证码保存到session中,并设置过期时间为2分钟
       stringRedisTemplate.opsForValue().
               set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
​
        //5.发送验证码
        log.info("发送验证码成功,验证码为: {}",code);
​
        //6.结束 返回ok
        return Result.ok();
    }

2、短信验证码登录注册

黑马的redis实战篇-短信登录

 常量中的值

public static final String LOGIN_USER_KEY = "login:token:";
​
public static final Long LOGIN_USER_TTL = 36000L;
@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1.校验手机号是否格式是否正确(其实这里应该也将手机号加入到session中,然手机号和验证码一一对应)
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式不正确");
        }
​
        //2.从redsi中取出验证码,校验验证码是否正确
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
​
        if (cacheCode == null || !cacheCode.equals(loginForm.getCode())) {
            //3.验证码不正确,直接返回
            return Result.fail("验证码不正确");
        }
​
        //4.通过,通过手机号查询用户是否存在
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getPhone,phone);
        User user = baseMapper.selectOne(lambdaQueryWrapper);
​
        //5.不存在进行注册
        if (user == null) {
            user = registerUser(phone);
        }
​
        //6.存在 保证用户到redis中
          //6.1 注意这里需要进行脱敏
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
​
          //6.2 转换生成Map 
        //这里因为使用的StringRedisTemplate 因此都是String
        //这里如果不装换成string  map中存储userDto对象的id是long类型
        //存储在redis的hash时 会出现String转换long 数字转换异常
        Map<String, Object> map = BeanUtil.beanToMap(userDTO,new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName,fieldValue) ->
                                fieldValue.toString()));
​
          //6.3.生成一个随机的token
        String token = UUID.fastUUID().toString(true);
​
          //6.4 存到到redis中
        String key = LOGIN_USER_KEY+token;
         stringRedisTemplate.opsForHash().putAll(key,map);
         //设置有效时间
         stringRedisTemplate.expire(key,LOGIN_USER_TTL,TimeUnit.MINUTES);
        return Result.ok(token);
    }
​
    /**
     * 注册
     * @param phone
     * @return
     */
    private User registerUser(String phone) {
        User user = new User();
        user.setPhone(phone);
        String nickName = RandomUtil.randomString(8);
        user.setNickName(USER_NICK_NAME_PREFIX+nickName);
        //保存用户到数据库
        baseMapper.insert(user);
        return user;
    }

3、校验登录状态

黑马的redis实战篇-短信登录

 

这里需要进行优化一下,需要使用两个拦截器,因为session的时候使用一个拦截器,只有登录等被拦截的请求,才会增加redis键值的时间,例如访问主页不经过拦截器,可能半小时后键值就自动销毁的,这样是不符合业务要求的,因此要在增加一个拦截器

黑马的redis实战篇-短信登录

 

  • 拦截器1 拦截一切路径

package com.hmdp.utils;
​
import cn.hutool.core.bean.BeanUtil;
import com.hmdp.dto.UserDTO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
​
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;
​
import static com.hmdp.utils.RedisConstants.LOGIN_USER_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;
​
/**
 * @packageName: com.hmdp.utils
 * @author: winter
 * @date: 2023/4/7 17:15
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 拦截器所有请求
 */
public class ExtraTimeInterceptor implements HandlerInterceptor {
​
​
​
​
    private StringRedisTemplate stringRedisTemplate;
    public ExtraTimeInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
​
    /**
     * 请求进入preHandle之前执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.通过请求获取到token信息
        String token = request.getHeader("authorization");
        //2.通过token从redis中获取对象信息
        Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);
        if (map.isEmpty()) {
            return  true;
        }
        //将map转换为对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(map, new UserDTO()
                , false);
​
        //3.将用户保证在ThreadLocal中  拦截器放行
        UserHolder.saveUser( userDTO);
​
        //4.刷新用户存储有效期
        stringRedisTemplate.expire(LOGIN_USER_KEY + token,LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }
​
​
    /**
     * 响应结束后,执行这个方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除 防止内存泄漏
        UserHolder.removeUser();
    }
​
}
​
  • 拦截器2 拦截部分需要登录才能访问的请求

package com.hmdp.utils;
​
import com.hmdp.dto.UserDTO;
import org.springframework.web.servlet.HandlerInterceptor;
​
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
​
/**
 * @packageName: com.hmdp.utils
 * @author: winter
 * @date: 2023/4/7 17:15
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: 拦截部门请求,拦截需要登录的路径
 */
public class LoginInterceptor implements HandlerInterceptor {
​
​
​
    /**
     * 请求进入preHandle之前执行
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.取到用户
        UserDTO user = UserHolder.getUser();
​
        //2.判断用户是否为空
        if (user == null) {
            response.setStatus(401);
            return false;
        }
        return true;
    }
​
​
​
}
 
  • mvc配置一下拦截器需要拦截什么请求

package com.hmdp.config;
​
import com.hmdp.utils.ExtraTimeInterceptor;
import com.hmdp.utils.LoginInterceptor;
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;
​
/**
 * @packageName: com.hmdp.config
 * @author: winter
 * @date: 2023/4/7 17:39
 * @version: 1.0
 * @email 1660420659@qq.com
 * @description: mvc配置类
 */
@Configuration
public class MvcConfig implements WebMvcConfigurer {
​
    @Resource
    private StringRedisTemplate stringRedisTemplate;
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //默认先添加执行  ,也可以设置order  值越小越先执行
       registry.addInterceptor(new ExtraTimeInterceptor(stringRedisTemplate))
               .addPathPatterns("/**").order(0);
       registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")
                .excludePathPatterns(
                "/user/login",
                "/user/code",
                "/shop/**",
                "/shop-type/**",
                "/voucher/**",
                "/upload/**",
                "/blog/hot"
        ).order(1);
    }
}
具体代码gitee上:

redis实战篇-hmdp-短信登录: 存放黑马点评中redis进行短信登录的代码 ,包括前端后后端文章来源地址https://www.toymoban.com/news/detail-414446.html

到了这里,关于黑马的redis实战篇-短信登录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【SpringBoot篇】详解基于Redis实现短信登录的操作

    使用 Redis 进行登录适用于以下情况: 分布式系统: 当系统需要支持多个节点的分布式部署时,使用 Redis 存储登录信息能够更好地支持多节点间的共享和同步,确保用户的登录状态能够在整个系统中得到有效的传递和管理。 高并发访问: 面对大规模的并发访问,使用 Redis

    2024年02月04日
    浏览(41)
  • 【Redis】2、Redis应用之【根据 Session 和 Redis 进行登录校验和发送短信验证码】

    🌼 文章基于 B 站黑马程序员视频教程编写 🌼 做笔记便于日后复习 ① 手机号格式后端校验 手机号校验的正则表达式 校验工具类: ② 生成短信验证码 🌼 hutool 工具的详细使用: https://doc.hutool.cn/pages/index/ 🌿 根据 Cookie 中的 JSESSIONID 获取到 Session 🌿 然后从 Session 中获取到

    2024年02月11日
    浏览(57)
  • 黑马Redis视频教程实战篇(一)

    目录 一、短信登录 1.1、导入黑马点评项目 (1)导入黑马点评sql脚本 (2)导入后端项目 (3)导入前端项目  1.2、基于Session实现登录流程 1.3 、实现发送短信验证码功能 1.4 、实现登录拦截功能  1.5 、隐藏用户敏感信息 (1)在登录方法处修改  (2)在拦截器处  (3)在

    2024年02月07日
    浏览(73)
  • 黑马Redis视频教程实战篇(五)

    目录 一、达人探店 1.1、发布探店笔记 1.2、查看探店笔记 1.3、点赞功能 1.4、点赞排行榜 二、好友关注 2.1、关注和取消关注 2.2、共同关注 2.3、Feed流实现方案 2.4、推送到粉丝收件箱 2.4、实现分页查询收邮箱 发布探店笔记 探店笔记类似点评网站的评价,往往是图文结合。对

    2024年02月07日
    浏览(38)
  • 黑马Redis视频教程实战篇(二)

    目录 一、什么是缓存? 1.1 为什么要使用缓存? 1.2 如何使用缓存? 二、添加商户缓存 2.1 缓存模型和思路 2.2 代码实现 三、缓存更新策略 2.1 数据库缓存不一致解决方案 2.2 数据库和缓存不一致采用什么方案 四、实现商铺和缓存与数据库双写一致 五、缓存穿透问题的解决思

    2024年02月07日
    浏览(46)
  • 黑马Redis学习笔记 (基础篇+实战篇)

    1.1.1结构化和非结构化 (1) SQL关系性数据库 ​ 传统关系型数据库是结构化数据,每一张表都有严格的约束信息:字段名、字段数据类型、字段约束等等信息,插入的数据必须遵守这些约束 (2) NoSql数据库 NoSql对数据库格式没有严格约束,往往形式松散,自由。 可以是key-value,可

    2024年02月09日
    浏览(47)
  • 黑马点评Redis实战(优惠卷秒杀)

    本文是上一篇文章的后续,上一篇文章链接 马点评Redis实战(短信登录;商户查询缓存) id是一个订单必备的属性,而订单的id属性是必须唯一的,首先我们会想到使用数据库主键id,并设置为自增。这样似乎就能满足唯一性。 但是,这样会存在一些问题: id的规律太过明显,因

    2024年02月04日
    浏览(32)
  • django-短信登录

       2.2.1视图函数 sms_login函数是短信登录的逻辑。 sms_send函数是获取点击短信验证码,校验我表单中手机号是否填的正确,后调用第三方的sdk(腾讯云或者阿里云)来发送短信,获取了短信在Redis中缓存等待用户输入校验。  2.2.2 froms组件

    2024年04月15日
    浏览(27)
  • 【若依RuoYi短信验证码登录】汇总

    遇到一个场景,需要同时支持手机号或者邮箱和密码或者验证码进行登录的场景,故来记录一下。 说明:此流程主要是基于若依框架集成的多种方式登录,主要演示登录业务逻辑和前端登录密码和验证码切换组件和配置Security 一:后端登录业务逻辑代码: 因为有多个端,多个语

    2024年02月11日
    浏览(40)
  • 若依RuoYi整合短信验证码登录

    背景:若依默认使用账号密码进行登录,但是咱们客户需要增加一个短信登录功能,即在不更改原有账号密码登录的基础上,整合 短信验证码登录 。 本案例基于RuoYi-Vue版本实现,其他版本应该大同小异。 一、自定义短信登录 token 验证 仿照 UsernamePasswordAuthenticationToken 类,编

    2023年04月16日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包