CAS方式实现单点登录SSO

1. CAS介绍

CAS(Central Authentication Service)中心认证服务
下面这张图来自官网,清晰简单的介绍了CAS的继续交互过程
在这里插入图片描述

2. CAS具体实现

首先需要分别搭建CAS-server和CAS-client服务,
这两个服务分别在2台机器上,官方地址如下:

https://github.com/apereo/java-cas-client

2.1 搭建CAS-server

这一步就不详细阐述了,许多公司内部都已经搭建好了CAS server,我们只需要把我们的域名注册到CAS server即可。

2. 搭建CAS-client

在我们自己的项目中,我们首先需要导入依赖

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

根据这个库的实现,我们还需要写2个Filter,分别为Filter1_CasAuthenticationFilterFilter2_CasTicketValidationFilter
具体实现如下:
Filter1_CasAuthenticationFilter

package com.vip.data.unific.server.config;import lombok.extern.slf4j.Slf4j;
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;@WebFilter(urlPatterns = "/*")
@Slf4j
public class Filter1_CasAuthenticationFilter implements Filter {private AuthenticationFilter authentication;@Autowiredprivate CasProperties casProperties;public Filter1_CasAuthenticationFilter() {super();}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {this.authentication = new AuthenticationFilter();this.authentication.setIgnoreInitConfiguration(true);this.authentication.setServerName(casProperties.getServerName());this.authentication.setCasServerLoginUrl(casProperties.getCasUrlPrefix() + "/login");authentication.init(filterConfig);}@Overridepublic void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;if (Boolean.TRUE.equals(casProperties.getSkipFilter())) {chain.doFilter(request, response);return ;}// 不需要 CAS 单点登录的页面直接跳过if (Arrays.stream(casProperties.getIgnorePaths()).filter(p -> request.getRequestURI().matches(p)).count() > 0) {
//            log.info(LogMsgKit.of("doFilter").p("uri", request.getRequestURI()).end("跳过cas认证"));chain.doFilter(request, response);return;}this.authentication.doFilter(request, response, chain);}@Overridepublic void destroy() {this.authentication.destroy();}
}

Filter2_CasTicketValidationFilter如下

package com.vip.data.unific.server.config;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;@WebFilter(urlPatterns = "/*")
@Slf4j
public class Filter2_CasTicketValidationFilter implements Filter  {private static String casServerUrlPrefix = "casServerUrlPrefix";private static String serverName = "serverName";private static String encoding = "encoding";private final TicketValidationWrapper ticketValidation;@Autowiredprivate CasProperties casProperties;public Filter2_CasTicketValidationFilter() {super();this.ticketValidation = new TicketValidationWrapper();}@Overridepublic void init(final FilterConfig filterConfig) throws ServletException {ticketValidation.init(new FilterConfig() {@Overridepublic String getFilterName() {return filterConfig.getFilterName();}@Overridepublic ServletContext getServletContext() {return filterConfig.getServletContext();}@Overridepublic String getInitParameter(String name) {String value = null;if (casServerUrlPrefix.equals(name)) {value = casProperties.getCasUrlPrefix();} else if (serverName.equals(name)) {value = casProperties.getServerName();} else if(encoding.equals(name)){value = casProperties.getEncoding();}if (value == null) {value = filterConfig.getInitParameter(name);}return value;}@Overridepublic Enumeration<String> getInitParameterNames() {Enumeration<String> name = filterConfig.getInitParameterNames();Set<String> set = new HashSet<>();while (name.hasMoreElements()) {set.add(name.nextElement());}set.add(casServerUrlPrefix);set.add(serverName);set.add(encoding);final Iterator<String> iterator = set.iterator();set = null;return new Enumeration<String>() {@Overridepublic boolean hasMoreElements() {return iterator.hasNext();}@Overridepublic String nextElement() {return iterator.next();}};}});ticketValidation.setRedirectAfterValidation(false);//取消重定向,自定义重定向}@Overridepublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) resp;if (Boolean.TRUE.equals(casProperties.getSkipFilter())) {chain.doFilter(request, response);return ;}// 不需要 CAS 单点登录的页面直接跳过if (Arrays.stream(casProperties.getIgnorePaths()).filter(p -> request.getRequestURI().matches(p)).count() > 0) {
//            log.info(LogMsgKit.of("doFilter").p("uri", request.getRequestURI()).end("跳过cas认证"));chain.doFilter(request, response);return;}ticketValidation.doFilter(request, response, chain);}@Overridepublic void destroy() {ticketValidation.destroy();}
}

这2个Filter基本就是固定写法,顺序最好不要交换(虽然交换了也能执行成功),

3、常见问题讨论

常见问题

如果我有多台服务器,如何实现分布式session共享?

单点登录其本质就是分布式session共享的一种解决方案,也就是集中管理session,所以单点登录已经解决了session共享问题

用户访问/api/xxx路径,登录成功后跳转到/api/aaa路径,如何修改重定向路径?

修改重定向有2个方法,一个是修改response如下,但是cas-client中是直接response.sendRedirect();所以这种方法不管用

response.setHeader(“Location”,“/index”);
response.setStatus(302);
第二种方法,继承Cas20ProxyReceivingTicketValidationFilter,然后重写onSuccessfulValidation方法,自己定义重定向地址

public class TicketValidationWrapper extends Cas20ProxyReceivingTicketValidationFilter {//    @Value("${cas.redirectURL}") //filter启动先于spring bean初始化public static final String redirectURL="/";@Overrideprotected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response,final Assertion assertion) {try {response.sendRedirect(redirectURL);} catch (IOException e) {throw new RuntimeException(e);}}
}

为什么需要2个Filter,能写在一起吗?

2个Filter负责的职责不同,理论上可以写在一起,但是分开写逻辑更加清楚

这两个Filter的顺序能交换吗?

通过实验测试,发现2个Filter交换顺序并不会影响登录,用户体验结果是一样,但是F2放在F1前面的话会多走1此Filter,建议Filter1 auth,Filter2 ticket

Filter1_CasAuthenticationFilter
这个主要用来判断用户是否登录,没有登录则重定向到登录界面进行登录,登录完成继续重定向到原始路径

Filter2_CasTicketValidationFilter
这个主要用来验证是否有ST-ticket(ticket是一次性使用的)

session到底存储在哪?

登录成功后,CAS-server有一个session,当用户请求的cookie中携带TGT-xxx访问CAS-server时,可以得到一个ST-ticket

自己的app中也会存储一个session,当用户请求的cookie中携带jsessionid时,则判断为登录成功

也就是CAS-server和自己的app都存储一份session,这两个session是不一样的

编码时可能出现的问题?

注册Filter时,使用2个注解即可,否则会多次注册Filer,导致一个请求执行多次Filter

@ServletComponentScan 加在springboot启动main函数上
@WebFilter(urlPatterns = “/*”) 加在Filter上

Filter上不用加@Component,加了可能会报错或者filter多次注册
Filer执行多次原因还有可能是浏览器默认请求了favicon.ico这个文件,可能检查网络请求中是否有

参考链接:https://blog.csdn.net/chaijunkun/article/details/7646338

在Filter进行注入时要注意,无法使用@Value注入,因为Filter启动时,springbean还没初始化?(可能)

如何控制Filter执行顺序

@order注解不管用,默认是按照类名,所以建议以Filer1xxx,Filter2xxx命名

参考链接:https://www.cnblogs.com/tfgzs/p/4571137.html

下面是一些调研

分布式session解决方案
方案1:Tomcat集群Session全局复制(集群内每个tomcat的session完全同步)【会影响集群的性能】

方案2:根据请求的IP进行Hash映射到对应的机器上【如果服务器宕机了,会丢失Session的数据,实现最简单】

方案3:引入中间件Redis,把Session数据放在Redis中,已经实现的框架有Spring session 使用Spring Session和Redis解决分布式Session共享【有一定的侵入性,实现难度中等,】

方案4:JWT方式,user信息保存在token中,每次请求都携带token【可能存在安全问题,网络开销大一点】

(单点登录其实就属于方案1和3的结合,CAS-server保存登录状态,每个app中也保存的单独的session)

共同点(单点登录的核心)
问题:单点登录的问题是session是各个系统所独自拥有的,各个系统不知道用户是否登录,无法共享用户的登录状态,
目标/切入点:目标/切入点是 “一定要让所有的系统就都可以知道现在用户登录没有”,只要能够实现这个目标/切入点,就可以作为方案,所以 Tomcat集群Session全局复制、请求的IP一直会访问同一个服务器、引入中间件Redis 都是方案。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构与算法实验(黑龙江大学)

实验一 顺序存储的线性表&#xff08;2 学时&#xff09; 一、实验目的 1 、掌握线性表的逻辑结构特征。 2、熟练掌握线性表的顺序存储结构的描述方法。 3 、熟练掌握顺序表上各种基本操作的实现。 二、实验内容 1 、设线性表的数据元素都为整数&#xff0c;存放在顺序表…

orvibo的Mini网关VS20ZW玩法

概述 闲鱼淘来一个2016年生产的网关,此网关的型号:VS20ZW。 已经不能用APP入网了,没事拆来玩玩。 此设备已经被淘汰,很多新的zigbee产品不再支持入网。 官网设备的简介: ZigBee Mini网关,智能家居网关,智能家居主机|ORVIBO欧瑞博智能网关 设备概貌: 主要器件: …

你了解Postman 变量吗?

变量是在Postman工具中使用的一种特殊功能&#xff0c;用于存储和管理动态数据。它们可以用于在请求的不同部分、环境或集合之间共享和重复使用值。 Postman变量有以下几种类型&#xff1a; 1、环境变量&#xff08;Environment Variables&#xff09;: 环境变量是在Postman…

Java基于B/S架构,包括PC后台管理端、APP移动端、可视化数据大屏的智慧工地源码

智慧工地管理平台充分运用数字化技术&#xff0c;聚焦施工现场岗位一线&#xff0c;依托物联网、互联网、AI等技术&#xff0c;围绕施工现场管理的人、机、料、法、环五大维度&#xff0c;以及施工过程管理的进度、质量、安全三大体系为基础应用&#xff0c;实现全面高效的工程…

Docker基础知识总结

文章目录 1.Docker介绍2.Docker版本3.为什么要使用Docker4.Docker基础组件4.1 镜像&#xff08;Images&#xff09;4.2 容器&#xff08;Container&#xff09;和仓库&#xff08;Repository&#xff09; 5.Docker安装6.Docker run7.Dockerfile8.Docker commit9.镜像发布到镜像…

【20年扬大真题】编写对数组求逆的递归算法

【20年扬大真题】 编写对数组求逆的递归算法 void swap(int* a, int* b) {int tmp *b;*b *a;*a tmp; } void Ni(int arr[],int left,int right) {if (left > right) {return;}swap(&arr[left], &arr[right]);Ni(arr, left 1, right - 1); } int main() {int ar…

Thread的常用方法

一&#xff0c;常用方法 二&#xff0c;案例 父类&#xff1a; package ThreadLianXi;import ThreadLianXi.ZhiLeiA;public class Name {public static void main(String[] args)throws Exception{Thread t1 new ZhiLeiA("1号");//修改名字t1.setName("1号&quo…

csdn最新最全pytest系列——pytest-xdist插件之多进程运行测试用例|| pytest-parallel插件之多线程运行测试用例

pytest之多进程运行测试用例(pytest-xdist) 前言 平常我们功能测试用例非常多时&#xff0c;比如有1千条用例&#xff0c;假设每个用例执行需要1分钟&#xff0c;如果单个测试人员执行需要1000分钟才能跑完当项目非常紧急时&#xff0c;会需要协调多个测试资源来把任务分成两部…

ZYNQ_project:uart(odd,even)

概念&#xff1a; UART&#xff08;Universal Asynchronous Receiver-Transmitter&#xff09;&#xff1a;即通用异步收发器&#xff0c;是一种通用串行数据总线&#xff0c;用于异步通信。一般UART接口常指串口。 UART在发送数据时将并行数据转换成串行数据来传输&#xff…

采用connector-c++ 8.0操作数据库

1.下载最新的Connector https://dev.mysql.com/downloads/connector/cpp/&#xff0c;下载带debug的库。 解压缩到本地&#xff0c;本次使用的是带debug模式的connector库&#xff1a; 注&#xff1a;其中mysqlcppconn与mysqlcppconn8的区别是&#xff1a; 2.在cmakelist…

【2018年数据结构真题】

方法一 给定一个含n(n>1)个整数的数组&#xff0c;请设计一个在时间上尽可能高效的算法&#xff0c;找出数组中未出现的最小正整数。例如&#xff0c;数组{-5&#xff0c;3&#xff0c;2&#xff0c;3}中未出现的最小正整数是1&#xff1b;数组{1&#xff0c;2&#xff0c;…

算法刷题-动态规划2

算法刷题-动态规划2 珠宝的最高价值下降路径最小和 珠宝的最高价值 题目 大佬思路 多开一行使得代码更加的简洁 移动到右侧和下侧 dp[ i ][ j ]有两种情况&#xff1a; 第一种是从上面来的礼物最大价值&#xff1a;dp[ i ][ j ] dp[ i - 1 ][ j ] g[ i ][ j ] 第二种是从左…

常见的8个JMeter压测问题

为什么在JMeter中执行压力测试时&#xff0c;出现连接异常或连接重置错误&#xff1f; 答案&#xff1a;连接异常或连接重置错误通常是由于服务器在处理请求时出现问题引起的。这可能是由于服务器过载、网络故障或配置错误等原因导致的。 解决方法&#xff1a; 确定服务器的…

【高级网络程序设计】Week3-2 Servlet

一、 What are servlets? 1. 定义 &#xff08;1&#xff09;Servlets are Java’s answer to CGI&#xff1a; programs that run on a web server acting as middle layer between HTTP request and databases or other applications.Used for client requests that cann…

【LeetCode刷题】-- 29.两数相除

29.两数相除 思路&#xff1a; class Solution {public int divide(int dividend, int divisor) {//考察被除数为最小值的情况if(dividend Integer.MIN_VALUE){//被除数为最小值&#xff0c;除数是1&#xff0c;返回最小值if(divisor 1){return Integer.MIN_VALUE;}//除数是-…

羊大师提示,羊奶都有哪些惊人功效?

羊奶不仅是一种美味的健康饮品&#xff0c;在近年来备受瞩目的的健康圈子里&#xff0c;羊奶还被赋予了更多的功效&#xff0c;成为一种备受推崇的保健品。羊奶不但富含营养&#xff0c;而且还有着非常多的益处&#xff0c;它能够用来美容、保健&#xff0c;甚至还可以治疗某些…

C语言基本算法之选择排序

目录 概要&#xff1a; 代码如下 运行结果如下 概要&#xff1a; 它和冒泡排序一样&#xff0c;都是把数组元素按顺序排列&#xff0c;但是方法不同&#xff0c;冒泡排序是把较小值一个一个往后面移&#xff0c;选择排序则是直接找出最小值&#xff0c;可以这个说&#xff…

【OpenCV实现图像:OpenCV利用Python创作热力图】

文章目录 概要读取图像图像灰度化**像素化效果**小结 概要 热力图是一种强大的统计图表&#xff0c;通过对数据进行色彩映射&#xff0c;直观展示了数据分布的热度和密度。在绘制热力图时&#xff0c;关键在于指定颜色映射的规则&#xff0c;这决定了图中不同数值的呈现方式。…

【React-Router】路由导航

1. 概念 路由系统中的多个路由之间需要进行路由跳转&#xff0c;并且在跳转的同时有可能需要传递参数进行通信。 2. 声明式导航 // /page/Login/index.jsimport { Link } from react-router-dom const Login () > {return <div>登录页{/* 解析成 a 链接 */}<Li…