【Redis】基于Redis实现共享Session登录

 用户登录是一种常用功能。这里记录一下基于Redis实现用户登录的代码。
 下面是登录的流程图:

  • 用户先提交手机号和验证码,服务器以手机号为key校验redis中存储的验证码,存在,则查询数据库中是否存在用户,不存在则创建并将token作为key,用户信息作为value保存在Redis中。
  • 用户登录成功后的请求需要在session中携带token,来进行身份的鉴权。
    在这里插入图片描述

1.用户短信验证码登录、注册代码

  • Controller层
	/**
     * 登录功能
     * @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
     */
    @PostMapping("/login")
    public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
        // TODO 实现登录功能
        return userService.login(loginForm, session) ;
    }
  • Service层
	@Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        String phone = loginForm.getPhone();
        // 1. 校验手机号
        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();
        if(user == null){
            // 5. 用户不存在
            user = createUserWithPhone(phone);
            // 6. 不存在创建用户

        }


        // 7. 保存用户信息到session
        // 7.1 生成token
        String token = UUID.randomUUID().toString(true);
        // 7.2 保存到redis,hash存储,使用Map减少与服务器交互次数
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        // 将所有字段转为string
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fileName,fileValue)->fileValue.toString()));
        // 7.3 存储
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY+token,userMap);
        // 7.4 设置过期时间
        stringRedisTemplate.expire(LOGIN_USER_KEY+token, LOGIN_USER_TTL, TimeUnit.SECONDS);
        // 返回token
        System.out.println(token);
        return Result.ok(token);
    }

2.校验登录状态代码

 这段功能基于拦截器实现。考虑到用户的任何请求都可以刷新token的有效期,如下图所示,这部分由两个拦截器组成,第一个拦截器拦截所有请求,但都会放行,唯一的作用的token存在,刷新其在Redis中的有效期。第二个拦截没有token的请求。
在这里插入图片描述

  • 拦截器配置
@Configuration
public class MvcConfig implements WebMvcConfigurer {

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

    }
}

  • 第一个拦截器
public class RefreshTokenInterceptor implements HandlerInterceptor {

    // 没有注册为Bean,只能手动引入
    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate redisTemplate) {
        this.stringRedisTemplate = redisTemplate;
    }
    @Override
    public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
        // 1. 获取请求头的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;
        }

        // 2. 基于token获取用户
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(RedisConstants.LOGIN_USER_KEY + token);

        // 3. 用户是否存在
        if(userMap.isEmpty()){
            // 4. 放行
            return true;
        }
        // 5. 转为UserDTO
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        // 6. 保存用户信息到ThreadLocal
        UserHolder.saveUser(userDTO);
        // 刷新有效期
        stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY+token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        // 7. 放行
        return true;
    }

    @Override
    public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 4. 清理资源
        UserHolder.removeUser();
    }
}

  • 第二个拦截器
public class LoginInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
        // 1. 拦截
        if(UserHolder.getUser() == null){
            response.setStatus(401);
            return false;
        }
        // 7. 已登录,放行
        return true;
    }

    @Override
    public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 4. 清理资源
        UserHolder.removeUser();
    }
}

  • 其中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();
    }
}

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

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

相关文章

【Quixel Mixer】简单介绍

一、下载 官网下载地址&#xff1a;Quixel Mixer - All-in-one texturing & material creation tool 下载好之后双击exe来安装 等待安装完成 下载后打开&#xff0c;新建一个工程和Mix 二、界面介绍 我们先将软件界面分为如下3个部分 1号区域为菜单栏 2号区域介绍 2号…

Windows Docker 安装

Docker 并非是一个通用的容器工具&#xff0c;它依赖于已存在并运行的 Linux 内核环境。 Docker 实质上是在已经运行的 Linux 下制造了一个隔离的文件环境&#xff0c;因此它执行的效率几乎等同于所部署的 Linux 主机。 因此&#xff0c;Docker 必须部署在 Linux 内核的系统上…

Linux —— 定时任务(sleep、crontab、at)

目录 1、使用 sleep 来完成定时任务 2、使用 crontab 来进行定时任务 3、使用 at 来执行单次的定时任务 1、使用 sleep 来完成定时任务 sleep n 等待 n 秒再继续往后执行 usleep n 等待 n 微秒再继续往后执行&#xff08;1 秒等于 1 000 000 微秒&#xf…

聚道云如何实现薪人薪事与金蝶云无缝对接,破解财务难题?

一、客户介绍 某科技有限责任公司是一家在信息技术领域具有显著影响力的企业&#xff0c;长期致力于为企业提供全面的解决方案和技术支持。在业务范围上&#xff0c;该公司覆盖了多个关键领域&#xff0c;包括云计算、大数据、人工智能等前沿技术。公司不仅提供定制化的技术解…

从资金管理的角度谈谈个人怎样交易现货白银

刚进入现货白银市场&#xff0c;个人要怎么交易现货白银的&#xff1f;这就涉及现货白银交易生涯的开启问题&#xff0c;开头开的好&#xff0c;我们整个交易生涯都将会有所裨益&#xff0c;所以我们要为个人怎样交易现货白银开个好头。下面我们就从资金管理的角度出发&#xf…

可视化搭建一个智慧零售订单平台

前言 智慧零售行业是在数字化浪潮中快速发展的一个领域&#xff0c;它利用先进的信息技术和大数据分析来提升零售业务的效率和顾客体验。智慧零售订单平台&#xff0c;具有跨平台、数据智能清洗和建模&#xff0c;以及更加丰富的数据展示形式等优势。智慧零售订单平台可以以文…

1.gradle编译和运行

1.在Windows 项目的根目录下使用.\gradlew.bat build命令进行编译。 如果出错的原因是连接超时&#xff1a; Exception in thread “main” java.io.IOException: Downloading from https://services.gradle.org/distributions/gradle-8.6-bin.zip failed: timeout (10000ms) a…

薄板/厚板模态分析Matlab有限元编程 | 【源码+理论文本】|板单元|板壳单元|Mindlin Reissner

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

软件测试方法 -- 等价类边界值

测试用例的定义 测试用例是为了特定的目的而设计的一组测试输入、执行条件和预期的结果&#xff0c;以便测试是否满足某个特定需求。通过大量的测试用例来检验软件的运行效果&#xff0c;他是指导测试工作进行的依据。 下面我们介绍几种常用的黑盒测试方法 等价类划分法 定…

挑战杯 车位识别车道线检测 - python opencv

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 车位识别车道线检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) …

BOM(物料清单)是什么?在生产管理中有什么用?

BOM&#xff08;物料清单&#xff09;是什么&#xff1f;在生产管理中有什么用&#xff1f; 一、什么是BOM &#xff08;物料清单&#xff09;&#xff1f; 举个例子&#xff0c;做一双运动鞋&#xff0c;需要的配料可以笼统的分为鞋身和鞋带&#xff0c;那么由鞋身和鞋带组成…

部署高斯喷射项目gaussian-splatting

硬件要求 支持 CUDA 的 GPU&#xff0c;具有 7.0 的计算能力24 GB VRAM 软件要求 Conda用于 PyTorch 扩展的 C 编译器&#xff08;Visual Studio 2019&#xff09; CUDA SDK 11 for PyTorch 扩展&#xff0c;在 Visual Studio 之后安装C 编译器和 CUDA SDK 必须兼容 拉取源码 …

AI实景无人自动直播间怎么搭建?三步教你轻松使用

最近很多朋友看到AI自动直播带货玩法&#xff0c;也想开启自己的自动直播间&#xff0c;但还是有些问题比较担心&#xff0c;这种自动讲解、自动回复做带货的直播间是不是很麻烦&#xff1f; 实景无人自动直播 ​ 实际上这种直播间搭建相当简单便捷&#xff01;今天跟着笔者&…

sqlserver字段2按字段1分组后;合并字段2

效果 相同dzbm的mc通过‘;’合并 sqlserver语句 按字段dzbm分组,有相同dzbm的mc通过 ;合并成一个字段,其它字段都选择第一个 SELECT dzbm, STUFF((SELECT DISTINCT ; + mc FROM tablenameWHERE dzbm = p.dzbm FOR XML PATH()), 1

PHP反序列化--pop链

目录 一、了解pop链 1、pop链&#xff1a; 2、pop链触发规则&#xff1a; &#xff08;1&#xff09;通过普通函数触发&#xff1a; &#xff08;2&#xff09;通过魔术方法触发&#xff1a; 3、pop链魔术方法例题&#xff1a; 一、了解pop链 1、pop链&#xff1a; pop链…

力扣大厂热门面试算法题 43-45

43. 字符串相乘&#xff0c;44. 通配符匹配&#xff0c;45. 跳跃游戏 II&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.18 可通过leetcode所有测试用例。 目录 43. 字符串相乘 解题思路 完整代码 Python Java 44. 通配符…

【Redis知识点总结】(五)——Redis实现分布式锁

Redis知识点总结&#xff08;五&#xff09;——Redis实现分布式锁 setnxsetnx expiresetnx expire lua脚本set nx exset nx ex 随机值set nx ex 随机值 lua脚本set ex nx 随机值 lua脚本 锁续期RedissonRedLock 在Redis的众多应用场景中&#xff0c;分布式锁是Redis比…

FDA: 用于语义分割的傅里叶域自适应

论文链接&#xff1a;https://arxiv.org/abs/2004.05498 代码链接&#xff1a;GitHub - YanchaoYang/FDA: Fourier Domain Adaptation for Semantic Segmentation 机构&#xff1a;UCLA 发表于2020CVPR 这篇文章别的地方略读了&#xff0c;主要看看方法&#xff0c;感兴趣自…

淘宝商品详情API接口采集商品上货

使用淘宝商品详情API接口采集商品信息以进行上货是一个常见的需求&#xff0c;但需要注意的是&#xff0c;淘宝的API接口使用受到严格的限制和规定&#xff0c;需要遵循淘宝的开放平台政策。以下是一般性的步骤和建议&#xff0c;但请确保在实际操作中遵循淘宝的官方文档和规定…

极智芯 | 解读移动端芯片荟萃篇 主流移动芯片性能对比

欢迎关注我的公众号「极智视界」,获取我的更多技术分享 大家好,我是极智视界,本文分享一下 解读移动端芯片荟萃篇 主流移动芯片性能对比。 要说芯片的应用场景一般都会说云边端、云边端的,这里的移动端芯片当然是会属于云边端中的端场景了,主要是面向手机、平板等应用。下…