【Redis场景1】用户登录注册
细节回顾:
关于cookie和session不熟悉的朋友;
建议阅读该博客:https://www.cnblogs.com/ityouknow/p/10856177.html
执行流程:

在单体模式下,一般采用这种模式来存储,传递、认证用户登录、注册等信息;
如果浏览器禁用Cookies,****如何保障整个机制的正常运转。
url拼接或者POST请求:每个请求都携带SessionIDToken机制:在用户登录或者注册的时候,与用户信息绑定一个随机字符串,用于用户状态管理
在分布式下的Session问题:

为了支撑更大的流量,后台往往需要在多台服务器中部署,那如果用户在 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数据拷贝),该解决方式有以下问题:
- 每台服务器中都有完整的一份
session数据,服务器压力过大。 session拷贝数据时,可能会出现延迟
所以我们需要采用的中间件需要有以下特征(Redis):
- 数据共享
- 基于内存读取
- 满足数据存储格式(
KEY:VALUE)
实现场景:
该场景实现流程:以下分析结合部分代码(聚焦于redis的实现);
完整后端代码可在Github中获取:https://github.com/xbhog/hm-dianping

开发流程:
【获取验证码流程】前端根据手机号提交获取验证码请求:触发sendCode方法:
- 校验手机号合法性
- 生成验证码
- 保存验证码到
redis中 - 发送验证码(模拟实现,未调用第三方平台)
- 结束
在第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方法:
校验手机号合法性
校验验证码合法性:从
redis中获取数据库通过手机号查询用户
- 不存在:创建用户
- 存在:执行后续逻辑(4)
UUID生成随机TOKEN将用户信息存入
Redis中,设置过期时间返回前端
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的存活时间。
子问题:
对于拦截器,我们不能拦截所有的路径,比如获取验证码请求,用户登录,首页等;
- 用户在所拦截的范围内:则可执行刷新
Token操作 - 用户不在拦截的范围内:无法执行刷新
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令牌存活时间。
获取
token查询
redis用户- 存在,不拦截(执行3)
- 不存在,拦截
用户信息保存到
threadLocal中刷新
Token有效期放行
相关代码:
/**
* @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;
}
}
输入及参考
【Redis场景1】用户登录注册的更多相关文章
- android安卓Sqlite数据库实现用户登录注册
看了很多别人写的安卓SQlite数据的操作代码,一点也不通俗易懂,我觉得我写的不错,而且安卓项目也用上了,所以在博客园里保存分享一下!建立一个类 并继承SQLiteOpenHelper public ...
- javaweb学习总结(二十二)——基于Servlet+JSP+JavaBean开发模式的用户登录注册
一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp ...
- 纯JSP实现用户登录注册,记事本
没有美化,没有格式,没有样式 1.JSP登陆注册 将用户注册的信息保存在application对象中,用于登录时的验证. 首页如下: 如果未登录,在 session 中找不到 currentUser ...
- Asp.NET WebApi+Redis实现单用户登录实战演练
一.课程介绍 本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的一部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高级编程的技巧分享出来给大家进行学习,不断的收集.整理和 ...
- JavaWeb学习 (二十一)————基于Servlet+JSP+JavaBean开发模式的用户登录注册
一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp ...
- Java Spring+Mysql+Mybatis 实现用户登录注册功能
前言: 最近在学习Java的编程,前辈让我写一个包含数据库和前端的用户登录功能,通过看博客等我先是写了一个最基础的servlet+jsp,再到后来开始用maven进行编程,最终的完成版是一个 Spri ...
- 基于Servlet+JSP+JavaBean开发模式的用户登录注册
http://www.cnblogs.com/xdp-gacl/p/3902537.html 一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBea ...
- javaweb(二十二)——基于Servlet+JSP+JavaBean开发模式的用户登录注册
一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp ...
- /*用户登录注册页面输入框的设置*/<span>的使用
<!DOCTYPE html> /*用户登录注册页面输入框的设置*/ <html lang="en"> <head> <meta char ...
- flask 开发用户登录注册功能
flask 开发用户登录注册功能 flask开发过程议案需要四个模块:html页面模板.form表单.db数据库操作.app视图函数 1.主程序 # app.py # Auther: hhh5460 ...
随机推荐
- Java并发编程 | 从进程、线程到并发问题实例解决
计划写几篇文章讲述下Java并发编程,帮助一些初学者成体系的理解并发编程并实际使用,而不只是碎片化的了解一些Synchronized.ReentrantLock等技术点.在讲述的过程中,也想融入一些相 ...
- P3919 【模板】可持久化线段树 1(可持久化数组)
还是用主席树来做(因为提到不同的版本),这时候的主席树不是以权值为下标的,就是普通的线段树,维护范围1~n,i存的是a[ ]中的数. 1 #include <bits/stdc++.h> ...
- 220722 T4 求和 /P4587 [FJOI2016]神秘数 (主席树)
好久没打主席树了,都忘了怎么用了...... 假设我们选了一些数能构成[0,x]范围内的所有值,下一个要加的数是k(k<=x+1),那么可以取到[0,x+k]内的所有取值,所以有一种做法: 对于 ...
- printf-库函数重定向、重载
重定向:这是针对标准C语言语法来讲,指的是我们可以重新声明库函数并定义库函数的执行体 重载:这是CPP引入的新特性,即根据函数不同参数个数或参数类型来对同一函数名进行不同定义 C和CPP对重定向处理机 ...
- MyBatis的各种查询功能
1.查询一个实体类对象 /** * 根据用户id查询用户信息 * @param id * @return */ User getUserById(@Param("id") int ...
- Unexpected token u in JSON at position 0
文章目录 1.1 错误原因: 1.2 解决思路: 1.1 错误原因: 因为JSON.parse()不能解析字符串中的undefined 出错的结果:某一行的这个字段的值为空,就会报错,整个表格都显示不 ...
- 11.mixins混合类
一.混合类(mixins) 使用基于类的视图,最大的优势之一就创建可复用的代码 我们编写的非常类似的代码,可以抽象出来,将这部分代码放到mixin类系列中,然后作为父类提供子类继承使用 from ...
- 洛P8109题解
摘自本人洛谷博客,原文章地址:https://www.luogu.com.cn/blog/cjtb666anran/solution-p8109 本题原题目摘录: 本场比赛共有 \(n\) 道题,Ci ...
- Prometheus 监测 RocketMQ 最佳实践
本文作者:郭雨杰,阿里云智能技术专家. Prometheus 集成的 50 多款云产品中,RocketMQ 在可观测方面实现了非常完善的功能,是一个特别具有代表性的云产品. 01 RocketMQ如何 ...
- Linux下安装 SkyWalking 分布式追踪系统
Linux下安装 SkyWalking 分布式追踪系统 1.SkyWalking简介 1.1 SkyWalking介绍 SkyWalking项目是由华为大牛吴晟开源的个人项目,目前已经加入Apache ...