微服务Redis-Session共享登录状态
一、背景
随着项目越来越大,需要将多个服务拆分成微服务,使代码看起来不要过于臃肿,庞大。微服务之间通常采取feign交互,为了保证不同微服务之间增加授权校验,需要增加Spring Security登录验证,为了多个服务之间session可以共享,可以通过数据库实现session共享,也可以采用redis-session实现共享。
本文采取Spring security做登录校验,用redis做session共享。实现单服务登录可靠性,微服务之间调用的可靠性与通用性
二、代码
本文项目采取 主服务一服务、子服务二 来举例
1、服务依赖文件
主服务依赖
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '2.5.4'implementation group: 'org.springframework.session', name: 'spring-session-data-redis', version: '2.4.1'implementation(group: 'io.github.openfeign', name: 'feign-httpclient')implementation 'org.springframework.boot:spring-boot-starter-security'
子服务依赖
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '2.5.4'implementation group: 'org.springframework.session', name: 'spring-session-data-redis', version: '2.4.1'implementation 'org.springframework.boot:spring-boot-starter-security'
2、服务配置文件
主服务配置文件
#redis连接
spring.redis.host=1.2.3.4
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码
spring.redis.password=password
#连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.pool.max-idle=8
#连接池中的最小空闲连接
spring.redis.pool.min-idle=0
#连接超时时间(毫秒)
spring.redis.timeout=30000
#数据库
spring.redis.database=0
#redis-session配置
spring.session.store-type=redis
#部分post请求过长会报错,需加配置
server.tomcat.max-http-header-size=65536
子服务配置文件
#单独登录秘钥
micService.username=service
micService.password=aaaaaaaaaaaaa
#登录白名单
micService.ipList=1.2.3.4,1.2.3.5,127.0.0.1,0:0:0:0:0:0:0:1
spring.redis.host=1.2.3.4
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码
spring.redis.password=password
#连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
#连接池中的最大空闲连接
spring.redis.pool.max-idle=8
#连接池中的最小空闲连接
spring.redis.pool.min-idle=0
#连接超时时间(毫秒)
spring.redis.timeout=30000
#数据库
spring.redis.database=0
#最大请求头限制
server.maxPostSize=-1
server.maxHttpHeaderSize=102400
#redis session缓存
spring.session.store-type=redis
server.servlet.session.timeout=30m
3、登录校验文件
主服务SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {//注入密码加密的类@Beanpublic AuthenticationProvider authenticationProvider() {AuthenticationProvider authenticationProvider = new EncoderProvider();return authenticationProvider;}@Autowiredpublic void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService);auth.authenticationProvider(authenticationProvider());}public static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().successHandler((request,response,authentication) -> {HttpSession session = request.getSession();session.setAttribute("TaobaoUser",authentication.getPrincipal());ObjectMapper mapper = new ObjectMapper();response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();TaobaoUser user = (CurrentUser)session.getAttribute("TaobaoUser");out.write(mapper.writeValueAsString(user));out.flush();out.close();}).failureHandler((request,response,authentication) -> {ObjectMapper mapper = new ObjectMapper();response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();out.write(mapper.writeValueAsString(new ExceptionMessage("400",authentication.getMessage())));out.flush();out.close();}).loginPage("/Login.html").loginProcessingUrl("/login").and().authorizeRequests().antMatchers("/api/common/invalidUrl","/**/*.css", "/**/*.js", "/**/*.gif ", "/**/*.png ", "/**/*.jpg", "/webjars/**", "**/favicon.ico", "/guestAccess", "/Login.html","/v2/api-docs","/configuration/security","/configuration/ui","/api/common/CheckLatestVersionInfo").permitAll().anyRequest()//任何请求.authenticated().and().sessionManagement().maximumSessions(-1).sessionRegistry(sessionRegistry());;http.csrf().disable();}@Autowiredprivate FindByIndexNameSessionRepository sessionRepository;@Beanpublic SpringSessionBackedSessionRegistry sessionRegistry(){return new SpringSessionBackedSessionRegistry(sessionRepository);}}
EncoderProvider.java
@Service
public class EncoderProvider implements AuthenticationProvider {public static final Logger logger = LoggerFactory.getLogger(EncoderProvider.class);/*** 自定义验证方式*/@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {try {//支持用户名和员工号登录 可能为用户名或员工号String username = authentication.getName();String credential = (String) authentication.getCredentials();//加密过程在这里体现TaobaoUser user= userService.getUserData(username);//校验,用户名是否存在if(user==null){throw new DisabledException("用户名或密码错误");}//校验登录状态checkPassword()Collection<GrantedAuthority> authorities = new ArrayList<>();return new UsernamePasswordAuthenticationToken(userCurrent, credential, authorities);} catch (Exception ex) {ex.printStackTrace();throw new DisabledException("登录发生错误 : " + ex.getMessage());}}@Overridepublic boolean supports(Class<?> arg0) {return true;}
子服务SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {//注入密码加密的类@Beanpublic AuthenticationProvider authenticationProvider() {AuthenticationProvider authenticationProvider = new EncoderProvider();return authenticationProvider;}@Autowiredpublic void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {auth.authenticationProvider(authenticationProvider());}public static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);@Overrideprotected void configure(HttpSecurity http) throws Exception {logger.info("用户登录日志test1 username:"+http.toString());http.formLogin().loginProcessingUrl("/login").successHandler((request,response,authentication) -> {HttpSession session = request.getSession();session.setAttribute("TaobaoUser",authentication.getPrincipal());ObjectMapper mapper = new ObjectMapper();response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();TaobaoUser user = (TaobaoUser )session.getAttribute("TaobaoUser");out.write(mapper.writeValueAsString(user));out.flush();out.close();}).failureHandler((request,response,authentication) -> {ObjectMapper mapper = new ObjectMapper();response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();out.write(mapper.writeValueAsString(new ExceptionMessage("400",authentication.getMessage())));out.flush();out.close();}).loginPage("/Login.html").and().authorizeRequests().antMatchers("/**/*.css", "/**/*.js", "/**/*.gif ", "/**/*.png ","/**/*.jpg", "/webjars/**", "**/favicon.ico", "/Login.html","/v2/api-docs","/configuration/security","/configuration/ui").permitAll().anyRequest().authenticated().and().sessionManagement().maximumSessions(-1).sessionRegistry(sessionRegistry());http.csrf().disable();}@Autowiredprivate FindByIndexNameSessionRepository sessionRepository;@Beanpublic SpringSessionBackedSessionRegistry sessionRegistry(){return new SpringSessionBackedSessionRegistry(sessionRepository);}}
EncoderProvider.java
@Service
public class EncoderProvider implements AuthenticationProvider {public static final Logger logger = LoggerFactory.getLogger(EncoderProvider.class);@Value("${service.username}")private String userName1;@Value("${service.ipList}")private String ipList;/*** 自定义验证方式*/@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {try {//支持用户名和员工号登录 可能为用户名或员工号String username = authentication.getName();String credential = (String)authentication.getCredentials();TaobaoUser user=new TaobaoUser();if(username.equals(userName1)){List<String> ips = Arrays.asList(ipList.split(","));WebAuthenticationDetails details = (WebAuthenticationDetails) authentication.getDetails();String remoteIp = details.getRemoteAddress();logger.info("ip为{}-通过用户{}调用接口",remoteIp,username);if(!ips.contains(remoteIp)){throw new DisabledException("无权登陆!");}}else{throw new DisabledException("账户不存在!");}user.setA("A");Collection<GrantedAuthority> authorities = new ArrayList<>();return new UsernamePasswordAuthenticationToken(currentUser, credential, authorities);} catch (Exception ex) {ex.printStackTrace();throw new DisabledException("登录发生错误 : " + ex.getMessage());}}@Overridepublic boolean supports(Class<?> arg0) {return true;}}
4、主服务feign配置
FeignManage.java
#url = "${file.client.url}",
@FeignClient(name="file-service",fallback = FeignFileManageFallback.class,configuration = FeignConfiguration.class)
public interface FeignFileManage {@RequestMapping(value = "/file/upload", method = {RequestMethod.POST}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)ApiBaseMessage fileUpload(@RequestPart("file") MultipartFile file, @RequestParam("fileName") String fileName) ;}
public class FeignManageFallback implements FeignManage{@Overridepublic ApiBaseMessage fileUpload(MultipartFile file, String type) {return ApiBaseMessage.getOperationSucceedInstance("400","失败");}}
FeignFileManageFallback.java
FeignConfiguration.java
@Configuration
@Import(FeignClientsConfiguration.class)
public class FeignConfiguration {/**删除请求头文件*/final String[] copyHeaders = new String[]{"transfer-encoding","Content-Length"};@Beanpublic FeignFileManageFallback echoServiceFallback(){return new FeignFileManageFallback();}@Beanpublic FeignBasicAuthRequestInterceptor getFeignBasicAuthRequestInterceptor(){return new FeignBasicAuthRequestInterceptor();}/*** feign 调用,添加CurrentUser*/private class FeignBasicAuthRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {//1、使用RequestContextHolder拿到刚进来的请求数据ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (requestAttributes != null) {HttpServletRequest request = requestAttributes.getRequest();Enumeration<String> headerNames = request.getHeaderNames();if (headerNames != null) {while (headerNames.hasMoreElements()) {String name = headerNames.nextElement();String values = request.getHeader(name);//删除的请求头if (!Arrays.asList(copyHeaders).contains(name)) {template.header(name, values);}}}}else{template.header("Accept", "*/*");template.header("Accept-Encoding", "gzip, deflate, br");template.header("Content-Type", "application/json");}//增加用户信息if(requestAttributes!=null && SessionHelper.getCurrentUser()!=null){try {template.header("TaobaoUser", URLEncoder.encode(JSON.toJSONString(SessionHelper.getCurrentUser()),"utf-8") );} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}}}
5、主服务session文件
SessionUtil.java
public class SessionUtil {public static CurrentUser getCurrentUser() {HttpSession session = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();CurrentUser user = (CurrentUser)session.getAttribute("TaobaoUser");return user;}public static void setCurrentUser(String userName){HttpSession session = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();Collection<GrantedAuthority> authorities = new ArrayList<>();session.setAttribute("TaobaoUser",new CurrentUser(userName, "", authorities));}
}
三、完成配置后
1、直接访问主服务接口,不登录无法访问
2、直接访问自服务,不登录无法访问,(可通过nacos配置用户密码实现登录)
3、主服务通过feign调用子服务接口正常(session已共享)
4、子服务登陆之后,调用主服务理论也可以,需校验下主服务用户侧
相关文章:
微服务Redis-Session共享登录状态
一、背景 随着项目越来越大,需要将多个服务拆分成微服务,使代码看起来不要过于臃肿,庞大。微服务之间通常采取feign交互,为了保证不同微服务之间增加授权校验,需要增加Spring Security登录验证,为了多个服务…...
30道C++ 基础高频题整理(附答案背诵版)
1. C和C有什么区别? C是C语言的超集(我看网上很多文章说这是不对的),这意味着几乎所有的C程序都可以在C编译器中编译和运行。然而,C引入了许多新的概念和特性,使得两种语言在一些关键点上有显著的区别。 …...

【Spark面试】Spark面试题答案
目录 1、spark的有几种部署模式,每种模式特点?(☆☆☆☆☆) 2、Spark为什么比MapReduce块?(☆☆☆☆☆) 3、简单说一下hadoop和spark的shuffle相同和差异?(☆☆☆☆☆…...

Axure的动态面板
目录 动态面板 什么是Auxre动态模板 动态模板的步骤 应用场景 实战案例 轮播图 多功能登录界面 主界面左侧菜单栏 动态面板 什么是Auxre动态模板 动态面板是Axure中的一个重要功能,它允许用户创建可交互的页面,并模拟用户与页面的交互。通过添加元素…...

【STM32】STM32学习笔记-对射式红外传感器计次 旋转编码器计次(12)
00. 目录 文章目录 00. 目录01. NVIC相关函数1.1 NVIC_PriorityGroupConfig函数1.2 NVIC_PriorityGroup类型1.3 NVIC_Init函数1.4 NVIC_InitTypeDef类型 02. 外部中断相关API2.1 GPIO_EXTILineConfig2.2 EXTI_Init2.3 EXTI_GetITStatus2.4 EXTI_ClearITPendingBit2.5 中断回调函…...

后端项目操作数据库-中枢组件Service调用Mapper实现增删改查-实例
接上篇 使用MyBatis配置Mapper实现增删改查 1.Service的基本作用 Service在代码中的的作用是调用Mapper、被Controller调用。是后端项目中非常重要的组件。 用于设计业务流程、业务逻辑,以保障数据的完整性、有效性、安全性。 2. Service使用举例——“添加相册”…...
kafka学习笔记--节点的服役与退役
本文内容来自尚硅谷B站公开教学视频,仅做个人总结、学习、复习使用,任何对此文章的引用,应当说明源出处为尚硅谷,不得用于商业用途。 如有侵权、联系速删 视频教程链接:【尚硅谷】Kafka3.x教程(从入门到调优…...

2023-12-16:用go语言,给定整数数组arr,求删除任一元素后, 新数组中长度为k的子数组累加和的最大值。 来自字节。
2023-12-16:用go语言,给定整数数组arr,求删除任一元素后, 新数组中长度为k的子数组累加和的最大值。 来自字节。 答案2023-12-16: 来自左程云。 灵捷3.5 大体步骤如下: 算法 maxSum1 分析࿱…...

libxls - 编译
文章目录 libxls - 编译概述笔记静态库工程测试控制台exe工程测试备注备注END libxls - 编译 概述 想处理.xls格式的excel文件. 查了一下libxls库可以干这个事. 库地址 https://github.com/libxls/libxls.git 但是这个库的makefile写的有问题, 在mingw和WSL下都编译不了. 好在…...
自建私有git进行项目发布
自建私有git进行博客项目发布 之前尝试过通过建立私有git仓库,来发布自己的hexo静态博客,但是失败了,今天尝试了一下午,算是有了结果。下面记录我的过程。 我的需求: 我有一个服务器,希望在服务器端建一…...
华为HCIP认证H12-821题库上
1、2.OSPF核心知识 (单选题)下面关于0SPF的特殊区域,描述错误的是: A、Totally Stub Area允许ABR发布缺省的三类LSA,不接受五类LSA和细化三类LSA B、NSSA Area和Stub区域的不同在于该区域允许自治系统外部路由的引入,由ABR发布…...

Web安全漏洞分析—文件包含
在当今数字化时代,随着Web应用程序的广泛应用,网络安全问题愈加凸显。其中,文件包含漏洞作为一种常见但危险的安全隐患,为恶意攻击者提供了可乘之机。在这篇博客中,我们将深入探讨文件包含漏洞的本质、攻击手法以及应对…...

C++入门【9-C++循环】
C 循环 有的时候,可能需要多次执行同一块代码。一般情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。 编程语言提供了允许更为复杂的执行路径的多种控制结构。 循环语句允许我们多次…...
Python3 数字(Number) ----20231215
Python3 数字(Number) # Python3 数字(Number)# Python 数字数据类型用于存储数值。 # 数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间。# 以下实例在变量赋值时 Number 对象将被创建: var1 = 1 var2 = 10# 您也可以使用del语句删除一些数…...

PyQt6 QToolBar工具栏控件
锋哥原创的PyQt6视频教程: 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计44条视频,包括:2024版 PyQt6 Python桌面开发 视频教程(无废话版…...

nodejs+vue+微信小程序+python+PHP基于大数据的银行信用卡用户的数仓系统的设计与实现-计算机毕业设计推荐
银行信用卡用户的数仓系统综合网络空间开发设计要求。目的是将银行信用卡用户的数仓系统从传统管理方式转换为在网上管理,完成银行信用卡用户的数仓管理的方便快捷、安全性高、交易规范做了保障,目标明确。银行信用卡用户的数仓系统可以将功能划分为管理…...

EMC RI/CI测试方案助您对抗电磁设备干扰!
方案背景 电磁或射频干扰的敏感性,会给工程师带来重大的风险和安全隐患。尤其是在工业、船用和医疗设备环境。这些环境系统中的控制、导航、监控、通信和警报等关键零部件必须具备电磁抗扰水平,以确保系统始终正常运行。 抗扰系统测试方案一般分为传导…...
【机器学习】数据降维
非负矩阵分解(NMF) sklearn.decomposition.NMF找出两个非负矩阵,即包含所有非负元素(W,H)的矩阵,其乘积近似于非负矩阵x。这种因式分解可用于例如降维、源分离或主题提取。 主成分分析(PCA) sklearn.decomposition.PCA使用数据的奇异值分解…...
vue3路由跳转及传参
1.创建项目及路由 1.1 创建文件时记得勾选上vue-router,没有勾选也没有关系 // vue3安装命令 npm create vuelatest // 以下选项可根据自己所需,进行选择,不懂就翻译 ✔ Project name: … <your-project-name> ✔ Add TypeScript? …...

cesium 自定义贴图,shadertoy移植教程。
1.前言 cesium中提供了一些高级的api,可以自己写一些shader来制作炫酷的效果。 ShaderToy 是一个可以在线编写、测试和分享图形渲染着色器的网站。它提供了一个图形化的编辑器,可以让用户编写基于 WebGL 的 GLSL 着色器代码,并实时预览渲染结…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...

Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...

Mysql故障排插与环境优化
前置知识点 最上层是一些客户端和连接服务,包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念,为通过安全认证接入的客户端提供线程。同样在该层上可…...

Element-Plus:popconfirm与tooltip一起使用不生效?
你们好,我是金金金。 场景 我正在使用Element-plus组件库当中的el-popconfirm和el-tooltip,产品要求是两个需要结合一起使用,也就是鼠标悬浮上去有提示文字,并且点击之后需要出现气泡确认框 代码 <el-popconfirm title"是…...