【SpringBoot】Redis集中管理Session和自定义用户参数解决登录状态及校验问题

本文主要是介绍【SpringBoot】Redis集中管理Session和自定义用户参数解决登录状态及校验问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

🏡浩泽学编程:个人主页

 🔥 推荐专栏:《深入浅出SpringBoot》《java对AI的调用开发》
              《RabbitMQ》《Spring》《SpringMVC》

🛸学无止境,不骄不躁,知行合一

文章目录

  • 前言
  • 一、分布式Session问题
    • Redis集中管理Session
  • 二、用户校验问题
    • 自定义用户参数
    • MVC拦截器
  • 总结


前言

主要讲解:Redis集中管理Session存储用户登录信息,解决分布式Session问题;自定义用户参数配合MVC拦截器实现控制层入参前进行用户校验,解决每层用户接口都要做用户校验问题。


一、分布式Session问题

在实现用户登录时,我们需要注意的就是就是用户权限带来的用户登录状态问题:在大多数项目中,应用采用Nginx反向代理,这会存在一种情况——用户信息在Tomcat1登录之后,用户信息放在Tomcat1的Session里,过一会,请求又被Nginx分发到Tomcat2上,这是Tomcat2上Session里还没有用户信息,于是又要登录。

解决方案有很多:

  • Session复制
    • 优点
      • 无需修改代码,只需要修改Tomcat配置
    • 缺点
      • Session同步传输占用内网带宽
      • 多台Tomcat同步性能指数级下降
      • Session占用内存,无法有效水平扩展
  • 前端存储
    • 优点
      • 不占用服务器端内存
    • 缺点
      • 存在安全风险
      • 数据大小受到cookie限制
      • 占用外网带宽
  • Session粘滞
    • 优点
      • 无需修改代码
      • 服务器端可以水平扩展
    • 缺点
      • 增加新机器,会重新Hash,导致重新登录
      • 应用重启,需要重新登录

Redis集中管理Session

  • 这里采用Redis集中管理所有Session,即多个地方从一个地方(Redis)中获取信息。当然大家也可以使用SpringSession实现分布式Session。
  • 实现:登录时将用户信息存入Redis,这里只是实现了简单的集中储存用户信息,并没有
  • 对于Redis集中管理Session,我在做黑马点评时记录过,很完善,可以看看:短信登录实现(黑马点评)

Redis配置类:键值对序列化

/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: RedisConfig* @Description: Redis配置类* @Date: 2024/1/25 15:32*/
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();//key序列化redisTemplate.setKeySerializer(new StringRedisSerializer());//value序列化redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//hash的key序列化redisTemplate.setHashKeySerializer(new StringRedisSerializer());//hash的value序列化redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());//注入连接工厂redisTemplate.setConnectionFactory(redisConnectionFactory);return redisTemplate;}
}

登录逻辑:

/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: IUserServiceImpl* @Description: 登录处理* @Date: 2024/1/23 15:52*/
@Service
@Primary
public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RedisTemplate redisTemplate;/**** @Description: 登录* @param loginVo* @methodName: doLogin* @return: com.example.seckill.vo.RespBean* @Author: dragon_王* @Date: 2024-01-23 17:38:35*/@Overridepublic RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {String mobile = loginVo.getMobile();String password = loginVo.getPassword();//根据手机号获取用户User user = userMapper.selectById(mobile);if (null == user) {throw new GlobalException(RespBeanEnum.LOGIN_ERROR);}//判断密码是否正确if (!MD5Util.formPassToDBPass(password,user.getSalt()).equals(user.getPassword())){throw new GlobalException(RespBeanEnum.LOGIN_ERROR);}//生成cookieString ticket = UUIDUtil.uuid();redisTemplate.opsForValue().set("user:" + ticket,user);CookieUtil.setCookie(request,response,"userTicker",ticket);return RespBean.success();}/**** @Description: 根据cookie获取用户* @param userTicker* @methodName: getUserByCookie* @return: com.example.seckill.pojo.User* @Author: dragon_王* @Date: 2024-01-25 16:03:14*/@Overridepublic User getUserByCookie(String userTicker,HttpServletRequest request,HttpServletResponse response) {if (StringUtils.isEmpty(userTicker)){return null;}User user = (User) redisTemplate.opsForValue().get("user:" + userTicker);if (user != null) {CookieUtil.setCookie(request,response,"userTicker",userTicker);}return user;}
}

主要看如下代码:

//在账号密码正确后随机生成UUID,将 “user”+UUID 作为用户信息唯一key值,并存入redis中,这里我的cookie里存了一份UUID,因为Cookie存在于服务器或本地,不同于Session(只能存在于服务器),在使用应用程序时,不管请求在哪个tomcat,而用户在自己浏览器或本地上的信息能获取到。CookieUtil是自定义封装的cookie存取的工具类。String ticket = UUIDUtil.uuid();redisTemplate.opsForValue().set("user:" + ticket,user);CookieUtil.setCookie(request,response,"userTicker",ticket);return RespBean.success();//如果Cookie里没有就说明用户没登陆过,因为我在登录时已经存过当前用户随机生成的UUID作为Cookie,没有的话就会返回空,有就根据当前用户UUID获取存在redis中的序列化的用户信息public User getUserByCookie(String userTicker,HttpServletRequest request,HttpServletResponse response) {if (StringUtils.isEmpty(userTicker)){return null;}User user = (User) redisTemplate.opsForValue().get("user:" + userTicker);if (user != null) {CookieUtil.setCookie(request,response,"userTicker",userTicker);}return user;}

在黑马点评实现登录时,关于分布式Session,用到了token刷新:就是在用户登录时将用户信息存储到redis中,所谓token值,使用的就是UUID,同时token也作为redis中的key值,并且设置过期时间,但是这里有个刷新机制,就是设置拦截器——当用户访问某个页面时就自动刷新过期时间,使得如果用户一直在操作就不会突然过期,详细看那篇文章,这里不再补充。

二、用户校验问题

对于用户操作,会有权限限制即判断用户是否登录,如果每层用户业务接口都做用户校验会太过麻烦,所以可以自定义用户参数,在每次controller层入参之前就去做拦截校验。

自定义用户参数

/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: UserArgumentResolve* @Description: 自定义用户参数*              获取用户是否登录* @Date: 2024/1/25 16:31*/
@Component
public class UserArgumentResolve implements HandlerMethodArgumentResolver {@Autowiredprivate IUserService userService;@Overridepublic boolean supportsParameter(MethodParameter parameter) {Class<?> parameterType = parameter.getParameterType();return parameterType == User.class;}@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);HttpServletResponse response = webRequest.getNativeRequest(HttpServletResponse.class);String ticker = CookieUtil.getCookieValue(request, "userTicker");if (StringUtils.isEmpty(ticker)){return null;}return userService.getUserByCookie(ticker,request,response);}
}

解释:supportsParameter函数判断参数类型是否为User类型,是的的话执行resolveArgument函数,resolveArgument函数则会先查询当前cookie里是否有用户信息,没有的话返回空,有的话返回用户通过校验。

MVC拦截器

/*** @Version: 1.0.0* @Author: Dragon_王* @ClassName: WebConfig* @Description: MVC配置类* @Date: 2024/1/25 16:27*/
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate UserArgumentResolve userArgumentResolve;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(userArgumentResolve);}
}

这样设置后,以后Controller层接口传参只需要有User类型对象,自动校验用户状态,判断用户是否属于登录状态。


总结

以上就是Redis集中管理Session存储用户登录信息,解决分布式Session问题;自定义用户参数配合MVC拦截器实现控制层入参前进行用户校验,解决每层用户接口都要做用户校验问题的讲解。

这篇关于【SpringBoot】Redis集中管理Session和自定义用户参数解决登录状态及校验问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/696482

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory