
简单来说,SAML(Security Assertion Markup Language)就是一种跨域身份认证的标准语言。你想想看,平时我们登录一个应用,输用户名密码,这个应用就知道你是谁了。但如果另一个应用也想验证你的身份,难道又要输一次密码吗?SAML就是来解决这个问题的。
它用一种标准化的XML格式来描述身份信息,让不同的安全域之间可以共享这些信息。就好比你去住酒店,在前台登记一次身份信息后,酒店里所有服务(健身房、游泳池、餐厅)都知道你是住客了,不用每去一个地方就再登记一次。
我这里用一个实际例子来说明。上个月我们公司要接入一个第三方的CRM系统,他们要求用SAML进行单点登录。当时产品经理跟我说:"张工,咱们系统能不能实现登录一次,CRM那边就直接能访问?"我当时就想到了SAML。

这里就涉及到两个关键角色:IDP(身份提供商)和SP(服务提供商)。IDP是管身份认证的,就像酒店的"前台登记处";SP是需要验证用户身份的,就像酒店的"各种服务设施"[1]。
在我们那个项目中,我们自己的系统要当IDP,CRM系统是SP。用户在我们这边登录成功后,CRM那边就不用再登录了。这个过程中,SAML协议就像一个信使,负责把身份信息从我们这里安全地传递给CRM系统。
说到具体的实现,我一开始用的是Spring Security SAML这个框架。说实话,Spring家族的东西就是好用,很多轮子都给你造好了[1]。你只需要引入相关依赖,配置一下元数据和证书,基本上就能跑起来。
但是,配置过程中遇到的问题可一点都不少。第一个坑就是元数据文件。SAML这个东西依赖一个叫"元数据"的配置文件,里面包含了双方的地址、标识符、证书信息等。
记得第一次配置的时候,我们的IDP元数据文件生成好了,发给CRM那边的工程师。结果人家说他们解析不了。后来一查,原来是我们生成的元数据文件中没有包含签名的公钥证书。这就好比你的身份证上没有照片,别人怎么验证你到底是不是你呢?
重新配置的那天,我在办公室里待到晚上10点多。手头有大量的XML文件要处理,看得眼睛都花了。不过最后总算是搞定了一个基本可用的版本。
后来有个项目,客户要求把他们的ADFS(Active Directory Federation Services)作为IDP,接入到我们的应用中来。这对我们来说是个新挑战,因为之前都是用自己建的IDP。
客户那边用的是阿里云的IDaaS(身份即服务),他们想把企业现有的ADFS整合进来。这个过程说实话比我们自己做IDP要复杂得多。需要两边配置,一边是IDP侧(就是客户那边的ADFS),一边是SP侧(我们的应用)[5]。
具体的步骤大概是这样的:首先要在IDaaS上创建一个SAML身份提供方,然后把ADFS的元数据URL填进去。系统会自动解析ADFS的配置信息,获取SSO地址、唯一标识这些基本信息[5]。
接着就是字段映射这步,这是最关键的。ADFS那边的用户属性(比如邮箱、姓名)要映射到我们系统的用户字段上。这个映射关系搞错了,用户登录后信息就对不上。
我印象深刻的是有一次配置完,客户测试的时候发现登录成功了,但是显示的用户名不对。后来一看,原来是我们把ADFS的"显示名称"字段映射错误了,明明是"张三",结果显示成了"samzhangsan"。这种细节问题,不经过实际验证还真发现不了。
说到IDP,不能不提Keycloak。这个开源的身份和访问管理解决方案,现在用的人越来越多。我之前有个项目,客户要用Keycloak做SAML IDP,为他们的IDE工具提供企业级SSO登录(这个有时间也写一篇文章分享下)。
这个架构挺有意思的:Keycloak作为SAML IDP,跑在AWS ECS Fargate上,数据库用的是Aurora PostgreSQL。前端是AWS IAM Identity Center当SP,负责把身份认证委托给Keycloak,然后再向应用发放临时凭证。
这个方案的最大好处是免运维。Keycloak跑在Fargate上,数据库用托管的Aurora,咱们只需要关注业务逻辑就行了。我记得当时做性能测试的时候,Keycloak的响应时间很不错,基本在毫秒级别。
但是Keycloak的SAML配置也有不少坑。比如clientId不能随便写,得等于Identity Center的Issuer URL。还有saml_name_id_format这个参数,必须写完整的URN格式:"urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress",写得不对就报错。
更头疼的是protocolMappers配置。email和email-nameid这两个映射器必须都配置上,少一个都不行。我第一次配置的时候只配了email,结果测试的时候发现saml的NameID字段为空,折腾了好久才找到原因。
说到SAML,就绕不开签名和证书这个话题。所有的SAML请求和响应默认都要签名,这是为了确保信息的真实性和完整性。
在我们那个Keycloak项目中,我特意研究了签名算法。一般来说都是用RSA-SHA256,这个算法现在挺安全的。但是配置证书的时候有个细节需要注意:IDP和SP要互相信任对方的证书。
有一次我们公司内部测试环境出问题了,后来发现是SP换了证书但没通知我们IDP侧更新。结果SP发的请求IDP不认,IDP发的断言SP也不认,两边就这么僵住了。
说到这,我想起一个有趣的对比。传统的session-based认证和SAML这种基于断言的认证,有点像现金支付和移动支付的区别。session认证就是现金,你在一个地方用的钱(session会话)在别的地方不好使;SAML就像是移动支付,你的身份信息(断言)可以在不同的地方被验证。
我现在总结一下这几年搞SAML项目的一些经验和想法:
1. 元数据管理很重要 元数据文件就像是SAML世界的身份证。一定要确保元数据里面的地址、标识符、证书都是最新的。我现在的做法是:元数据文件不要手动改,都通过工具生成;元数据变更要有版本控制;元数据发布要有通知机制。
2. 字段映射要仔细 这是最容易出问题的地方。IDP和SP两边的用户属性字段名往往不一样,比如一个叫"emailAddress",一个叫"mail"。配置的时候一定要做充分的测试,最好有一个测试用户清单,把各种属性的映射都验证一遍。
3. 错误处理要做好 SAML认证失败的原因有很多:签名无效、时间过期、受众不符、格式错误等等。SP这边一定要有友好的错误页面,最好能把SAML响应的错误信息记录下来(当然不是明文记录,要脱敏)。
4. 考虑性能问题 SAML请求和响应都是XML格式,里面还包含了签名信息,传输体积不小。在高并发场景下,要考虑压缩、缓存等优化措施。
5. 日志要打全 SAML流程中的每个关键步骤都要打日志:SAML请求发起、IDP认证、生成断言、验证签名、映射用户等等。出了问题这些日志就是救命稻草。
讲个实际的例子吧。我们公司内部现在有十几个系统,之前每个系统都要登录一次。后来我们搞了个统一的IDP,用SAML打通了所有系统。
现在的情况是这样的:员工早上来公司,在OA系统登录一次。然后去看考勤系统,直接进去了;再到研发管理系统,也不需要登录;下午要报个销,进财务系统也是秒进。这种体验真的不一样,用户满意度提升了不少。
但是,单点登录也有副作用。因为登录太方便了,有时候用户在某个应用里做敏感操作时,反而容易忽略这是有风险的行为。我们就遇到过员工在公共电脑上登录OA后离开,别人可以用他的身份访问所有关联系统。
说了这么多,最后想说一点我的个人看法。SAML这个东西,并不是适合所有场景。
首先,它适合企业级应用,多个应用之间需要共享认证信息的情况。如果你的应用是独立的,或者只有一两个关联应用,其实用传统的session + cookie就够了。
其次,实施SAML需要一定的技术储备。前面说的那些XML处理、证书管理、签名验证,都不是很简单的事情。如果你的团队技术力量不够,可能要慎重考虑。
最后,要考虑运维成本。SAML引入了新的组件(IDP),增加了系统复杂度,也增大了运维压力。证书过期了怎么办?元数据变更了怎么处理?这些都是要考虑的问题。
但是,如果你真的需要多应用间的无缝单点登录体验,SAML是目前比较成熟和可靠的选择。特别是现在很多云服务都支持SAML协议,对接起来相对方便。
写了这么长,感觉还有很多东西没说全。SAML和IDP这个话题,涉及的东西确实挺多的:协议标准、安全机制、实际部署、运维管理……每个方面都有很多值得探讨的地方。
我的经验是,学SAML最好的方法就是实践。自己动手配一个Keycloak IDP,对接一个SP应用,把整个流程走一遍。过程中肯定会遇到很多问题,但解决这些问题的过程就是最好的学习。
说句实在话,我现在还记得第一次看到"SAML Assertion accepted!"日志时的激动心情。那种感觉就像是解开了一个复杂的密码锁,之前所有的努力和尝试都值得了。
好了,今天就说这么多吧。身份认证这个领域还有很多好玩的东西,比如OAuth 2.0、OpenID Connect这些协议,以后有机会再跟大家聊。
如果这篇文章对你有帮助,记得转发给身边需要的朋友。我们运维这行,最缺的就是经验分享。
大家有什么问题或者自己的SAML实战经历,也欢迎在评论区分享交流。
公众号:耕云躬行录
个人博客:躬行笔记