当前位置: 首页 > news >正文

若依微服务集成CAS,实现单点登录

若依(RuoYi)微服务是一款基于Spring Cloud Alibaba开发的企业级微服务框架,采用前后端分离方式,使用了常用的微服务组件,如Feign、Nacos、Sentinel、Seata等,提供了丰富的微服务治理功能,如服务注册、发现、路由、负载均衡、熔断降级、限流等。借助若依微服务框架可以让我们快速构建起一个高效、可靠、可扩展的分布式系统,提高了开发效率和系统性能。

借助Spring Cloud Alibaba,若依微服务框架完成了后端的微服务改造,但是前端仍是一个单体服务,随着业务的增长,前端必然变的庞大、臃肿,不可避免的需要对前端进行拆分,然而前端拆分后面临的一个问题是登录信息如何同步?登录信息是以token存储在cookie中的,无法共享。

为了解决前端登录信息同步的问题,这里考虑通过集成CAS的方式,实现统一认证。

研究了若依微服务的认证功能后发现,若依微服务的认证并未使用Spring Security,仅仅使用了Spring Security的加密功能,无法直接套用若依分离版集成CAS的方式。所以通过结合分离版集成思路及CAS官方集成方法完成若依微服务集成CAS。集成方法如下:

1、添加CAS依赖

在auth模块中添加cas依赖:

<!-- Cas Core -->
<dependency><groupId>org.jasig.cas.client</groupId><artifactId>cas-client-core</artifactId><version>3.6.4</version>
</dependency>

2、修改配置文件

在nacos中修改ruoyi-auth-dev.yml(或直接修改auth模块下bootstrap.yml文件),增加cas配置:

cas:enable: trueserver:url:prefix: http://127.0.0.1:8888/caslogin: http://127.0.0.1:8888/cas/loginclient:url: http://127.0.0.1:8080/auth

3、修改Constants.java

修改common-core模块下
com.ruoyi.common.core.constant.Constants.java,增加CAS认证成功标识:

/*** CAS登录成功后的后台标识**/
public static final String CAS_TOKEN = "cas_token";/*** CAS登录成功后的前台Cookie的Key**/
public static final String WEB_TOKEN_KEY = "Cloud-Token";/*** CAS登录成功后的前台Cookie的Expires-In**/
public static final String WEB_TOKEN_EXPIRES = "Cloud-Expires-In";

4、添加CasProperties.java

在auth模块中添加CasProperties.java文件,获取CAS配置信息:

package com.ruoyi.auth.cas.config.properties;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;/*** @author LuoFei* @className: CasProperty* @projectName RuoYi-Cloud-master* @description: cas配置参数* @date 2022/7/21 10:11**/
@Configuration
@RefreshScope
public class CasProperties {@Value("${cas.enable}")private Boolean enabled;@Value("${cas.server.url.prefix}")private String casServerUrlPrefix;@Value("${cas.server.url.login}")private String casServerLoginUrl;@Value("${cas.client.url}")private String serverName;public Boolean getEnabled() {return enabled;}public void setEnabled(Boolean enabled) {this.enabled = enabled;}public String getCasServerUrlPrefix() {return casServerUrlPrefix;}public void setCasServerUrlPrefix(String casServerUrlPrefix) {this.casServerUrlPrefix = casServerUrlPrefix;}public String getCasServerLoginUrl() {return casServerLoginUrl;}public void setCasServerLoginUrl(String casServerLoginUrl) {this.casServerLoginUrl = casServerLoginUrl;}public String getServerName() {return serverName;}public void setServerName(String serverName) {this.serverName = serverName;}
}

5、添加NoCasFilter.java

在auth模块中添加NoCasFilter.java文件,在未启用CAS时直接放行:

package com.ruoyi.auth.cas.filter;import javax.servlet.*;
import java.io.IOException;/*** @author LuoFei* @className: NoCasFilter* @projectName RuoYi-Cloud-master* @description: 单点登录停用辅助过滤器* @date 2022/7/21 11:19**/
public final class NoCasFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {filterChain.doFilter(request, response);}
}

6、 添加
CustomSessionMappingStorage.java

在auth模块中添加
CustomSessionMappingStorage.java文件,实现单点登出:

package com.ruoyi.auth.cas.storage;import com.ruoyi.common.core.constant.Constants;
import com.ruoyi.common.security.service.TokenService;
import org.apache.catalina.session.StandardSessionFacade;
import org.jasig.cas.client.session.SessionMappingStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;/*** @author LuoFei* @className: CustomSessionMappingStorage* @projectName RuoYi-Vue-master* @description: 单点登录-前后端分离-单点登出删除token* @date 2022/4/28 12:56**/
@Component
public class CustomSessionMappingStorage implements SessionMappingStorage {private final Map<String, HttpSession> MANAGED_SESSIONS = new HashMap();private final Map<String, String> ID_TO_SESSION_KEY_MAPPING = new HashMap();private final Logger logger = LoggerFactory.getLogger(this.getClass());@Autowiredprivate TokenService tokenService;public CustomSessionMappingStorage() {}@Overridepublic synchronized void addSessionById(String mappingId, HttpSession session) {this.ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId);this.MANAGED_SESSIONS.put(mappingId, session);}@Overridepublic synchronized void removeBySessionById(String sessionId) {this.logger.debug("Attempting to remove Session=[{}]", sessionId);String key = (String)this.ID_TO_SESSION_KEY_MAPPING.get(sessionId);if (this.logger.isDebugEnabled()) {if (key != null) {this.logger.debug("Found mapping for session.  Session Removed.");} else {this.logger.debug("No mapping for session found.  Ignoring.");}}this.MANAGED_SESSIONS.remove(key);this.ID_TO_SESSION_KEY_MAPPING.remove(sessionId);}@Overridepublic synchronized HttpSession removeSessionByMappingId(String mappingId) {StandardSessionFacade session = (StandardSessionFacade) this.MANAGED_SESSIONS.get(mappingId);if (session != null) {this.removeBySessionById(session.getId());try {String token = (String) session.getAttribute(Constants.CAS_TOKEN);tokenService.delLoginUser(token);} catch (IllegalStateException e) {this.logger.error("已成功登出");}}return session;}
}

7、增加casLogin方法

在auth模块的TokenController.java文件中添加casLogin方法,实现登录功能:

/*** 单点登录成功创建token* @param request* @param response* @throws IOException* @throws ServletException**/
@GetMapping("casLogin")
public void casLogin(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {String username = request.getRemoteUser();HttpSession httpSession = request.getSession();String url = request.getParameter("redirect");LoginUser userInfo = sysLoginService.login(username);Map<String, Object> token = tokenService.createToken(userInfo);Cookie tokenCookie = new Cookie(Constants.WEB_TOKEN_KEY, (String) token.get("access_token"));//必须设置path,否则获取不到cookietokenCookie.setPath("/");response.addCookie(tokenCookie);Cookie expiresCookie = new Cookie(Constants.WEB_TOKEN_EXPIRES, ((Long) token.get("expires_in")).toString());expiresCookie.setPath("/");response.addCookie(expiresCookie);//设置后端认证成功标识httpSession.setAttribute(Constants.CAS_TOKEN, token.get("access_token"));//登录成功后跳转到前端访问页面response.sendRedirect(url);
}

8、添加CasConfig.java文件

在auth模块中添加CasConfig.java文件:

package com.ruoyi.auth.cas.config;import com.ruoyi.auth.cas.config.properties.CasProperties;
import com.ruoyi.auth.cas.filter.NoCasFilter;
import com.ruoyi.auth.cas.storage.CustomSessionMappingStorage;
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author LuoFei* @className: CasConfig* @projectName RuoYi-Cloud-master* @description: TODO* @date 2022/7/19 14:17**/
@Configuration
public class CasConfig {@Autowiredprivate CasProperties casProperties;@Autowiredprivate CustomSessionMappingStorage customSessionMappingStorage;/*** 单点登出过滤器* @return**/@Beanpublic FilterRegistrationBean logoutFilter() {FilterRegistrationBean authenticationFilter = new FilterRegistrationBean<>();SingleSignOutFilter signOutFilter = new SingleSignOutFilter();signOutFilter.setSessionMappingStorage(customSessionMappingStorage);signOutFilter.setIgnoreInitConfiguration(true);authenticationFilter.setFilter(signOutFilter);authenticationFilter.addUrlPatterns("/*");Map<String, String> initParameters = new HashMap<>();initParameters.put("casServerUrlPrefix", casProperties.getCasServerUrlPrefix());authenticationFilter.setInitParameters(initParameters);authenticationFilter.setOrder(1);if (casProperties.getEnabled()) {return authenticationFilter;}return new FilterRegistrationBean<>(new NoCasFilter());}/*** 单点登录认证入口* @return**/@Beanpublic FilterRegistrationBean authenticationFilterRegistrationBean() {FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();authenticationFilter.setFilter(new AuthenticationFilter());Map<String, String> initParameters = new HashMap<>();initParameters.put("casServerLoginUrl", casProperties.getCasServerLoginUrl());initParameters.put("serverName", casProperties.getServerName());authenticationFilter.setInitParameters(initParameters);authenticationFilter.setOrder(2);List<String> urlPatterns = new ArrayList<>();urlPatterns.add("/casLogin");authenticationFilter.setUrlPatterns(urlPatterns);if (casProperties.getEnabled()) {return authenticationFilter;}return new FilterRegistrationBean<>(new NoCasFilter());}/*** 单点登录验证入口* @return**/@Beanpublic FilterRegistrationBean validationFilterRegistrationBean() {FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();authenticationFilter.setFilter(new Cas20ProxyReceivingTicketValidationFilter());Map<String, String> initParameters = new HashMap<>();initParameters.put("casServerUrlPrefix", casProperties.getCasServerUrlPrefix());initParameters.put("serverName", casProperties.getServerName());initParameters.put("encoding", "UTF-8");initParameters.put("useSession", "true");authenticationFilter.setInitParameters(initParameters);authenticationFilter.setOrder(3);List<String> urlPatterns = new ArrayList<>();urlPatterns.add("/*");authenticationFilter.setUrlPatterns(urlPatterns);if (casProperties.getEnabled()) {return authenticationFilter;}return new FilterRegistrationBean<>(new NoCasFilter());}/*** 单点登录获取登录信息* @return**/@Beanpublic FilterRegistrationBean casHttpServletRequestWrapperFilter() {FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();authenticationFilter.setFilter(new HttpServletRequestWrapperFilter());authenticationFilter.setOrder(4);List<String> urlPatterns = new ArrayList<>();urlPatterns.add("/*");authenticationFilter.setUrlPatterns(urlPatterns);if (casProperties.getEnabled()) {return authenticationFilter;}return new FilterRegistrationBean<>(new NoCasFilter());}
}

9、放行casLogin请求

修改common-security模块中的WebMvcConfig.java,放行casLogin请求:

/** 不需要拦截地址 **/
public static final String[] excludeUrls = { "/casLogin", "/login", "/logout", "/refresh", "/register" };

修改nacos中ruoyi-gateway-dev.yml,在网关中放行casLogin请求:

# 不校验白名单ignore:whites:- /auth/casLogin- /auth/getToken- /auth/logout- /auth/login- /auth/register- /auth/updatePassword- /*/v2/api-docs- /csrf

至此,即完成若依微服务后端CAS集成工作。

若依微服务前端集成与分离版相同,请参考若依分离版集成CAS。

相关文章:

若依微服务集成CAS,实现单点登录

若依&#xff08;RuoYi&#xff09;微服务是一款基于Spring Cloud Alibaba开发的企业级微服务框架&#xff0c;采用前后端分离方式&#xff0c;使用了常用的微服务组件&#xff0c;如Feign、Nacos、Sentinel、Seata等&#xff0c;提供了丰富的微服务治理功能&#xff0c;如服务…...

解锁园区交通新模式:园区低速自动驾驶

在当今科技飞速发展的时代&#xff0c;自动驾驶技术成为了备受关注的领域之一。尤其是在园区内部交通管理方面&#xff0c;自动驾驶技术的应用正在日益受到重视。 园区低速自动驾驶的实现需要多个技术领域的协同合作&#xff0c;包括自动驾驶技术、计算机视觉技术、通信技术、物…...

SpringBoot-Hello World

SpringBootWeb快速入门 创建Springboot工程&#xff0c;并勾选web开发相关依赖定义HelloController类&#xff0c;添加方法hello&#xff0c;并添加相关注释运行测试 创建新的SpringBoot项目 几个注意的点&#xff1a; Name&#xff1a;基本上不用管&#xff0c;会根据下面的Ar…...

香港服务器三网直连内地线路什么意思?好用吗?

​  三网直连内地是指香港服务器可以直接连接中国内地的电信、联通和移动三大运营商网络&#xff0c;避免了中间网络干线的支持。这样可以实现直接、快速、稳定的网络访问&#xff0c;提高用户对网络访问的效率&#xff0c;减少网络访问问题和拥堵的现象。 香港服务器直连内地…...

component:()=>import(“@/views/Home.vue“) 报错,ts说没有找到类型声明文件

1 没有写.vue文件的类型声明&#xff0c;要在env.d.ts文件中写.vue的类型声明文件 2 ts.config.josn的incluede字段中&#xff0c;没有把.d.ts文件的路径写对。 如果没写对&#xff0c;就会在项目启动的时候&#xff0c;找不到.d.ts文件。找不到类型声明文件...

为什么hive会出现_HIVE_DEFAULT_PARTITION分区

问题&#xff1a; 为什么hive表中出现_HIVE_DEFAULT_PARTITION分区&#xff1f; 解答&#xff1a; 因为在业务sql中使用的是动态分区&#xff0c;并且hive启用动态分区时&#xff0c;对于指定的分区键如果存在空值时&#xff0c;会对空值部分创建一个默认分区用于存储该部分…...

通讯协议041——全网独有的OPC HDA知识一之聚合(九)首值

本文简单介绍OPC HDA规范的基本概念&#xff0c;更多通信资源请登录网信智汇(wangxinzhihui.com)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化&#xff0c;以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&…...

opencv进阶01-直方图的应用及示例cv2.calcHist()

直方图是什么&#xff1f; 直方图是一种图形表示方法&#xff0c;用于显示数据中各个数值或数值范围的分布情况。它将数据划分为一系列的区间&#xff08;也称为“箱子”或“bin”&#xff09;&#xff0c;然后统计每个区间中数据出现的频次&#xff08;或频率&#xff09;。直…...

网络通信原理TCP的四次断开连接(第四十九课)

FIN:发端完成发送任务标识。用来释放一个连接。FIN=1表明此报文段的发送端的数据已经发送完毕,并要求释放连接。 SEQ:序号字段。 TCP链接中传输的数据流中每个字节都编上一个序号。序号字段的值指的是本报文段所发送的数据的第一个字节的序号。 序列号为X ACK :确认号 。 …...

(二)结构型模式:2、桥接模式(Bridge Pattern)(C++实现示例)

目录 1、桥接模式&#xff08;Bridge Pattern&#xff09;含义 2、桥接模式应用场景 3、桥接模式的UML图学习 4、C实现桥接模式的示例 1、桥接模式&#xff08;Bridge Pattern&#xff09;含义 桥接模式是一种结构型设计模式&#xff0c;它将抽象部分与实现部分分离&#…...

FPGA_学习_16_IP核_ROM

在寻找APD最合适的偏压的过程中&#xff0c;一般会用到厂商提供一条曲线&#xff0c;横坐标是温度的变化&#xff0c;纵坐标表示击穿偏压的变化&#xff0c;但每个产品真正的击穿偏压是有差异的。 为了能够快速的找到当前温度下真实的击穿偏压&#xff0c;我们可以这样做&#…...

机器学习---对数几率回归

1. 逻辑回归 逻辑回归&#xff08;Logistic Regression&#xff09;的模型是一个非线性模型&#xff0c; sigmoid函数&#xff0c;又称逻辑回归函数。但是它本质上又是一个线性回归模型&#xff0c;因为除去sigmoid映射函 数关系&#xff0c;其他的步骤&#xff0c;算法都是…...

网络通信原理IP头部格式(第四十二课)

字段作用解析:1)版本: 指的IP地址的版本 (IPv4 或 IPV6)2)首部长度: 次数据包的首部长度一共是多少,没有加可选项3)优先级与服务类型:表示****数据包是否需要优选传递4)总长度: 表示的是整个数据包的大小,也就****是首部+数据5)标识符、标志、段偏移量:的作用将拆开的…...

Flink多流处理之join(关联)

Flink的API中只提供了join的算子,并没有left join或者right join,这里我们就介绍一下join算子的使用,其实join算子底层调用的就是coGroup,具体原理这里就不过多介绍了,如果感兴趣可以看我前面发布的文章Flink多流操作之coGroup. 数据源➜ ~ nc -lk 1111 101,A 102,B 103,C 10…...

LeetCode Top100 Liked 题单(序号34~51)

​34. Find First and Last Position of Element in Sorted Array ​ 题意&#xff1a;找到非递减序列中目标的开头和结尾 我的思路 用二分法把每一个数字都找到&#xff0c;最后返回首尾两个数 代码 Runtime12 ms Beats 33.23% Memory14 MB Beats 5.16% class Solution {…...

视觉slam十四讲---第一弹三维空间刚体运动

1.旋转矩阵 1.1内积 1.2外积 1.3坐标系间的欧式变换 相机运动是一个刚体运动&#xff0c;它保证了同一个向量在各个坐标系下的长度和夹角都不会 发生变化。这种变换称为欧氏变换。 旋转矩阵&#xff1a;它是一个行列式为 1 的正交矩阵。 旋转矩阵为正交阵&#xff0c;它的逆…...

手把手教你配置Jenkins自动化邮件通知

完成基于Jenkins的持续集成部署后&#xff0c;自动化测试执行后&#xff0c;测试结果需要通知到相关人员&#xff0c;除了钉钉通知外我们还可以通过Email通知到对应负责人&#xff0c;这里记录一下测试结果通过Jenkins邮件通知的配置与部署 01、安装插件 方法1&#xff1a; 进…...

Arcgis连续数据的分类(求不同值域的面积)

问题描述&#xff1a;如果得到的一个连续的影响数值数据&#xff0c;但是我们想求取某一段值域的面积占比&#xff0c;需要进行以下操作&#xff1a; 1.按照数值重分类&#xff0c;将某段数值变成一个类别 2.栅格转矢量&#xff0c;再求取面积...

C++ 函数

函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数&#xff0c;即主函数 main() &#xff0c;所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的&#xff0c;但在逻辑上&#xff0c;划分通常…...

关于如何创建一个windows窗口的exe文件

如何创建一个windows窗口exe文件&#xff0c;具体参照这个博主&#xff1a; http://t.csdn.cn/pfQK5 以下是实现代码&#xff0c;注意用vs打开&#xff1a; #pragma comment( linker, "/subsystem:\"windows\" /entry:\"WinMainCRTStartup\"" …...

re学习(33)攻防世界-secret-galaxy-300(动态调试)

下载压缩包&#xff1a; 下载链接&#xff1a;https://adworld.xctf.org.cn/challenges/list 参考文章&#xff1a;攻防世界逆向高手题之secret-galaxy-300_沐一 林的博客-CSDN博客 发现这只是三个同一类型文件的三个不同版本而已&#xff0c;一个windows32位exe&#xff0…...

springboot工程集成前端编译包,用于uni-app webView工程,解决其需独立部署带来的麻烦,场景如页面->画布->图片->pdf

前端工程 访问方式 http://127.0.0.1:8080/context/frontEnd/index放行 public class SecurityConfig extends WebSecurityConfigurerAdapter { "/frontEnd/**",SysFrontEndController import lombok.extern.slf4j.Slf4j; import nl.basjes.shaded.org.springfram…...

NeuralNLP-NeuralClassifier的使用记录(二),训练预测自己的【中文文本多分类】

NeuralNLP-NeuralClassifier的使用记录&#xff0c;训练预测自己的【中文文本多分类】 数据准备&#xff1a; ​ 与英文的训练预测一致&#xff0c;都使用相同的数据格式&#xff0c;将数据通过代码处理为JSON格式&#xff0c;以下是我使用的一种&#xff0c;不同的原数据情况…...

express学习笔记8 - 文件上传 下载以及预览

一、上传 1、 安装multer (任意选其中一种) yarn add multer --S npm install multer --S 2、新建配置文件(utils/multerConfig) const multer require(multer); const mkdirp require(mkdirp); // const sd require(silly-datetime); const path require(path);con…...

Python系统学习1-9-类(一)

一、类之初印象 1、类就是空表格&#xff0c;将变量&#xff08;列名&#xff09;和函数&#xff08;行为&#xff09;结合起来 2、创建对象&#xff0c;表达具体行 3、创建类就是创建数据的模板 --操作数据时有提示 --还能再组合数据的行为 --结构更加清晰 4、类的内存分配…...

什么是公网、私网、内网、外网?

中午好&#xff0c;我的网工朋友。 最近经常有很多小白朋友在问&#xff0c;公网、私网、内网、外网&#xff0c;这些的概念是啥样的&#xff0c;又该怎么去界定。 关于IP地址&#xff0c;确实没有太明确的区分&#xff0c;其实也不必太过咬文嚼字。 内网、外网就是一个参考…...

一篇文章教会你搭建私人kindle图书馆,并内网穿透实现公网访问

搭建私人kindle图书馆&#xff0c;并内网穿透实现公网访问 在电子书风靡的时期&#xff0c;大部分人都购买了一本电子书&#xff0c;虽然这本电子书更多的时候是被搁置在储物架上吃灰&#xff0c;或者成为盖泡面的神器&#xff0c;但当亚马逊发布消息将放弃电子书在中国的服务…...

好用的安卓手机投屏到mac分享

工具推荐&#xff1a;scrcpy github地址&#xff1a;https://github.com/Genymobile/scrcpy/tree/master mac使用方式 安装环境&#xff0c;打开terminal&#xff0c;执行以下命令&#xff0c;没有brew的先安装brew brew install scrcpy brew install android-platform-too…...

df -h

df -h 命令用于查看磁盘占用的空间 Filesystem&#xff1a;表示该文件系统位于哪个分区&#xff0c;因此该列显示的是设备名称&#xff1b; Used&#xff1a;表示用掉的磁盘空间大小&#xff1b; Available&#xff1a;表示剩余的磁盘空间大小&#xff1b; Use%&#xff1a;磁盘…...

彻底卸载Android Studio

永恒的爱是永远恪守最初的诺言。 在安装Android Studio会有很多问题导致无法正常运行&#xff0c;多次下载AS多次错误后了解到&#xff0c;删除以下四个文件才能彻底卸载Android Studio。 第一个文件&#xff1a;.gradle 路径&#xff1a;C:\Users\yao&#xff08;这里yao是本…...