ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CAS单点登录实战:票据、证书、会话与集群的坑与解法

CAS单点登录实战:票据、证书、会话与集群的坑与解法 说实话单点登录这个事儿做过的都觉得不难没做过的总觉得很神秘。其实CASCentral Authentication Service这套东西已经火了十几年了从耶鲁大学放出来之后几乎成了Java后端做统一认证的首选方案。尤其是企业里一旦上了OA、HR、财务、项目管理好几套系统每个系统一套账号密码IT部门光找回密码就忙不过来。我见过很多项目组功能开发一个月CAS对接却拖了两三周最后卡住的往往不是业务逻辑而是证书、回调、票据、会话这四个老问题。这篇就结合我这些年帮客户搭CAS、以及把各种乱七八糟的系统泛微OA、帆软报表、若依框架这类接入统一登录的实操经历把单点登录实施里最常见、最容易翻车的坑挨个过一遍。不求面面俱到但求每个坑都能看清原因、知道怎么改遇到类似情况能马上解决。1. 先把CAS的底细摸清楚票据、会话、客户端服务端各干各的活1.1 一套系统两种身份凭证很多人一上来就配CAS出了问题就抓瞎根本原因是没搞清楚CAS里“票据”这东西是怎么流转的。CAS里最核心的凭证有三类TGTTicket Granting Ticket、STService Ticket、PGTProxy Granting Ticket。TGT是用户在CAS服务器登录成功后CAS服务器种在浏览器Cookie里的凭证它的作用相当于“你在这个统一认证中心已经验过身份了”。ST则是CAS服务器在用户访问某个业务系统时临时签发的一张一次性票据业务系统拿到ST之后会拿着它回到CAS服务器去校验校验通过这个业务系统才相信“这个用户是真的”。PGT是代理模式下用的场景是A系统代表用户去访问B系统B系统也得验证用户身份这时候A系统就得从CAS服务器拿PGT再拿它换一个访问B系统的ST。我做一个不太严谨但很好懂的类比TGT就是你的小区门禁卡进大门刷一下就说明你住这个小区ST是你去邻居家串门时门口保安临时给你开的那张访客条邻居看到条子才让你进去但条子用一次就作废。理解这个之后大部分票据相关的报错比如“ST校验失败”“非法票据”你就知道是哪个环节出了问题。整个CAS登录流程串起来是这样的用户第一次访问业务系统业务系统的CAS客户端发现没有登录凭证就把请求重定向到CAS服务器同时带上一个service参数告诉CAS服务器“我是谁验证完了回哪儿去”用户在CAS服务器登录成功后CAS服务器生成了TGT存在Cookie里同时根据service参数重新生成一个ST拼在回调URL后面重定向回业务系统业务系统收到ST之后再去CAS服务器的serviceValidate接口校验ST校验通过业务系统本地建立自己的会话。1.2 服务端客户端责任边界不能混CAS部署通常是两个部分cas-server统一认证服务端和cas-client集成在各业务系统里的客户端。如果用的是Java生态cas-client-core这类的依赖或者用Spring Security CAS插件都是干这个的。这里容易犯的毛病是把所有问题都推给CAS服务端实际上很多问题的根源在客户端配置。我遇到过最典型的一次客户说“CAS登录后跳不回业务系统”查了半天发现业务系统里cas-client配置的回调地址写死了内网IP浏览器从外网访问时拿内网地址重定向当然进不来。所以排查CAS问题先分清楚是哪一端的锅用户在浏览器里的跳转链路异常多半是配置或网络问题ST校验失败、票据过期多半是服务端或时间同步问题登录成功后业务系统没建立会话多半是客户端Filter或会话配置问题。还有一点要特别注意尽量别把CAS服务端和业务系统混在一起部署。服务端一旦挂了所有接入系统的登录全部瘫痪。我建议服务端单独一台机器或者用集群至少和核心业务系统做到故障隔离。2. 证书与回调地址实施初期的两个拦路虎2.1 HTTPS证书不认ST一辈子都校验不过CAS协议在生产环境强制要求走HTTPS因为票据在URL上明文传不回加密封装。很多第一次搞CAS的同学本地测试用HTTP没问题一上生产就全线飘红基本上都是HTTPS证书没配好。其实这句话更准确的说法应该是CAS客户端请求CAS服务端校验ST的时候如果服务端返回的证书不被客户端信任那么HTTPS握手就失败ST校验自然也就失败。常见报错是SSLHandshakeException、PKIX path building failed这类。解决办法分两种场景。如果CAS服务端用的是正规CA机构签发的证书比如企业从阿里云、腾讯云、Let‘s Encrypt申请的那一般不会出现信任问题除非JDK的cacerts证书库太老缺少根证书。这种情况更新JDK版本或者手动把根证书导入到JDK的cacerts里就行keytool -import -trustcacerts -alias yourAlias -file yourCert.crt -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit如果CAS服务端用的是自签名证书测试环境居多或一些内网部署的生产环境那客户端这边必须把服务端的证书导入自己的信任库否则一直会报错。我遇到过Windows服务器和Linux服务器处理方式不一样Windows上如果业务系统跑在Tomcat里除了导入JDK的cacerts还得注意Tomcat可能使用单独的SSL配置别导错了库。另外一个容易忽略的细节如果域名有多个或者存在IP访问的情况证书的SANSubject Alternative Name里必须包含所有会用到的域名和IP。否则就算证书导入了浏览器地址栏也是报“证书不匹配”CAS服务端会拒绝跳转。这是非常隐蔽的问题——证书明明有效但就是报错因为你用IP去访问一个只签了域名的证书。2.2 service参数必须URL编码回调地址大小写也讲究CAS的重定向URL是标准的URLservice参数是完整URL所以必须要做URL编码。举个例子跳转地址应该是https://cas.example.com/cas/login?servicehttps%3A%2F%2Foa.example.com%2Fcas%2Fclient如果service参数没有编码很多浏览器和CAS服务端解析的时候会出问题尤其是业务系统的URL里带了参数的情况比如servicehttps://oa.example.com/cas/client?paramxxx。这里就有个我见过无数次的坑业务系统的回调地址带自己的业务参数时CAS服务端在解析service时只认第一个问号后面的参数导致参数错乱。业内推荐的写法是把业务参数做二次编码放到自定义参数里或者干脆在service的那段URL里把业务参数也整体编码一遍让CAS服务端把它当作service的一部分来解析。回调地址还有大小写问题。很多Tomcat应用对URL路径大小写不敏感但是CAS的service注册表里如果配置了精确匹配大小写不一致直接校验不过。比如你注册的是https://oa.example.com/cas/client浏览器实际跳回来的是https://oa.example.com/CAS/Client注册表对比就会失败。2.3 Nginx反向代理下的经典翻车现场企业中CAS服务端和业务系统都可能跑在内网由Nginx统一做HTTPS终结和反向代理。这种情况下CAS服务端经常会看到“不允许访问此服务”或者跳转异常原因是无法识别外部真实请求地址。需要在Nginx里显式传递Host和协议头proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $remote_addr;CAS服务端对应的Tomcat也要配置一个“远程IP阀值”和“协议头处理”支持否则它看到的还是内部IP和HTTP协议。Tomcat 8.5以上可以在server.xml的Host里加一个ValveValve classNameorg.apache.catalina.valves.RemoteIpValve remoteIpHeaderX-Forwarded-For protocolHeaderX-Forwarded-Proto /这套组合不配置的话CAS生成的TGT和ST的地址可能变成http://内网IP这样的形式浏览器拿到之后访问的还是外网域名票据校验就会出现各种莫名其妙的问题。还有一个和Nginx强相关的坑是SSO跳转路径里如果拼接了HTTPS和HTTP混用会直接导致redirect_uri不匹配。建议全程统一HTTPSNginx把所有HTTP请求301到HTTPS避免一个系统HTTP一个系统HTTPS。3. 会话保持与超时登录明明成功了过一会儿又跳回登录页3.1 集群环境下TGT和ST的存储共享是绕不开的坎现在稍微像样点的公司系统部署都是至少两台机器打集群。如果CAS服务端是集群部署而TGT和ST只是各自存在本地内存里那用户第一次登录命中了A节点下一次校验ST命中了B节点B节点没有这个ST就会直接报错。这算是集群部署下最常见的翻车点了。解决办法是把票据存储集中放到Redis这样的中间件里。cas-server支持TicketingRegistry的配置老版本通常是配置JpaTicketRegistry存数据库或HazelcastTicketRegistry新版本里也有专门的RedisTicketRegistry。我个人的建议是直接用Redis因为票据读取极其频繁Redis性能要比数据库好得多。同时也要注意业务系统这边如果也是集群部署业务系统自己保存用户会话的Session也必须共享否则用户在A节点登录下一次请求被负载均衡转发到了B节点B节点没有会话就会带着用户去CAS重新登录。业务系统的Session共享工具业内常见的可以用Spring Session Redis或者Tomcat的RedisSessionManager。3.2 超时控制别乱拍脑袋各层超时要匹配CAS的超时时间分为几个层次最核心的是TGT超时、ST超时、业务系统Session超时。这三个时间如果设置不合理会出现很别扭的情况。具体来说TGT存活时间决定了用户在统一认证中心“免登录”的时长ST是一次性票据存活时间非常短一般30秒到几分钟业务系统的Session超时决定了用户晾在那里多久之后业务系统要求重新登录。常见的错误是把ST超时时间设置太长觉得这样更“稳定”。结果就是业务系统拿着一个超旧的ST去校验CAS服务端一看早已过期直接拒绝。另一个容易踩的坑是TGT的有效期比业务系统Session短。用户用着用着业务系统Session还在但TGT没了等到用户访问另一个系统B时B系统一看用户没登录重定向到CASCAS发现TGT已经过期直接要求重新登录。用户就觉得“我明明在用系统A怎么切到B就要重新登录”体验很差。所以各层超时设置建议是TGT要比业务系统的Session长业务系统Session不低于30分钟。TGT可以设置8小时到24小时或者按企业安全规范设定。有些企业还希望“用户关掉浏览器清掉Cookie之后下次打开还要登录”这里就要把TGT对应的Cookie设为会话级Cookie也就是不设置过期时间浏览器关闭即失效但内部系统的Session还是会保持一段时间这个策略也需要根据使用习惯去权衡。3.3 退出登录的连锁反应很多团队没见过单点登录除了“单点登录”还有个“单点退出”的要求。用户在A系统点了退出结果B系统还是登录状态这种情况其实很常见原因是A系统只把A系统自己的Session清了并没有通知CAS和B系统。CAS支持SLOSingle Log Out原理是CAS服务端在退出接口收到请求后会遍历所有由这个TGT签发的ST对应的业务系统向它们的logout回调地址发送HTTP请求。所以要做成单点退出有几个条件缺一不可业务系统接入的cas-client启用了单点退出功能并暴露了logout回调地址。如果业务系统没用cas-client而是自己对接CAS协议这个回调就得自己实现。业务系统调用的退出地址必须指向CAS的/logout接口而不是自己系统的/logout。如果你只是在A系统本地删了Session那CAS和其他系统完全感知不到。如果CAS服务端和业务系统之间有防火墙或白名单限制要确保CAS能访问到业务系统的logout回调端口否则SLO请求会失败。另外单点退出时CAS会对每个接入的业务系统发回调如果有个别系统响应很慢会拖累整个退出流程。所以CAS这种做法对系统可用性要求高如果业务系统挂了SLO请求会失败用户看到的情况就是部分系统没退出。这个暂时没有特别完美的解决办法只能让各业务系统做好容错以及接受“退出不保证100%同步”的现状。3.4 浏览器Cookie域和SameSite策略是新老项目都会踩的坑Cookie域的配置对单点登录至关重要。CAS的TGT默认写在CAS服务端域名下业务系统自己的Cookie写在业务系统域名下。如果CAS和业务系统是同一个主域名下的不同子域名比如cas.company.com和oa.company.com那可以设置Cookie的Domain为company.com这样各个子域名共享Cookie对TGT的传递会更顺畅也能减少一部分跳转问题。但如果CAS和业务系统完全不是一个主域名比如cas.company.com和oa.other.com那TGT的Cookie无法跨域传递只能依靠重定向携带ST来处理。此时TGT还是种在cas.company.com下但是用户从业务系统跳去CAS登录时浏览器会带着cas.company.com的Cookie请求所以CAS能识别到已经登录过直接签发ST跳转回来。注意这一步并不是所有浏览器都默认放行尤其是新版本的浏览器对第三方Cookie跨站请求限制特别严格。这里要重点提一下SameSite属性。Chrome 80之后SameSite的默认值是Lax如果CAS服务端返回的Set-Cookie里没明确设置SameSite或者设置成了Strict有些跨站场景下Cookie就不会发送。比如用户从业务系统a.example.com跳转到cas.example.com去登录CAS生成了TGT并返回Set-Cookie如果浏览器把这个响应当成跨站Set-Cookie来处理后续用户带着这个Cookie回到业务系统校验可能就会失败。我建议在CAS服务端设置Cookie时把SameSite设为Lax并且Secure属性在HTTPS环境下正常设置。如果你用的是Spring Boot内嵌Tomcat可以通过配置CookieSameSite来覆盖server: servlet: session: cookie: same-site: lax secure: true但如果你的CAS前端还套了一层CDN或网关需要检查一下CDN是否过滤了Set-Cookie或者改了Domain属性这类问题排查起来非常隐蔽经常查半天代码发现是网关层的cookie改写策略有问题。4. 对接不同业务系统时的高频疑难杂症4.1 泛微OA、帆软报表、若依这类系统对接时该怎么下手热词里提到“泛微OA系统单点登录金蝶”、“帆软单点登录插件下载”、“将多个独立的若依系统改造为统一单点登录”这些都属于典型业务系统对接CAS的场景。它们各有各的接入方式但共性是不能用“我以为对方支持”的心态去写代码一定要先看对方官方提供的对接说明和回调接口。泛微OA对接CAS通常走的是它的外部认证接口OA提供一个第三方认证跳转地址你在CAS服务端配置成OA支持的方式登录后由OA自行建立Session。这里有一个常见误区有人直接在OA里写死一个账号密码去调金蝶没有通过CAS拿到的用户身份去做映射导致用户在统一认证中心登录成功进了OA却变成了另一个人的身份。正确做法是从CAS的Assertion里取出用户名再和OA的用户表做映射映射成功才能建立会话然后OA把处理后的用户身份再传给金蝶。帆软报表的对接相对规范一些。帆软有官方单点登录插件支持CAS协议在帆软后台配置好CAS服务端地址和回调地址就能用。但帆软有自己的用户体系CAS登录成功之后帆软会按用户名去匹配用户表匹配不上也会被拦在门外。所以接入前要先确认用户同步策略是定时同步组织架构到帆软还是用帆软的插件直接对接LDAP或者是在帆软里手动建同名用户。反正用户名不一致是帆软对接CAS的最大坑。若依这个框架近两年很流行很多团队拿它做后台管理系统一个公司好几个若依项目结果每个都带着自己的登录页。改成统一CAS的时候核心是改两处一处是自定义认证过滤器把若依的登录逻辑替换成CAS的登录流程校验通过后创建本地的Authentication和Session另一处是退出逻辑若依的退出接口要改成同时调CAS的logout并且把若依自身的Cookie清理干净。多接入一个系统就要多注意一次这个系统的Session管理和CasClient过滤器执行顺序尤其是Spring Security和Shiro配置冲突的问题。4.2 不要混淆“此CAS非彼CAS”顺带提一句热词里有个“H3C CAS”和“CAS号数据库”这跟我们要讲的单点登录CAS其实没关系。H3C的CAS是虚拟化平台化学领域的CAS号是化学文摘社的登记号容易在搜索资料时碰见注意别搞混就行。与CAS相关的一个经常一起出现的技术是“bacnet explorer”这个热词——它通常和楼宇自控的BACnet协议有关和单点登录没有关系。我列出来是为了提醒大家热词搜索很容易带偏真正做CAS排查的时候还是要把眼光聚焦在认证协议本身别被无关信息干扰。4.3 LDAP统一用户认证与CAS组合是最常见的企业标配热词里有一条“ldap统一用户认证和单点登录”这两者确实是绝配。CAS解决的是多个系统的登录状态互通LDAP解决的是用户的统一存储和密码校验。企业里面通常是用OpenLDAP或AD域作为用户源CAS服务端配置LDAP认证处理器实现“一套账号密码登录所有系统”。这个组合常见的坑有三个。第一个是LDAP的BaseDN和过滤条件配错。很多运维把BaseDN写成oupeople,dccompany,dccom但实际部门树里还有一层组织导致搜索范围不全用户查不到。建议先用ldapsearch命令验证一下过滤条件能搜到用户再配置到CAS里ldapsearch -x -H ldap://ldap.company.com -b oupeople,dccompany,dccom (uidzhangsan)第二个是LDAP密码策略与CAS不匹配。LDAP里做了密码过期提醒、连续错误锁定等策略CAS这边的密码处理器要能正确解析LDAP返回的错误码并转成人类可读的错误提示否则用户看到的是“用户名或密码错误”实际上可能是密码已过期。第三个是账号映射。LDAP里的uid、sAMAccountName可能和业务系统里的用户ID不一致对接时一定要建立映射规则否则会出现同一个用户在不同系统里看到不同身份的情况。4.4 其他协议也可以参考CAS的思路很多项目最后并没有真的部署CAS而是参考CAS的思路做了自研单点登录比如OAuth2、OIDC、SAM。这些协议和CAS虽然不一样但很多问题域是重叠的比如回调地址、Token有效期、会话保持、跨域Cookie。你在排查CAS问题的时候积累的这些经验迁移到OAuth2里同样适用所以别觉得掌握一个CAS协议就过时了底层的思路才是最有价值的。5. 常见问题速查表与排查日志的方法论5.1 拿不到ST、跳转循环、校验失败的排查路径诊断CAS问题最有效的手段还是看日志。CAS服务端一般会开DEBUG级别日志cas-client也会打认证过程的日志。发现问题时先看这几类问题跳转循环或者一直转圈。多半是service参数没编码、service未注册、回调地址不匹配导致的。你在浏览器地址栏里看一下跳转到CAS的service参数手动解码确认和注册表里的地址是否一致。登录成功但跳不回业务系统。查看CAS服务端发布的ST票据以及Nginx配的代理头和Host是否正常。如果只在内网生效、外网访问跳不回来大概率是回调地址写死了内网IP。ST校验失败。重点确认证书是否可信、时间是否同步、ST是否已经过期、是否在集群环境下落到了不同节点。一个特别实用的经验排查CAS问题时先把CAS服务端日志和业务系统Tomcat日志时间对齐再手动模拟一次登录这样你在两边日志里找同一个ST的流转记录就很快。5.2 记录一份可长期复用的问题速查表我在多个项目里实践下来最靠谱的方式是维护一份运维速查表每次对接新系统、出现新问题就往上补充。下面整理一份最常见问题的紧凑版按“现象—原因—解决方法”的方式记录。C列和D列建议填写实际部署的URL和命令直接复制到团队文档里就能用后续培训新人也能少踩坑。现象原因解决方法访问业务系统跳转到CAS后提示service未授权service参数未注册到CAS服务端或者回调地址和注册表不完全一致检查WEB-INF/services下的JSON配置文件确认URL完全一致注意路径大小写登录成功后跳转回业务系统页面报ST校验失败ST过期、服务端时间不同步、集群节点不一致统一各节点时间启用NTP同步检查Redis共享票据配置重新登录测试页面报SSL证书不受信任CAS服务端证书未导入客户端JDK信任库手动导入证书到cacerts或为客户端单独配置信任库登录成功后业务系统未创建本地会话客户端Filter拦截顺序错误或本地会话在跳转过程中被重置检查web.xml里CAS Filter的url-pattern范围确保在权限Filter之前执行用户在A系统登录成功切到B系统仍要求重新登录TGT Cookie未保存成功或SameSite、Cookie域配置不对检查CAS登录成功后浏览器的Cookie是否种下检查Domain和SameSite必要时设置Domain为主域退出A系统但B系统还是登录状态单点退出回调未配置或业务系统不支持SLO配置cas-client的单点退出功能在业务系统暴露logout回调地址单点登录成功但用OpenLDAP校验时某些用户无法登录过滤条件范围不对或用户名映射不一致用ldapsearch验证过滤条件检查CAS用户源中的baseDNNginx反代后回调地址变成了http和内网IP未传递Host和X-Forwarded-ProtoNginx配置proxy_set_headerTomcat加RemoteIpValve时间不对导致票据无效服务器和客户端时钟偏差过大配置NTP时间同步偏差控制在1分钟以内5.3 CAS服务端日志怎么看CAS的日志一般位于logs目录下。排查时先打开DEBUG级别的logger比如logback.xml配置logger nameorg.jasig.cas levelDEBUG/重点关注几个关键字Creating ticket [TGT-...] 说明登录成功并创建了TGTGranting service ticket [ST-...] 说明跳转前签发了STValidating service ticket 说明客户端来校验了Service ticket [ST-...] is not recognized 或者expired说明ST没找到或超时这个流转日志能帮你在几分钟内定位到是用户登录还是票据校验环节出了问题。5.4 耗时问题的定位思路还有一个高频现象是CAS登录偶尔很慢慢到用户以为被卡住。这个慢通常是这三类原因CAS服务端到LDAP/AD的认证超时CAS服务端在跳转时调用了外部接口比如验签、审计、风控而外部接口响应慢Redis连接池满了或者网络抖动导致票据读写超时。定位方法也很直接打开CAS服务端DEBUG日志看时间戳之间的间隔哪个环节耗时最长就去查哪个环节。如果认证用户源响应慢可以在CAS的LDAP配置里设置连接和读取超时参数如果Redis响应慢就看Redis的监控指标和网络延迟。6. 最后再分享一个实用小习惯每次做CAS对接时先画一张链路图再动手可能有人觉得这不是技术问题但我在实际项目里发现很多对接出问题恰恰是因为心里没有一张完整的链路图。这里说的链路图不一定要画得很漂亮但至少要把这些要素列出来。CAS服务端域名客户端域名网络是否可达浏览器在哪个环节拿到TGT在哪个环节拿到ST哪些系统部署了集群各系统的Session超时时间单点退出动作从哪个地址发起需要回调哪些系统每次出问题先回到这张图上看是哪一环断了而不是一头扎进代码里翻。排查速度会快很多也更容易定位“啊原来是集群部署导致ST丢失”这类隐蔽问题。我在实际项目里的感受是CAS本身是一个比较成熟的协议大多数问题都是配置和运维层面的问题代码层面的坑反而不多。与其去记一堆报错细节不如把Single Sign-On这个链路彻底吃透——你只要把Cookie、票据、重定向这三样东西的流转方向搞清楚CAS里的很多问题自然迎刃而解。希望这篇能把你们从“配置两小时排错两星期”的痛苦里解救出来。
返回列表