【Redis场景1】用户登录注册

news2024/11/14 19:03:31

细节回顾:

关于cookiesession不熟悉的朋友;

建议阅读该博客:https://www.cnblogs.com/ityouknow/p/10856177.html

执行流程:
在这里插入图片描述

在单体模式下,一般采用这种模式来存储,传递、认证用户登录、注册等信息;

**如果浏览器禁用Cookies,**如何保障整个机制的正常运转。

  1. url拼接或者POST请求:每个请求都携带SessionID
  2. Token机制:在用户登录或者注册的时候,与用户信息绑定一个随机字符串,用于用户状态管理

在分布式下的Session问题:

img

为了支撑更大的流量,后台往往需要在多台服务器中部署,那如果用户在 A 服务器登录了,第二次请求跑到服务 B 就会出现登录失效问题如何解决?

  • Nginx ip_hash 策略,服务端使用 Nginx 代理,每个请求按访问 IP hash分配,这样来自同一 IP 固定访问一个后台服务器,避免了在服务器 A 创建 Session,第二次分发到服务器 B 的现象。
  • Session 复制,任何一个服务器上的 Session 发生改变(增删改),该节点会把这个 Session 的所有内容序列化,然后广播给所有其它节点。
  • 共享 Session,服务端无状态话,将用户的 Session 等信息使用缓存中间件来统一管理,保障分发到每一个服务器的响应结果都一致。

第一种策略(Nginx ip_hash 策略)可以看该博客:https://www.cnblogs.com/xbhog/p/16929786.html

我们主要实现第三种:通过缓存中间件来统一管理。

解决痛点:

在原来场景中存在的Session不互通的问题(Session数据拷贝),该解决方式有以下问题:

  1. 每台服务器中都有完整的一份session数据,服务器压力过大。
  2. session拷贝数据时,可能会出现延迟

所以我们需要采用的中间件需要有以下特征(Redis):

  1. 数据共享
  2. 基于内存读取
  3. 满足数据存储格式(KEY:VALUE)

实现场景:

该场景实现流程:以下分析结合部分代码(聚焦于redis的实现);

完整后端代码可在Github中获取:https://github.com/xbhog/hm-dianping

img

开发流程:

**【获取验证码流程】**前端根据手机号提交获取验证码请求:触发sendCode方法:

  1. 校验手机号合法性
  2. 生成验证码
  3. 保存验证码到redis
  4. 发送验证码(模拟实现,未调用第三方平台)
  5. 结束

在第3步的实现如下:

stringRedisTemplate.opsForValue().set(PHONE_CODE_KEY+phone,code,2L,TimeUnit.MINUTES);

使用的stringRedisTemplate继承于RedisTemplate,局限:key和value必须是String类型:

public class StringRedisTemplate extends RedisTemplate<String, String> {
......
}

设置验证码Key值:phone:code:,code为随机6位字符串。

设置key的过期时间。

**【登录功能】**前端根据手机号和验证码发送登录功能(如上图):触发login方法:

  1. 校验手机号合法性

  2. 校验验证码合法性:从redis中获取

  3. 数据库通过手机号查询用户

    1. 不存在:创建用户
    2. 存在:执行后续逻辑(4)
  4. UUID生成随机TOKEN

  5. 将用户信息存入Redis中,设置过期时间

  6. 返回前端Token

第2步的实现如下:

String redisCode = stringRedisTemplate.opsForValue().get(PHONE_CODE_KEY + loginForm.getPhone());
if(StringUtils.isBlank(loginForm.getCode()) || !loginForm.getCode().equals(redisCode)){
    return Result.fail("验证码错误");
}

第4、5步的实现如下:

//随机生成token,作为登录令牌
String token = UUID.randomUUID().toString(true);
//保存到redis中
String tokenKey = LOGIN_USER_KEY + token;
stringRedisTemplate.opsForHash().putAll(tokenKey,userBeanToMap);
//设置过期时间
stringRedisTemplate.expire(tokenKey,30L, TimeUnit.MINUTES);

设置用户KEY"login:token:",这里redis的使用的数据结构是Map,方便对单一字段操作。

使用了hashmap结构,需要单独对tokenKey设置过期时间(30m);

场景问题:

Redis Key续期问题:

在设置token的时候,在redis给的过期时间是30分钟,这里就有个问题,用户在30分钟内,结束请求,那没有问题,但只要用户的在线时间超过30分钟,redis删除token,直接给用户强制下线了;这个实在是不符合实际场景。

解决方式:

在请求的过程总给加入一层拦截器,用来刷新Token的存活时间。

子问题:

对于拦截器,我们不能拦截所有的路径,比如获取验证码请求,用户登录,首页等;

    1. 用户在所拦截的范围内:则可执行刷新Token操作
    2. 用户不在拦截的范围内:无法执行刷新Token操作

子问题解决:

增加两层拦截器,第一层拦截全部请求路径,第二层拦截器基于第一层信息,对未登录的请求进行拦截。

需要设置两层拦截器的优先级,

**order()😗*指定要使用执行器顺序。默认值为0(最高)

@Configuration
public class MybatisConfig implements WebMvcConfigurer {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

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

在这里插入图片描述

**【拦截器1实现】**所有路径拦截,刷新登录Token令牌存活时间。

  1. 获取token

  2. 查询redis用户

    1. 存在,不拦截(执行3)
    2. 不存在,拦截
  3. 用户信息保存到threadLocal

  4. 刷新Token有效期

  5. 放行

相关代码:

/**
 * @author xbhog
 * @describe: 拦截器实现:校验用户登录状态
 * @date 2022/12/7
 */
public class RefreshTokeInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokeInterceptor(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("authorization");
        //如果页面没有登录,则没有token,直接放行给下一个拦截器
        if(StringUtils.isEmpty(token)){
            return true;
        }
        String tokenKey = LOGIN_USER_KEY + token;
        Map<Object, Object> userRedis = stringRedisTemplate.opsForHash().entries(tokenKey);
        if(userRedis.isEmpty()){
            return true;
        }
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userRedis, new UserDTO(), false);
        //用户存在,放到threadLocal
        UserHolder.saveUser(userDTO);
        //登录续期
        stringRedisTemplate.expire(tokenKey,30L, TimeUnit.MINUTES);
        return true;
    }

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

**【拦截器2实现】**需要登录的路径拦截;

实现代码:

/**
 * @author xbhog
 * @describe: 拦截器实现:校验用户登录状态
 * @date 2022/12/7
 */
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;
    }
}

输入及参考

cookie和session

斐波那契散列和hashMap实践

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

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

相关文章

阿里巴巴最新推出王者笔记:“Spring MVC 源码与实践”

前言&#xff1a; Spring MVC 是 Spring 框架中用于 Web 应用快速开发的一个模块。Spring MVC 的 MVC 是 Model-View-Contoller 的缩写。它是一个广泛应用于图形化用户交互开发中的设计模式&#xff0c;不仅常见于 Web 开发&#xff0c;也广泛应用于如 Swing 和 JavaFX 等桌面…

Java知识要点

第1章 Java概述 重要特性&#xff1a; Write Once Run Anyway 简单性&#xff1a;相比C移除指针、运算符重载、多重继承等&#xff0c;垃圾自动回收。 平台无关性&#xff1a;Java引进虚拟机&#xff08;JVM&#xff0c;Java Virtual Machine&#xff09;概念。 安全性&am…

论文推荐:Rethinking Attention with Performers

重新思考的注意力机制&#xff0c;Performers是由谷歌&#xff0c;剑桥大学&#xff0c;DeepMind&#xff0c;和艾伦图灵研究所发布在2021 ICLR的论文已经超过500次引用 传统的Transformer的使用softmax 注意力&#xff0c;具有二次空间和时间复杂度。Performers是Transformer…

常用射频器件性能指标

IFM&#xff08;Instantaneous FrequencyMeasurement&#xff09;&#xff1a;瞬时测频接收机&#xff1b; SOC&#xff08;System on Chip&#xff09;&#xff1a;片上系统&#xff0c;可独立实现接收机功能&#xff1b; AIU&#xff1a;前端 一、放大器关键参数 1.1 -1dB压…

如何选择好的软件测试技术?

软件测试技术是指测试软件或软件一部分的方法或方式。每种测试技术都有其自身的优势。不同的技术针对不同类型的缺陷。因此&#xff0c;说一种技术是最好的是错误的。根据软件及其要求&#xff0c;一种测试技术可能比另一种更适合用于该目的。有时&#xff0c;结合使用不同的测…

安卓APP源码和设计报告——魔幻相机

课程设计报告书 Android大作业 学 院 计算机科学与工程学院 专 业 计算机科学与技术一班 学生姓名 小组成员 学生学号 指导教师 课程编号 课程学分 起始日期 教师评语 教师签名&#xff1a;日期&#xff1a; 成绩评定 备注 Android大作业 魔幻相机 一、选题背景 据统计&am…

[激光原理与应用-47]:《焊接质量检测》-4-普雷茨特激光焊接过程监控系统LWM分析

目录 第1章 激光焊接过程监控系统LWM概述 第2章 产品特性与功能 2.1 生产相关的信息 2.2 原始信息检测 2.3 焊接质量分析信息 2.4 缺陷报告与生产控制 2.5 LWM给客户带来的好处 2.6 适用范围 2.7 人机界面 (HMI) 第3章 焊接质量检测的原理 3.1 基本原理 3.2 技术规…

excel_阻止常数值串以科学计数法显示@数据分列@空格分隔符号分列数据

文章目录excel_阻止常数值串以科学计数法显示WPS设置某列的数据显示格式核心步骤数据-分列选择分割符设置文本类型收尾工作数据居中Office-Excelexcel_阻止常数值串以科学计数法显示 WPS 设置某列的数据显示格式 核心步骤 如果您的数据是普通数据,那么类型框里输入0就可以了…

太忙,没时间学?在职人员如何高效备考MBA?

对于很多在职人员来说&#xff0c;想要进一步深造提升学历&#xff0c;备考MBA无疑是个不错的选择。但近几年随着MBA考生人数的增长&#xff0c;其竞争也愈加激烈。因此想要取得好的成绩&#xff0c;成功上岸&#xff0c;不仅仅需要持续不断的努力&#xff0c;时间的科学管理和…

机器人与视觉,基于TCP(工具坐标)偏移

基于工具坐标系的移动偏转。 基于TCP旋转的特点在于&#xff0c;具有1个固定端点&#xff0c;多个活动端的特点。 我们在建立TCP左边偏移的时候&#xff0c;可以将2个点近似的模拟在同一个坐标系下 基于TCP偏转的特点在于&#xff0c;工作的时候&#xff0c;示教点与工作点的位…

CSS -- 03. CSS盒子模型

文章目录盒子模型1 盒子模型1.1 看透网页的本质1.2 盒子模型组成1.3 边框1.4 表格的细线边框1.5 边框会影响盒子实际大小1.6 内边距&#xff08;padding&#xff09;1.7 外边距&#xff08;margin&#xff09;1.8 外边距合并1.9 清除内外边距2 圆角边框3 盒子阴影4 文字阴影盒子…

netfilteriptables探讨(4)——nat的实现与使用

在之前的几篇文章中&#xff0c;我们讨论了netfilter与iptables的实现原理与基本用法。在netfilter&iptables的各种使用场景中&#xff0c;nat是最常用也是最复杂的用法之一。许多常用的网络使用模式都是通过nat iptables规则实现的&#xff0c;例如docker默认的bridge网络…

Netty_06_手写RPC基础版(实践类)

文章目录一、前言二、整体运行三、客户端和服务端3.1 客户端3.2 服务端3.3 RpcServerInitializer和RpcClientInitializer四、小结一、前言 常用的rpc框架&#xff1a;dubbo thrift gRPC rpc定义&#xff1a;remote proceducer call rpc目的/解决的问题&#xff1a; 像调用本地…

Nginx实战应用-负载均衡

在上篇文章的基础上我们再创建两个服务&#xff0c;三个服务的端口分别是 8081 8082 8083. 2.Nginx配置 upstream块 upstream name{…} upstream gupao{ server 192.168.12.1:8081; server 192.168.12.1:8082; server 192.168.12.1:8083; } server { location / { pr…

平均月薪15k+?自动化测试工程师?3个月教你从“点工”蜕变为“码农”

前言 一、自动化测试工程师平均收入【看图&#xff08;来自职友集&#xff09;】 基本收入都在15k左右&#xff0c;随着技术的越来越牛逼工资也就会越来越高。 我的职业生涯开始和大多数测试人一样&#xff0c;刚开始接触都是纯功能界面测试。那时候在一家电商公司做测试&…

[附源码]计算机毕业设计基于vue+mysql开发的考试系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【PAT甲级 - C++题解】1092 To Buy or Not to Buy

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;PAT题解集合 &#x1f4dd;原题地址&#xff1a;题目详情 - 1092 To Buy or Not to Buy (pintia.cn) &#x1f511;中文翻译&#xff1a;买还是不买 &#x…

JAVA中的运算符-关系运算符

文章目录0 写在前面1 关系运算符说明2 举例3 写在最后0 写在前面 JAVA包含丰富的关系运算符&#xff0c;这些关系运算符最终结果一定是boolean类型。即两个结果&#xff1a;true false 1 关系运算符说明 符号说明ab,判断a和b的值是否相等&#xff0c;成立为true&#xff0c;…

游戏开发48课 性能优化6

3.7.2 算法优化 思路是找出最耗CPU的算法或逻辑&#xff0c;优化之。 空间换时间。利用预排序/预处理/缓存/动态规划等等思路换取CPU的性能。选取更快的算法。属于数据结构和算法的范畴&#xff0c;思路是将O(n2)降低成O(n)或O(logn)&#xff0c;具体可以参看《算法导论》《游…

springcloud 服务消费及熔断

目录 1. 服务消费方式 1.1 RestTemplate 1.2 feign 2. 服务熔断&#xff08;降级&#xff09; 2.1 在微服务架构中服务熔断的必要性 2.2 hystrix 1. 服务消费方式 1.1 RestTemplate 传统情况下在java代码里访问restful服务&#xff0c;一般使用Apache的HttpClient。不过…