博主之前一直使用了cas客户端进行用户的单点登录操作,决定进行源码分析来看cas的整个流程,以便以后出现了问题还不知道是什么原因导致的

cas主要的形式就是通过过滤器的形式来实现的,来,贴上示例配置:

     <listener>          <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListenerlistener-class>      listener>        <filter>          <filter-name>SSO Logout Filterfilter-name>          <filter-class>org.jasig.cas.client.session.SingleSignOutFilterfilter-class>      filter>       <filter-mapping>         <filter-name>SSO Logout Filterfilter-name>         <url-pattern>/*url-pattern>     filter-mapping>           <filter>         <filter-name>SSO Authentication Filterfilter-name>         <filter-class>org.jasig.cas.client.authentication.AuthenticationFilterfilter-class>           <init-param>                              <param-name>SSOServerUrlparam-name>               <param-value>http://sso.jxeduyun.com/ssoparam-value>           init-param>           <init-param>                              <param-name>SSOLoginUrlparam-name>               <param-value>http://www.jxeduyun.com/App.ResourceCloud/Src/index.phpparam-value>           init-param>           <init-param>                             <param-name>serverNameparam-name>               <param-value>http://127.0.0.1:9000param-value>           init-param>           <init-param>                              <param-name>needAttributeparam-name>               <param-value>trueparam-value>           init-param>           <init-param>                                             <param-name>excludedURLsparam-name>               <param-value>/site2\.jspparam-value>           init-param>     filter>     <filter-mapping>         <filter-name>SSO Authentication Filterfilter-name>         <url-pattern>/TyrzLogin/*url-pattern>     filter-mapping>         <filter>         <filter-name>SSO Ticket Validation Filterfilter-name>         <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilterfilter-class>         <init-param>                          <param-name>serverNameparam-name>             <param-value>http://127.0.0.1:9000param-value>         init-param>         <init-param>                          <param-name>needAttributeparam-name>             <param-value>trueparam-value>         init-param>         <init-param>                          <param-name>SSOServerUrlPrefixparam-name>             <param-value>http://sso.jxeduyun.com/ssoparam-value>         init-param>     filter>    <filter-mapping>         <filter-name>SSO Ticket Validation Filterfilter-name>         <url-pattern>/*url-pattern>     filter-mapping>

博主用的不是官方的cas的jar包,是第三方要求的又再次封装的jar包,不过就是属性,获取用户信息的逻辑多了点,其他的还是官方的源码,博主懒 的下载官方的jar在进行一步一步的debug看源码了。

基本配置是添加4个过滤器,请求的时候可以进行拦截进行查看,最后一个是jfinal的开发框架,类似spring,不用管

以上是jetty抓到请求时,进行获取过滤的流程,只关注cas的这四个,里面涉及到了缓存过滤器(节点类型存储)

全部进行路径URL匹配完之后,会获取到需要进行执行的过滤器,SSO Logout Filter->SSO Authentication Filter->SSO Ticket Validation Filter->CAS Assertion Thread Local Filter

那我们就来一个一个看看,每个过滤器都做了哪些事。

SSO Logout Filter,从名字上看,应该是个退出的流程操作。来源码附上:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest)servletRequest;        HttpServletResponse response = (HttpServletResponse)servletResponse;        //查看请求中是否带有ticket参数        if (!handler.isTokenRequest(request) && !CommonUtils.isNotBlank(request.getParameter("ticket"))) {            //如果没有的ticket参数,查看是否是退出请求            if (handler.isLogoutRequest(request)) {                if (this.sessionMappingStorage != null && !this.sessionMappingStorage.getClass().equals(HashMapBackedSessionMappingStorage.class)) {                    //是退出请求,直接销毁session,直接return,不会在执行其他过滤器                    handler.destroySession(request, response);                    return;                }            this.log.trace("Ignoring URI " + request.getRequestURI());        } else {            handler.recordSession(request);        }        ///继续执行下一个执行器        filterChain.doFilter(servletRequest, servletResponse);    }

AuthenticationFilter,该过滤器主要做法:

public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        String requestedUrl = ((HttpServletRequest)servletRequest).getServletPath();        boolean isExcludedUrl = false;        //这里会获取到xml中的排除需要过滤的URL配置        if (this.excludedRequestUrlPatterns != null && this.excludedRequestUrlPatterns.length > 0) {            Pattern[] arr$ = this.excludedRequestUrlPatterns;            int len$ = arr$.length;            for(int i$ = 0; i$ < len$; ++i$) {                Pattern p = arr$[i$];                if (isExcludedUrl = p.matcher(requestedUrl).matches()) {                    break;                }            }        }        HttpServletRequest request = (HttpServletRequest)servletRequest;        HttpServletResponse response = (HttpServletResponse)servletResponse;        //如果当前URL是被排除,不需要校验cas单点登录的话,直接跳过当前过滤器,进行下一步        if (this.isIgnoreSSO() && isExcludedUrl) {            filterChain.doFilter(request, response);        } else {            //如果当前不被排除在外,查看白名单URL,也可以直接跳过该过滤器            boolean isWhiteUrl = false;            if (this.whiteRequestUrlPatterns != null && this.whiteRequestUrlPatterns.length > 0) {                Pattern[] arr$ = this.whiteRequestUrlPatterns;                int len$ = arr$.length;                for(int i$ = 0; i$ < len$; ++i$) {                    Pattern p = arr$[i$];                    if (isWhiteUrl = p.matcher(requestedUrl).matches()) {                        break;                    }                }            }            if (isWhiteUrl) {                filterChain.doFilter(request, response);            } else {                //如果都没匹配上,说明该URL是需要进行校验查看的                HttpSession session = request.getSession(false);                //从session中取出改属性值,查看当前session是否已经认证过了。如果认证过了了,可以跳过该过滤器                Assertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;                //第一次请求的时候,改对象一定为null,因为没人登录过                if (assertion != null) {                    filterChain.doFilter(request, response);                } else {                    String serviceUrl = this.constructServiceUrl(request, response);                    String ticket = CommonUtils.safeGetParameter(request, this.getArtifactParameterName());                    //查看是否session中有_const_cas_gateway_该属性值,第一次登录也没有                    boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);                    //如果都没有                    if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {                        String encodedService;                        //查看是否是cas服务器return回调我们的这个接口请求,该属性值在下面,也就是第一次登录的时候,设置的                        if (request.getSession().getAttribute("casreturn") != null) {                            request.getSession().removeAttribute("casreturn");                            if (isExcludedUrl) {                                filterChain.doFilter(request, response);                            } else {                                encodedService = Base64.encodeBase64String(serviceUrl.getBytes());                                encodedService = encodedService.replaceAll("[\\s*\t\n\r]", "");                                if (!this.SSOLoginUrl.startsWith("https://") && !this.SSOLoginUrl.startsWith("http://")) {                                    this.SSOLoginUrl = this.getServerName() + (this.getServerName().endsWith("/") ? "" : "/") + this.SSOLoginUrl;                                }                                //-------------@这里----------------------                                //一直以为是所有校验都没有参数后,在下面才是跳转到登录页,,没想到,直接回调了,并没有让用户去登陆,而是在这里才去调用登录页                                //让用户去登陆。大坑                                response.sendRedirect(CommonUtils.joinUrl(this.SSOLoginUrl, "nextpage=" + encodedService));                            }                        } else {                            //第一次登录的时候是这里,他会将你xml中的cas服务器地址拼接成login登录地址,我们当前请求的URL编码之后,会被cas登录成功后回调使用                            encodedService = this.SSOServerUrl + "/login?service=" + URLEncoder.encode(serviceUrl, "UTF-8") + "&redirect=true";                            //并且设置cas服务器回调标识                            request.getSession().setAttribute("casreturn", true);                            //第一次登录的时候,只能到这里了,因为ticket参数,或则session中_const_cas_assertion_属性都没有,只能去cas服务器请求登录,                            //这里有个坑,,没想到在这里没有直接出现登录页,而是调用cas服务器地址后,直接返回来了,而且会在@那里再去调用登录地址                            response.sendRedirect(encodedService);                            //其他的事情后续就不要再debug了,已经跟我们cas没有啥关系了,博主,debug了半天越看越懵,才发现是服务在做其他的事情,                            // 我们的登录页面早就已经出现了                        }                    } else {                        filterChain.doFilter(request, response);                    }                }            }        }    }

上面的还有一个坑,就是,在用户登录成功后,回调我们的地址,第一次并不会带给我们ticket参数,而且还会走

encodedService = this.SSOServerUrl + "/login?service=" + URLEncoder.encode(serviceUrl, "UTF-8") + "&redirect=true";

这个逻辑,并且附上casreturn属性,然后,cas服务器这回才会把ticket参数返回给我们的接口,剩下的就是下一个过滤器的事情了,慢慢来:

好了,这次有ticket了,我们来看下一个过滤器SSO Ticket Validation Filter

public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        //这里做了点事,是否为代理,博主没用这个,默认代理为null,返回true        if (this.preFilter(servletRequest, servletResponse, filterChain)) {            HttpServletRequest request = (HttpServletRequest)servletRequest;            HttpServletResponse response = (HttpServletResponse)servletResponse;            //获取ticket请求参数            String ticket = CommonUtils.safeGetParameter(request, this.getArtifactParameterName());            //到这里了,分为三种情况,            //有ticket,因为你已经登录了,cas服务器登录成功返回给你了,接下来进行校验            //无ticket,可能你没有配置第一个过滤器,溜进来了            //无ticket,ticket已经校验成功后跳转回来了,用户属性已经设置到session中了,所以这次请求没有ticket了,不用去校验            if (CommonUtils.isNotBlank(ticket)) {                if (this.log.isDebugEnabled()) {                    this.log.debug("Attempting to validate ticket: " + ticket);                }                try {                    //开始ticket票据校验,这才是这个ticket过滤器真正要做的                    //constructServiceUrl这个方法不用管,就是拼接一下URL路径,把我的APPID啥的拼接上去                    //validate做了挺多事,请看下一个类注释,这里先过去(大概逻辑就是去cas服务器验证ticket)                    Assertion assertion = this.ticketValidator.validate(ticket, this.constructServiceUrl(request, response));                    if (this.log.isDebugEnabled()) {                        this.log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName());                    }                    //看到这里没有,就是在第一个过滤器进行校验的参数,如果ticket验证成功,就会往request,及session设置属性,该属性就是_const_cas_assertion_                    //该属性值则是一个用户信息map                    request.setAttribute("_const_cas_assertion_", assertion);                    if (this.useSession) {                        request.getSession().setAttribute("_const_cas_assertion_", assertion);                    }                    //空方法,不用管                    this.onSuccessfulValidation(request, response, assertion);                    //ticket验证成功后,在进行跳转,这次是跳到我们自己的请求地址                    if (this.redirectAfterValidation) {                        this.log.debug("Redirecting after successful ticket validation.");                        response.sendRedirect(this.constructServiceUrl(request, response));                        return;                    }                } catch (TicketValidationException var8) {                    response.setStatus(403);                    this.log.warn(var8, var8);                    this.onFailedValidation(request, response);                    if (this.exceptionOnValidationFailure) {                        throw new ServletException(var8);                    }                    return;                }            }            filterChain.doFilter(request, response);        }    }

里面的ticket验证逻辑在此:

public Assertion validate(String ticket, String service) throws TicketValidationException {        //此处是拼接好要调用的URL        //http://sso.jxeduyun.com/sso/,该路径是在web.xml中改ticket过滤器进行配置的SSOServerUrlPrefix        //http://sso.jxeduyun.com/sso/serviceValidate?needAttribute=true&ticket=ST-28699-qdyblKpRwc5LpLk57dRM-sso.jxeduyun.com&service=http%3A%2F%2F127.0.0.1%3A9000%2Fdsideal_yy%2FdsTyrzLogin%2FssoLogin%3FloginType%3Dweb%26from%3Dew%26appId%3D00000&appKey=00000        String validationUrl = this.constructValidationUrl(ticket, service);        if (this.log.isDebugEnabled()) {            this.log.debug("Constructing validation url: " + validationUrl);        }        try {            this.log.debug("Retrieving response from server.");            //这里不用看,就是发起请求调用上面的接口,查看ticket有效性            String serverResponse = this.retrieveResponseFromServer(new URL(validationUrl), ticket);            if (serverResponse == null) {                throw new TicketValidationException("The CAS server returned no response.");            } else {                if (this.log.isDebugEnabled()) {                    this.log.debug("Server response: " + serverResponse);                }                //这个不用看了,就是解析返回的cas数据,然后获取里面的用户信息,并封装成map                return this.parseResponseFromServer(serverResponse);            }        } catch (MalformedURLException var5) {            throw new TicketValidationException(var5);        }    }

因为ticket验证成功后并没有直接到下一个过滤器,而是从新请求了一次,这次不会有ticket参数了,因为session中已经有属性了,就在前几个过滤器中进行判断,在都走一次,然后才会到下面这个过滤器

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest)servletRequest;        HttpSession session = request.getSession(false);        Assertion assertion = (Assertion)((Assertion)(session == null ? request.getAttribute("_const_cas_assertion_") : session.getAttribute("_const_cas_assertion_")));        try {            //该过滤器的作用就是,把用户对象从session中拿出来,放到AssertionHolder里面,从而在代码中获取对象信息的时候,            //直接调用该对象即可            AssertionHolder.setAssertion(assertion);            filterChain.doFilter(servletRequest, servletResponse);        } finally {            AssertionHolder.clear();        }    }

至此,cas的登录流程全部走完,不知道大家看懂多少,花了博主大概一天的时间才把源码理解通,ticket返回示例给大家一下,还有代码调用:

失败示例:<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>  <cas:authenticationFailure code='INVALID_TICKET'>    ticket 'ST-28699-qdyblKpRwc5LpLk57dRM-sso.jxeduyun.com' not recognized  cas:authenticationFailure>cas:serviceResponse>成功示例:<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>  <cas:authenticationSuccess>    <cas:user>testcas:user>            <cas:attributes>                    <cas:multipleId>test-test-test-test-testcas:multipleId>                    <cas:userId>testcas:userId>                    <cas:loginName>testcas:loginName>            cas:attributes>  cas:authenticationSuccess>cas:serviceResponse>

代码调用示例:

Assertion assertion = AssertionHolder.getAssertion();        String openId = assertion.getPrincipal().getName();        Map<String, Object> attributes = assertion.getPrincipal().getAttributes();        String userId = attributes.get("userId").toString();        String loginName = attributes.get("loginName").toString();        System.out.println("openId:"+openId);        System.out.println("userId:"+userId);        System.out.println("loginName:"+loginName);

原创不易,转载请说明出处,谢谢!

单点登录 cas 设置回调地址_cas客户端流程详解(源码解析)单点登录相关推荐

  1. 短信平台专业版软件客户端功能详解源码搭建|移讯云短信系统

    国际短信平台专业版软件客户端功能详解|移讯云短信系统 首页显示 剩余条数 充值总数 提交总数 成功数量 失败数量 未知数量 代发数量 签名数量 最新提交 平台公告 API接口文档 短信发送 发送短信选 ...

  2. 单点登录 cas 设置回调地址_单点登录(SSO)看这一篇就够了

    背景 在企业发展初期,企业使用的系统很少,通常一个或者两个,每个系统都有自己的登录模块,运营人员每天用自己的账号登录,很方便. 但随着企业的发展,用到的系统随之增多,运营人员在操作不同的系统时,需要多 ...

  3. 单点登录 cas 设置回调地址_单点登录落地实现技术有哪些,有哪些流行的登录方案搭配?...

    实现单点登录说到底就是要解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效 性,因此要点也就以下两个:1.存储信任 :2.服务器生产~验证信任 : 3.拿到服务器再次验证. 单点登录的常 ...

  4. Qt之布局设置setLayout详解-源码剖析(下)

    一.简述 大家好,我是前行中的小猪,今天呢给大家继续上一篇Qt之布局设置setLayout详解(上)之后的内容,再给大家进行一下拓展. 1.1 setLayout源码剖析 上篇我们说到如何清空部件上的 ...

  5. MMI Code设置/查询 补充业务(Supplementary Service) 流程详解

    转载请注明出处:https://blog.csdn.net/turtlejj/article/details/83898624,谢谢- 以前遇到过用户报市场问题,手机的通知中心一直显示用户当前Sim卡 ...

  6. WebSocket安卓客户端实现详解(一)–连接建立与重连

    http://blog.csdn.net/zly921112/article/details/72973054 前言 这里特别说明下因为WebSocket服务端是公司线上项目所以这里url和具体协议我 ...

  7. LCS2005客户端配置详解:LCS2005系列之二

    LCS2005客户端配置详解 上篇博文中我们介绍了如何部署LCS2005标准版,今天我们要再进一步,配置好LCS2005的客户端,使用户能够使用LCS提供的即时通讯服务来进行彼此间的信息交流.实验拓扑 ...

  8. Git客户端图文详解如何安装配置GitHub操作流程攻略

    Git客户端图文详解如何安装配置GitHub操作流程攻略 软件应用 爱分享  3个月前 (08-15)  8896浏览  0评论 Git介绍 分布式 : Git版本控制系统是一个分布式的系统, 是用来 ...

  9. 阿里api网关接口客户端demo,java实现源码,其他语言可参考

    访问阿里api网关接口客户端demo,java实现源码,其他语言可参考 上一篇文章 <阿里api网关接口创建.发布.授权.调试> 中,介绍了3个典型接口的创建并在阿里控制台调试完成,地址: ...

最新文章

  1. 1. python 字符串简介与常用函数
  2. 字段缩写ti表示什么_【滴水研究】解码TI(一)
  3. LeetCode Minimum Height Trees(拓扑排序)
  4. 树莓派搭建minecraft服务器
  5. 逆向工程核心原理读书笔记-代码注入
  6. 【今晚七点半】:5G时代的云游戏还缺什么?
  7. java监控rabbitMq服务状态,spring cloud 的监控turbine-rabbitmq的示例
  8. mysql 日志节点恢复_基于binlog二进制日志的MySQL恢复笔记
  9. java文件下载的utils_Java文件操作Utils
  10. 11.11开启10分钟 达达承接的京东小时购首单已签收
  11. 外星人做系统logo_深圳福田外星人笔记本电脑维修服网点
  12. Web Server监视器 v0.75 - Free tool
  13. Exchange2016-抢先体验
  14. hint用法mysql,使用hint优化Oracle的执行计划
  15. Git 学习(篇六 --储藏与清理)
  16. java课程设计打字训练测试软件_打字训练测试软件-Java课程设计
  17. Android Studio调用python运行thensorflow模型--CLE方案实现
  18. 文件丢失怎么找回来?恢复文件的方法
  19. 去除眼袋(画笔涂抹法)
  20. LaTex使用技巧10:公式中的各种英文字体

热门文章

  1. 7点 讲明白地图切片的概念与原理
  2. java使用mangodb进行多条件组合查询
  3. 密码库LibTomCrypt学习记录——(2.11)分组密码算法的工作模式——CTR代码示例
  4. LeetCode(89):格雷编码 Gray Code(Java)
  5. Json格式转换报java.lang.StackOverflowError
  6. 修改latex参考文件名字bibliography为reference
  7. Element_弹出框
  8. 推荐一个查询基金宏观数据
  9. CAD中线宽问题的说明
  10. 新浪微博客户端源码 android