Redis实战(黑马点评:短信登录)

news2024/11/27 8:41:49

1.基于Session实现发送短信验证码

UserController:


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

UserServiceImpl:

 @Override
    public Result sedCode(String phone, HttpSession session) {
        //1. 校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误");
        }

        //3. 符合,生成验证码
        String code = RandomUtil.randomNumbers(6);
        //4. 保存验证码到session
        //session.setAttribute("code",code);
        //4.以手机号为key保存验证码到redis中,key=login:code:13456789001
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
        //5. 发送验证码
        log.debug("发送短信验证码成功,验证码:{}",code);
        //返回ok
        return Result.ok();
    }

2.短信验证登录功能


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

UserServiceImpl 


    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1. 校验手机号
        String phone = loginForm.getPhone();
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式错误");
        }
        //2. 校验验证码 从redis中取出验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        if (cacheCode == null || !cacheCode.equals(code)){
            //3. 不一致,报错
            return Result.fail("验证码错误");
        }

        //4.一致,根据手机号查询用户
        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)
                .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.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);
        return Result.ok(token);
    }

    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;
    }
    

3.定义拦截器

Session

public class LoginInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获取session
        HttpSession session = request.getSession();

        //2.获取session中的用户
        Object user = session.getAttribute("user");

        //3.判断用户是否存在
        if(user == null){
            //4.不存在,拦截 返回401状态码
            response.setStatus(401);
            return false;
        }
        //5.存在,保存用户信息到ThreadLocal
        UserHolder.saveUser((User) user);
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户
        UserHolder.removeUser();
    }
}

让拦截器生效

public class MvcConfig implements WebMvcConfigurer {
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new LoginInterceptor())
                //排除不需要拦截的请求
                .excludePathPatterns(
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                );
    }
}

UserController 

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

3.基于Redis实现共享session登录

UserServiceImpl

   @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 //set key value ex 120 
        //2, TimeUnit.MINUTES :设置有效期
//        session.setAttribute("code",code); 
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_USER_TTL, TimeUnit.MINUTES);
        //5.发送验证码
        log.debug("发送短信验证码成功,验证码:{}",code);

        //返回ok
        return Result.ok();
    }
  @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1.校验手机号
        String phone = loginForm.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            //2.如果不符合,返回错误信息
            return Result.fail("手机号格式错误!");
        }
        //2.从session中获取验证码
//        Object cacheCode = session.getAttribute("code");
        //从redis中获取验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);
        String code = loginForm.getCode();

        if(cacheCode == null || !cacheCode.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.保存用户信息到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_USER_KEY + token;
        stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);
        //设置token有效期
        stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);
//        //7.保存用户信息到session中
//        session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
        //返回token
        return Result.ok(token);
    }

修改拦截器

LoginInterceptor
 private StringRedisTemplate stringRedisTemplate
    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        //1.获取session
//        HttpSession session =request.getSession();
//        ///2.获取session中的用户
//        Object user =session.getAttribute("user");
//        //3.判断用户是否存在
//        if(user == null){
//            //4.不存在,拦截,返回401状态码
//            response.setStatus(401);
//            return false;
//        }
//        //5.存在,保存用户信息到ThreadLocal
//        UserHolder.saveUser((UserDTO) user)
//        //6.放行
//        return true;
        //1.获取请求头中的token
        String token = request.getHeader("authorization");
        if(StrUtil.isBlank(token)){
            //不存在,拦截,返回401状态码
            response.setStatus(401);
            return false;
        }
        //2.基于token获取redis中的用户
        String key = LOGIN_USER_KEY + token;
        Map<Object,Object> userMap = stringRedisTemplate.opsForHash()
                        .entries(LOGIN_USER_KEY+token)
        //3.判断用户是否存在
        if(userMap.isEmpty()){
            //4.不存在,拦截,返回401状态码
            response.setStatus(401);
            return false;
        }
        //5.将查询到的Hash数据转为UserDTO对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap,new UserDTO(),false);
        //6.存在,保存用户信息到ThreadLocal
        UserHolder.saveUser(userDTO);
        //7.刷新token有效期
        stringRedisTemplate.expire(key,LOGIN_USER_TTL, TimeUnit.MINUTES);
        //8.放行
        return 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)
                .setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));

4.登录拦截器的优化

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.判断是否需要拦截(ThreadLocal中是否有用户)
        if(UserHolder.getUser() == null){
            //没有,需要拦截,设置状态码
            response.setStatus(401);
            //拦截
            return false;
        }
        //有用户,则放行
        return true;
    }
    
}

 token刷新拦截器

public class RefreshTokenInterceptor implements HandlerInterceptor {


    private StringRedisTemplate stringRedisTemplate;
    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //1.获取请求头中的token
        String token = request.getHeader("authorization");
        if(StrUtil.isBlank(token)){

            return true;
        }
        //2.基于token获取redis中的用户
        String key = LOGIN_USER_KEY + token;
        Map<Object,Object> userMap = stringRedisTemplate.opsForHash()
                        .entries(LOGIN_USER_KEY+token);
        //3.判断用户是否存在
        if(userMap.isEmpty()){
            return true;
        }
        //5.将查询到的Hash数据转为UserDTO对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap,new UserDTO(),false);
        //6.存在,保存用户信息到ThreadLocal
        UserHolder.saveUser(userDTO);
        //7.刷新token有效期
        stringRedisTemplate.expire(key,LOGIN_USER_TTL, TimeUnit.MINUTES);
        //8.放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户
        UserHolder.removeUser();
    }
}

配置拦截器


@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    //order()中值越小,执行的优先级越高
    public void addInterceptors(InterceptorRegistry registry){
        //登录拦截器
        registry.addInterceptor(new LoginInterceptor())
                //排除不需要拦截的请求
                .excludePathPatterns(
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                ).order(1);
        //token刷新拦截器
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
    }
}

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

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

相关文章

[yolox]ubuntu上部署yolox的ncnn模型

首先转换pytorch->onnx->param模型&#xff0c;这个过程可以查资料步骤有点多&#xff0c;参考blog.51cto.com/u_15660370/6408303&#xff0c;这里重点讲解转换后部署。 测试环境&#xff1a; ubuntu18.04 opencv3.4.4(编译过程省略&#xff0c;参考我其他博客) 安装…

一个页面实现两个滚动条【前端】

一个页面实现两个滚动条【前端】 前言版权推荐一个页面实现两个滚动条最后 前言 2024-4-2 12:54:46 以下内容源自《【前端】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是https://jsss-1.blog.csdn.net …

25.死锁

一个线程如果需要同时获取多把锁&#xff0c;就容易产生死锁。 t1线程获得A对象锁&#xff0c;接下来想获取B对象的锁。 t2线程获得B对象锁&#xff0c;接下来想获取A对象的锁。 /*** 死锁demo* param args*/public static void main(String[] args) {Object a new Object(…

数字医疗:智慧医共体引领健康未来

随着科技的不断发展&#xff0c;数字医疗正在成为医疗行业的一场革命。数字化技术的应用不仅提高了医疗服务的效率和质量&#xff0c;还为人们带来了更便捷、智能的健康管理方式。在这个数字化时代&#xff0c;智慧医共体已经成为了健康未来的引领者。 数字医疗&#xff0c;顾…

超图新建三维数据集继续学习

1 新建三维数据集 之前操作过新建三维数据集&#xff0c;还不熟悉&#xff0c;继续熟悉&#xff1b; 现在有一个文件型的数据源&#xff0c;名为swtest1&#xff1b;它前面小图标上有UDX三个字母&#xff0c;表明这是一个UDX类型的数据源&#xff1b;在此数据源上右击&#x…

研发效能·创享大会—IDCF五周年专场

时光流转&#xff0c;IDCF即将迎来五周年的庆典。在这个意义非凡的时刻&#xff0c;我们精心筹备了一场盛大的聚会【研发效能创享大会—IDCF五周年专场】。 IDCF自2019年成立以来&#xff0c;携手百余位技术领头人共同打造DevOps技术学习平台&#xff0c;与30万社群伙伴联动&a…

如何改写出优质文案,AI写作工具有方法

在当今数字化时代&#xff0c;内容创作已成为企业和个人在市场竞争中脱颖而出的关键因素。而写作优质文案是吸引读者注意力、传达信息以及促使行动的重要手段之一。然而&#xff0c;对许多人来说&#xff0c;写作可能是一项具有挑战性的任务。幸运的是&#xff0c;随着人工智能…

提升你的CSS技能:深入理解伪类选择器和伪元素选择器!

在CSS的世界里&#xff0c;有些选择器并不像它们的名字那样直接。今天&#xff0c;我们要探索的是两种特殊的选择器&#xff1a;伪类选择器和伪元素选择器。它们虽然名字相似&#xff0c;但功能和用途却大有不同。 下面就让我们一起来了解一下它们是如何在我们的页面布局中扮演…

EasyDarwin 、ffmpeg 音视频推流拉流;OBS视频推理软件、obs-rtspserver服务器;python读取rtsp流

参考&#xff1a;https://blog.csdn.net/N71FS1/article/details/130019563 一、EasyDarwin ffmpeg ffmpeg 推送音视频流到rtsp流服务器 EasyDarwin 作为rtsp流服务器 &#xff08;下载&#xff1a;https://www.easydarwin.org/p/easydarwin.html&#xff09;OBS 直播音视频录…

什么是交换机虚拟化技术?

热门IT【视频教程】-华为/思科/红帽/oracle https://blog.csdn.net/XMWS_IT/article/details/137153651?spm1001.2014.3001.5501 简介 通过交换机虚拟化技术&#xff0c;既可以在逻辑上集成多台物理连接的交换机&#xff0c;实现拓宽虚拟交换机带宽、提升转发效率的目的&…

城市交通视频视频联网系统实施方案

目录 1.需求调研 2.系统设计 3.技术分析 4.技术开发 5.系统平台环境要求 6.网络要求 7.安全要求 8.项目交付和验收 8.1交付准备 8.2系统安装、培训 8.2.1系统验收 8.2.2项目进度计划 附录&#xff1a;交通监控设备情况调研表 1.需求调研 从SZ市交通运输局、以及下…

YOLOv9改进策略 :主干优化 | 无需TokenMixer也能达成SOTA性能的极简ViT架构 | CVPR2023 RIFormer

💡💡💡本文改进内容: token mixer被验证能够大幅度提升性能,但典型的token mixer为自注意力机制,推理耗时长,计算代价大,而RIFormers是无需TokenMixer也能达成SOTA性能的极简ViT架构 ,在保证性能的同时足够轻量化。 💡💡💡RIFormerBlock引入到YOLOv9,多个数…

【Linux】自定义协议+序列化+反序列化

自定义协议序列化反序列化 1.再谈 "协议"2.Cal TCP服务端2.Cal TCP客户端4.Json 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.再谈 “协议” 协议是一种 “约定”。在前面我们说过父亲和儿子约定打电话的例子&#xff0c;不过这是感性的认识&a…

Java接口与继承实践:Ether通信系统的构建(day16)

创建一个接口Icontroller, 再创建一个接口IReceiver, 创建一个子类实现IReceiver&#xff0c; 创建一个子类实现IContrller&#xff0c; 创建一个类Ether 创建一个Signal类 创建一个类Radiosignal继承Signal 创建一个用户User 最后创建一个Main类 今日总结&#xff1a…

GDC回顾与MAU前瞻丨Flat Ads开启开发者流量变现新篇章

3月18日-22日,全球游戏行业最具规模、最有影响力的盛会——GDC 2024 在美国旧金山 Moscone Convention Center 成功举办,Flat Ads作为参展商亮相GDC大会,向全球游戏开发者展示我们的最新技术与服务。此次Flat Ads团队不仅洞察了行业最前沿的技术和发展趋势,同时也与诸多一线开发…

【Qt】:常用控件(一)

常用控件 一.概述二.QWidget核心属性1.enabled&#xff08;是否可用&#xff09;2.geometry&#xff08;设置坐标&#xff09;3.WindTitle&#xff08;窗口标题&#xff09;4.windowIcon1.绝对路径2.qrc机制 5.windowOpacity&#xff08;透明度&#xff09; 一.概述 Widget是Q…

前端大文件分片上传

1.分片上传整体流程 开始上传&#xff1a;前端启动文件分片上传。后端返回唯一标识。分片上传&#xff1a;获取到上传的文件&#xff0c;然后设置一个固定的分片大小&#xff0c;将文件切成多个小片&#xff0c;计算出每一个分片的MD5值&#xff08;32位&#xff09;。将每个分…

新model开发记录

模型使用 -- 用blender导出为 fbx &#xff0c;修改渲染方式&#xff08;点击模型->Materials->Extract Materials(将材质从fbx中 单独提取出来了)->Materials 选择 Shader -> SimpleURPToonLitExample 点开脸的材质&#xff0c;勾选第一条&#xff09; 解决角色…

【Redis】redis集群模式

概述 Redis集群&#xff0c;即Redis Cluster&#xff0c;是Redis 3.0开始引入的分布式存储方案。实际使用中集群一般由多个节点(Node)组成&#xff0c;Redis的数据分布在这些节点中。集群中的节点分为主节点和从节点&#xff1a;只有主节点负责读写请求和集群信息的维护&#…

基于OSPF的企业内网安全优化

1.拓扑 2.IP地址规划 设备/地址/vlan设备/地址汇聚交换机/VLAN10192.200.10.0/24汇聚交换机/VLAN20192.200.20.0/24汇聚交换机/VLAN30192.200.30.0/24汇聚交换机/VLAN40192.200.40.0/24汇聚交换机/VLAN50192.200.50.0/24汇聚交换机/VLAN60192.200.60.0/24防火墙/VLAN70/服务器…