MaxKey 单点登录认证系统——实现登录后自动跳转及分析思路

Maxkey单点登录系统集成业务系统应用之后,登录界面登录之后不会自动跳转业务系统,需要在首页点击相应应用之后,才能实现跳转业务系统,故以下本人提供解决方法和分析思路。

环境配置

本例使用的是CAS协议实现单点登录

Maxkey 服务端

认证服务器地址端口:9527

前段登录界面地址:http://localhost:8527/maxkey/#/passport/login

业务系统

server:port: 8989
cas:server-url-prefix: http://localhost:9527/sign/authz/cas/ # 认证地址server-login-url: http://localhost:8527/maxkey/#/passport/login #登录地址client-host-url: http://localhost:8989 #客户端地址# 认证方式,默认casvalidation-type: cas3# CAS拦截的URL地址authentication-url-patterns:- /casTest/user

maxkey配置地址如下:

在这里插入图片描述

CAS 原理简单分析

未登录时流程分析

  1. 访问地址 http://localhost:8989/casTest/user

  2. AbstractTicketValidationFilter 过滤器拦截请求

    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {if (this.preFilter(servletRequest, servletResponse, filterChain)) {HttpServletRequest request = (HttpServletRequest)servletRequest;HttpServletResponse response = (HttpServletResponse)servletResponse;//获取请求中的 ticketString ticket = this.retrieveTicketFromRequest(request);if (CommonUtils.isNotBlank(ticket)) {//...}//无 ticket则放行进入下一个过滤器filterChain.doFilter(request, response);}}
    
  3. AuthenticationFilter 过滤器拦截请求

    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)servletRequest;HttpServletResponse response = (HttpServletResponse)servletResponse;//判断当前请求需不需要拦截if (this.isRequestUrlExcluded(request)) {this.logger.debug("Request is ignored.");filterChain.doFilter(request, response);} else {HttpSession session = request.getSession(false);//重定向之后的请求 session中拿出以上过滤器存入的 assertionAssertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;if (assertion != null) {filterChain.doFilter(request, response);} else {//无 assertion说明之前过滤器无 ticket校验String serviceUrl = this.constructServiceUrl(request, response);String ticket = this.retrieveTicketFromRequest(request);boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {this.logger.debug("no ticket and no assertion found");String modifiedServiceUrl;if (this.gateway) {this.logger.debug("setting gateway attribute in session");modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);} else {modifiedServiceUrl = serviceUrl;}this.logger.debug("Constructed service url: {}", modifiedServiceUrl);String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);this.logger.debug("redirecting to \"{}\"", urlToRedirectTo);//重定向到配置的登录见面this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);} else {filterChain.doFilter(request, response);}}}}
    

登录后流程分析

  1. 访问地址 http://localhost:8989/casTest/user

  2. AbstractTicketValidationFilter 过滤器拦截请求

    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {if (this.preFilter(servletRequest, servletResponse, filterChain)) {HttpServletRequest request = (HttpServletRequest)servletRequest;HttpServletResponse response = (HttpServletResponse)servletResponse;//获取请求中的ticketString ticket = this.retrieveTicketFromRequest(request);if (CommonUtils.isNotBlank(ticket)) {this.logger.debug("Attempting to validate ticket: {}", ticket);try {//有ticket时校验并返回 assertion Assertion assertion = this.ticketValidator.validate(ticket, this.constructServiceUrl(request, response));this.logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());request.setAttribute("_const_cas_assertion_", assertion);if (this.useSession) {//session中存入 assertionrequest.getSession().setAttribute("_const_cas_assertion_", assertion);}this.onSuccessfulValidation(request, response, assertion);if (this.redirectAfterValidation) {this.logger.debug("Redirecting after successful ticket validation.");//请求之后加上jsessionid并重定向请求response.sendRedirect(this.constructServiceUrl(request, response));return;}} catch (TicketValidationException var8) {this.logger.debug(var8.getMessage(), var8);this.onFailedValidation(request, response);if (this.exceptionOnValidationFailure) {throw new ServletException(var8);}response.sendError(403, var8.getMessage());return;}}filterChain.doFilter(request, response);}}
    
  3. AuthenticationFilter 过滤器拦截请求

    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest)servletRequest;HttpServletResponse response = (HttpServletResponse)servletResponse;//判断当前请求需不需要拦截if (this.isRequestUrlExcluded(request)) {this.logger.debug("Request is ignored.");filterChain.doFilter(request, response);} else {HttpSession session = request.getSession(false);//重定向之后的请求 session中拿出以上过滤器存入的 assertionAssertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;if (assertion != null) {//如果存在 assertion说明上个过滤器校验ticket通过,请求放行filterChain.doFilter(request, response);} else {//...}}}
    

存在问题

登录之后如果不先点击一次首页应用跳转,而是直接地址栏输入 http://localhost:8989/casTest/user 跳转,还是会跳转到登录界面,无法实现登录之后直接跳转应用业务界面

解决思路和步骤

  1. 通过以上 CAS原理分析在以下过滤器位置打个断点,发现 ticket为空

    在这里插入图片描述

  2. 而如果通过点击首页应用进行第一次跳转请求是会携带 ticket的

    在这里插入图片描述

  3. 说明问题就出在 ticket,所以就得搞清楚ticket是在什么时候设置的

  4. 通过截图发现点击应用时会调用跳转以下地址

    在这里插入图片描述

  5. 但是该地址通过项目搜索栏搜索却搜不到,所以想别的方法

  6. 接着想到点击应用就可以跳转到相应的应用业务系统,而maxkey和应用的唯一关联的地方就是以下设置的回调地址,说明这个回调地址肯定在服务器中哪个地方有使用来进行跳转

    在这里插入图片描述

  7. 通过查看源码发现这回调地址放在一个 callbackUrl的变量里

    在这里插入图片描述

  8. 接着我们查找整个项目发现 callbackUrl会频繁出现在 CasAuthorizeEndpoint.java 类里
    在这里插入图片描述

  9. 点进去发现以下接口,即 /authz/cas/应用id 请求接口,该接口会获取应用 callbackUrl并重定向到 /authz/cas/granting 请求地址,这就与以上的接口对应上了,说明我们分析我找对位置了

    @Operation(summary = "CAS页面跳转应用ID认证接口", description = "传递参数应用ID",method="GET")
    @GetMapping(CasConstants.ENDPOINT.ENDPOINT_BASE + "/{id}") //ENDPOINT_BASE:authz/cas
    public ModelAndView authorize(  @PathVariable("id") String id,HttpServletRequest request,HttpServletResponse response){AppsCasDetails casDetails = casDetailsService.getAppDetails(id , true);return buildCasModelAndView(request,response,casDetails,casDetails == null ? id : casDetails.getCallbackUrl());
    }private  ModelAndView buildCasModelAndView( HttpServletRequest request,HttpServletResponse response,AppsCasDetails casDetails,String casService){//...省略//ENDPOINT_SERVICE_TICKET_GRANTING:"authz/cas/granting"ModelAndView redirect = WebContext.redirect(CasConstants.ENDPOINT.ENDPOINT_SERVICE_TICKET_GRANTING);return redirect;}
    
  10. 接着发现该类还有一个接口,这个接口就是 /authz/cas/granting 接口,该接口进行的操作就是获取 ticket并且设置 ticket,那么我们登录完成之后只要也跟这部分功能一样有获取 ticket并设置的操作就能实现自动跳转

    @RequestMapping(CasConstants.ENDPOINT.ENDPOINT_SERVICE_TICKET_GRANTING) // /authz/cas/granting
    public ModelAndView grantingTicket( Principal principal,HttpServletRequest request,HttpServletResponse response){ModelAndView modelAndView = new ModelAndView("authorize/cas_sso_submint");AppsCasDetails casDetails = (AppsCasDetails)WebContext.getAttribute(CasConstants.PARAMETER.ENDPOINT_CAS_DETAILS);ServiceTicketImpl serviceTicket = new ServiceTicketImpl(AuthorizationUtils.getAuthentication(),casDetails);_logger.trace("CAS start create ticket ... ");//获取 ticketString ticket = ticketServices.createTicket(serviceTicket,casDetails.getExpires());_logger.trace("CAS ticket {} created . " , ticket);StringBuffer callbackUrl = new StringBuffer(casDetails.getCallbackUrl());if(casDetails.getCallbackUrl().indexOf("?")==-1) {callbackUrl.append("?");}if(callbackUrl.indexOf("&") != -1 ||callbackUrl.indexOf("=") != -1) {callbackUrl.append("&");}//append ticket 设置ticketcallbackUrl.append(CasConstants.PARAMETER.TICKET).append("=").append(ticket);callbackUrl.append("&");//append servicecallbackUrl.append(CasConstants.PARAMETER.SERVICE).append("=").append(casDetails.getService());//...//重定向到应用业务系统_logger.debug("redirect to CAS Client URL {}" , callbackUrl);modelAndView.addObject("callbackUrl", callbackUrl.toString());return modelAndView;
    }
    
  11. 在该类中有以下两个接口,点击应用时通过第二个接口来生成 ticket并设置的,而第一个接口为 /authz/cas/login?service=xxx 来实现的,那是不是我们在登录完成之后请求该接口就可实现自动转发

    @Operation(summary = "CAS页面跳转service认证接口", description = "传递参数service",method="GET")
    @GetMapping(CasConstants.ENDPOINT.ENDPOINT_LOGIN)
    public ModelAndView casLogin(@RequestParam(value=CasConstants.PARAMETER.SERVICE,required=false) String casService,HttpServletRequest request,HttpServletResponse response){AppsCasDetails  casDetails = casDetailsService.getAppDetails(casService , true);return buildCasModelAndView(request,response,casDetails,casService);
    }@Operation(summary = "CAS页面跳转应用ID认证接口", description = "传递参数应用ID",method="GET")
    @GetMapping(CasConstants.ENDPOINT.ENDPOINT_BASE + "/{id}")
    public ModelAndView authorize(  @PathVariable("id") String id,HttpServletRequest request,HttpServletResponse response){AppsCasDetails casDetails = casDetailsService.getAppDetails(id , true);return buildCasModelAndView(request,response,casDetails,casDetails == null ? id : casDetails.getCallbackUrl());
    }
    
  12. 根据以上思路我们在登录完成之后添加以上请求地址测试一下

    navigate(authJwt: any) {this.startupService.load().subscribe(() => {let url = this.tokenService.referrer!.url || '/';if (url.includes('/passport')) {url = '/';}if (localStorage.getItem(CONSTS.REDIRECT_URI) != null) {this.redirect_uri = `${localStorage.getItem(CONSTS.REDIRECT_URI)}`;localStorage.removeItem(CONSTS.REDIRECT_URI);}let service = this.getService('service');//添加请求地址this.redirect_uri = 'http://localhost:9527/sign/authz/cas/login?service=http://localhost:8989/casTest/user';if (this.redirect_uri != '') {console.log(`redirect_uri ${this.redirect_uri}`);location.href = this.redirect_uri;}this.router.navigateByUrl(url);});}
    
  13. 发现登录之后实现了跳转故我们就可以在以上方法中添加 this.redirect_uri 来实现登录后自动跳转,至此分析结束

  14. 因为访问业务系统时未登录会跳转回登录界面,跳转地址为:

    http://localhost:8527/maxkey/#/passport/login?service=http:%2F%2Flocalhost:8989%2FcasTest2%2Fuser2

    我们可以通过获取地址栏的serive来拼接到 this.redirect_uri后,这样就可以兼容多应用了,记得把 %2F 转换为 /

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

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

相关文章

Golang的API项目快速开始

开启一个简单的API服务。 golang的教程网上一大堆,官网也有非常详细的教程,这里不在赘述这些基础语法教程,我们意在快速进入项目开发阶段。 golang好用语法教程传送门: m.runoob.com/go/ 编写第一个API 前提:按照上一…

麒麟操作系统缓存rpm包,制作离线yum源

缓存rpm包,以make为例 mkdir -p /data/yum yumdownloader --resolve --destdir/data/yum make制作离线yum包 yum install createrepo -y cd /data/yum createrepo .写yum配置文件/etc/yum.repos.d/local.repo [local-repo] namelocal-repo baseurlfile:///data/…

python_selenium零基础爬虫学习案例_知网文献信息

案例最终效果说明: 去做这个案例的话是因为看到那个博主的分享,最后通过努力,我基本实现了进行主题、关键词、更新时间的三个筛选条件去获取数据,并且遍历数据将其导出到一个CSV文件中,代码是很简单的,没有…

JUC之CompletableFuture

Future接口理论 Future接口定义了异步任务执行的一些方法&#xff0c;包括异步任务执行结果&#xff0c;异步任务执行是否中断&#xff0c;异步任务是否完毕等。 Future接口常用实现类FutureTask异步任务 FutureTask<String> futureTask new FutureTask<String>…

debian12部署Gitea服务之二——部署git-lfs

Debian安装gitlfs: 先更新下软件包版本 sudo apt update 安装 sudo apt install git-lfs 验证是否安装成功 git lfs version cd到Gitea仓库目录下 cd /mnt/HuHDD/Git/Gitea/Repo/hu/testrepo.git 执行lfs的初始化命令 git lfs install客户机Windows端在官网下载并安装Git-Lfs 再…

Elasticsearch windows开箱即用【记录】

一、准备工作 安装ES之前要在本机安装好JDK&#xff0c;对应的兼容性见官网链接&#xff1a;https://www.elastic.co/cn/support/matrix ES官网链接&#xff1a;https://www.elastic.co/cn/, 我本机安装的是JDK8&#xff0c;测试使用的是7.3.0版本的ES和Kibana。 1、首先去…

iOS Universal Links(通用链接)详细教程

一&#xff1a;Universal Links是用来做什么的&#xff1f; iOS9.0推出的用于应用之间跳转的一种机&#xff0c; 通过一个https的链接启动app。如果手机有安装需要启动的app&#xff0c;可实现无缝跳转。如果没有安装&#xff0c;会打开网页。 实现场景&#xff1a;微信链接无…

Linux中快速搭建RocketMQ测试环境

必要的文件下载 为什么选择RocketMQ | RocketMQ x86_64位JDK下载0jdk/8u391-b13 rocketmq二进制包下载-rocketmq-all-5.1.4-bin-release.zip 编译好的直接可用的dashboard【rocketmq-dashboard-1.0.0.jar】请在文章顶部下载 dashboard配套的配置文件【application.propert…

【2024系统架构设计】 系统架构设计师第二版-通信系统架构设计理论与实践

目录 一 通信系统网络架构 二 网络构建的关键技术 三 网络构建和设计方法 四 案例分析 注:本节内容可作为知识储备,做一个基本的了解即可。

qt-C++笔记之QProcess

qt-C笔记之QProcess code review! 文章目录 qt-C笔记之QProcess零.示例&#xff1a;QProcess来执行不带参数的系统命令ls并打印出结果一.示例&#xff1a;QProcess来执行系统命令ls -l命令并打印出结果说明 二.示例&#xff1a;QProcess来执行系统命令ls -l命令并打印出结果&…

Android14实战:打破音频默认重采样的限制(五十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

spark中Rdd依赖和SparkSQL介绍--学习笔记

1&#xff0c;RDD的依赖 1.1概念 rdd的特性之一 相邻rdd之间存在依赖关系&#xff08;因果关系&#xff09; 窄依赖 每个父RDD的一个Partition最多被子RDD的一个Partition所使用 父rdd和子rdd的分区是一对一&#xff08;多对一&#xff09; 触发窄依赖的算子 map()&…

Spring Boot - Application Events 同步 VS 异步 发布订阅事件实战

文章目录 PreCode基础工程启动类切入口事件 发布事件同步 Listener异步Listener增加EnableAsync增加 Async 测试 Pre Spring Boot - Application Events 的发布顺序_ApplicationStartingEvent Spring Boot - Application Events 的发布顺序_ApplicationEnvironmentPreparedEv…

国内镜像:极速下载编译WebRTC源码(For Android/Linux/IOS)(二十四)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

LeetCode 232.用栈实现队列(详解) (๑•̌.•๑)

题目描述&#xff1a; 解题思路&#xff1a; 创建两个栈&#xff0c;一个用于入数据&#xff0c;一个用于出数据。分别是pushST和popST; 1.如果是入数据就直接入进pushST 2.如果是出数据&#xff0c;先检查popST中有无数据&#xff0c;如果有数据&#xff0c;就直接出。如果没…

2024年中职网络安全——Windows操作系统渗透测试(Server2105)

Windows操作系统渗透测试 任务环境说明&#xff1a; 服务器场景&#xff1a;Server2105服务器场景操作系统&#xff1a;Windows&#xff08;版本不详&#xff09;&#xff08;封闭靶机&#xff09;需要环境加Q 目录 1.通过本地PC中渗透测试平台Kali对服务器场景进行系统服务…

ubuntu安装mysql(tar.xz)

0:本机Ubuntu的版本为 腾讯云 18.04 1&#xff1a;下载地址 MySQL &#xff1a;&#xff1a; 下载 MySQL 社区服务器 2&#xff1a;上传文件到服务器 3:解压 sudo sumv mysql-8.2.0-linux-glibc2.17-x86_64-minimal.tar.xz /usrtar -xvf mysql-8.2.0-linux-glibc2.17-x86_6…

逆变器3前级推免(高频变压器)

一节电池标压是在2.8V—4.2V之间&#xff0c;所以24V电压需要大概七节电池串联。七节电池电压大概在19.6V—29.4V之间。 从24V的电池逆变到到220V需要升压的过程。那么我们具体需要升压到多少&#xff1f; 市电AC220V是有效值电压&#xff0c;峰值电压是220V*1.414311V 如果…

Ubuntu下AI4Green开源ELN服务的简单部署

主部署程序&#xff1a;AI4Green 配置参考这篇文档&#xff1a;AI4Green开源ELN&#xff08;电子实验记录本&#xff09;-CSDN博客 流量转发和负载均衡&#xff1a;使用Nginx 配置参考这篇文档&#xff1a;Nginx负载均衡-CSDN博客 SSL配置部分参考这篇文档&#xff1a; 设置…

Android Lint的使用

代码检查方式一&#xff1a; Android Studio使用Lint进行代码检查 找到Analyze目录下的Inspect Code检查代码选项点击然后弹出下面这个框框&#xff0c;在这个列表选项中我们可以选择Inspect Code的范围&#xff0c;点击OK 待分析完毕后&#xff0c;我们可以在Inspection栏目中…