单点登录系统(SSO)自建方案总结
总结自建企业级单点登录系统(SSO)的实战方案,解析基于 Token、Cookie 与跨域重定向的单点登入与全局注销机制。
一篇文章带你了解单点登录系统,包括不限于:SSO 是什么、SSO 的常用协议、SSO 的登录流程、SSO 的几种常见解决方案以及最后的企业级SSO 系统搭建。
什么是 SSO
单点登录(SSO)是一种身份验证解决方案,可让用户通过一次性用户身份验证登录多个应用程序和网站。这意味着用户只需输入一次用户名和密码,即可访问所有相互信任的系统,而无需在每个系统中单独登录。鉴于当今的用户经常直接从其浏览器访问应用程序,因此组织正在优先考虑改善安全性和用户体验的访问管理策略。SSO 兼具这两方面的优点,因为一旦验证身份,用户就可以访问所有受密码保护的资源,而无需重复登录。
SSO 的主要优点包括:
- 用户体验改进:用户只需记住一个用户名和密码,减少了重复登录的麻烦。
- 安全性增强:通过集中管理认证信息,可以更好地控制和保护用户数据,降低密码泄露的风险。
- 效率提升:减少了因多次登录导致的时间浪费,提高了工作效率。
在 SSO(单点登录)系统中,身份提供者(Identity Provider,IdP)和服务提供者(Service Provider,SP)是两个关键角色。SSO 依赖于身份提供者(Identity Provider,IdP)和服务提供者(Service Provider,SP)之间的信任关系。
-
身份提供者(Identity Provider, IdP):
定义:IdP 是负责验证用户身份并提供身份信息的实体。它管理用户的认证信息(如用户名、密码、令牌等)并提供这些信息给信任它的服务提供者。
例子:
- 企业内部 IdP:如 Microsoft Active Directory Federation Services(AD FS),它为企业内部应用提供单点登录服务。
- 第三方 IdP:如 Google、Facebook、Okta 等,它们为各种第三方应用提供认证服务。
-
服务提供者(Service Provider, SP):
定义:SP 是提供某种服务或应用的实体,它依赖 IdP 来验证用户身份。SP 信任 IdP 提供的身份验证信息,并根据这些信息决定用户的访问权限。
例子:
- 企业应用:如企业的邮件系统、CRM(客户关系管理)系统、ERP(企业资源计划)系统等,这些系统通过企业内部的 IdP 提供单点登录功能。
- 在线服务:如 Dropbox、Salesforce、Slack 等,这些在线服务可以通过第三方 IdP(如 Google 或 Okta)提供单点登录功能。
SSO 常见的协议和技术
- SAML(Security Assertion Markup Language):一种基于 XML 的开放标准,用于在身份提供者和服务提供者之间交换认证和授权数据。
- OAuth:一种开放标准,允许第三方应用程序在不暴露用户密码的情况下访问用户信息。
- OIDC:OpenID Connect 是使用一组用户凭证访问多个站点的方法。它允许服务提供商承担验证用户凭证的角色。Web 应用程序不是将身份验证令牌传递给第三方身份提供商,而是使用 OIDC 来请求附加信息并验证用户的真实性。
- LDAP:轻量级目录访问协议 (LDAP) 是一种行业标准,基于 X.500 标准的轻量级目录访问协议,用来进行统一账号管理、身份验证平台。
- Kerberos:Kerberos 是一种基于票证的身份验证系统,可让两方或多方在网络上相互验证其身份。它使用安全密码学来防止未经授权访问在服务器、客户端和密钥分发中心之间传输的标识信息。
SSO 的登录流程图
详细步骤说明:
- 步骤 1 和 2:用户请求资源,系统 A 发现用户未登录,返回重定向指令。
- 步骤 3 和 4:用户浏览器重定向到 SSO,用户在 SSO 登录。
- 步骤 5:SSO 验证成功后,重定向回 系统 A ,并附带 token。
- 步骤 6:用户浏览器重定向到 系统 A 的回调页面。
- 步骤 7 和 8:系统 A 向 SSO 验证 token,SSO 返回用户信息。
- 步骤 9:系统 A 创建或更新用户登录信息。
- 步骤 10:系统 A 返回用户请求的资源页面。
搭建 SSO 常用解决方案
搭建一个 SSO(单点登录)系统有多种方法,具体选择取决于组织的需求、技术栈以及安全要求。以下是几种常见的方法:
1. 使用现有的 SSO 解决方案
- 商业解决方案:
- Okta:提供广泛的 SSO 服务,包括云和本地应用的集成。
- Microsoft Azure Active Directory:适用于使用 Microsoft 技术的组织,提供强大的身份管理和 SSO 功能。
- Ping Identity:提供企业级的身份管理和 SSO 解决方案。
- 开源解决方案:
- Keycloak:一个开源的身份和访问管理工具,支持 SSO、身份联合和管理。
- Gluu Server:一个灵活的开源 SSO 和身份管理平台。
- Shibboleth:一个支持 SAML 的开源身份提供者和服务提供者解决方案。
适用场景
- 快速启动:项目需要快速部署且时间紧迫。
- 广泛支持:需要支持多种应用和服务的集成。
- 企业级支持:需要企业级的技术支持和维护。
2. 自定义开发实现 SSO
- SAML(Security Assertion Markup Language):
- SAML 是一个基于 XML 的开放标准,用于在身份提供者和服务提供者之间交换认证和授权数据。
- 适用于企业级应用和内部系统集成。
- OAuth 2.0 和 OpenID Connect:
- OAuth 2.0 是一种授权框架,允许第三方应用在不暴露用户密码的情况下访问用户信息。
- OpenID Connect 是基于 OAuth 2.0 的认证协议,提供简单而强大的身份验证功能。
- 适用于现代 web 和移动应用的 SSO 实现。
- 使用 JWT Token 自定义开发:
- 对于特定需求,可以自定义开发一个 SSO 系统,使用 JWT(JSON Web Token)等技术进行认证和授权。
- 需要较高的技术投入和安全管理。
适用场景
- 协议标准化:需要使用标准化协议(如 SAML、OAuth 2.0、OpenID Connect)进行认证和授权。
- 灵活性:需要灵活的配置和扩展能力。
- 定制需求:现有解决方案无法完全满足项目的特殊需求。
- 控制和安全:需要完全控制系统的每个方面,包括安全和性能。
3. 集成现有身份管理系统
- LDAP(轻量级目录访问协议):
- 使用 LDAP 服务器(如 OpenLDAP、Microsoft Active Directory)集中管理用户身份,并通过 LDAP 进行认证。
- 适用于需要集中管理用户和组的组织。
- Kerberos:
- 使用 Kerberos 协议进行网络认证,通过中心认证服务器验证用户身份。
- 适用于需要强身份验证和单点登录的企业环境。
适用场景
- 现有系统扩展:已有身份管理系统(如 LDAP、Active Directory)并希望扩展其功能以实现 SSO。
- 集中管理:希望统一管理用户身份和访问权限。
4. 使用 WAM 解决方案
- Web Access Management 工具
- 使用 WAM 工具(如 CA SiteMinder、IBM Tivoli Access Manager)管理 web 应用的访问和 SSO。
- 适用于大型企业和复杂的 web 应用环境。
适用场景
- 复杂环境:需要复杂的访问控制和策略管理,通常用于大型企业。
- 多种身份验证方法:需要支持多种身份验证方法和复杂的访问策略。
假设一个业务背景:使用低成本、快速来完成 SSO 系统平台的搭建,比较推荐以下两种方式:
1、采用 SpringBoot + Oauth2 + JWT Token 技术栈,进行代码的定制化开发
2、采用开源工具(比如:Keycloak) 来快速进行搭建,在此基础上进行基础以及一定的定制化插件开发
除了低成本、快速外,我们还需要更加强大的功能,比如:支持 two-factor 双因子校验、复杂的 password strategy 等,那可以尝试使用 Keycloak 来进行快速的搭建和集成。如果你有一整套基于 Oauth2 的代码可以进行快速的复用,采用第一种方案也是一种不错的考虑,从零开始开发的话,相较于第二种方案成本会大一些。下面就来介绍一下方案二如何使用。
使用 Keycloak 搭建企业 SSO
Keycloak 是一个开源的身份和访问管理解决方案,提供了 SSO(单点登录)、多因素认证、LDAP 和 Active Directory 集成、社交登录(如 Google、Facebook 等)、用户管理等功能。它基于现代身份协议(如 OAuth 2.0、OpenID Connect 和 SAML),并且非常适合微服务架构和现代 Web 应用。
如何开始使用 KeyCloak
大致可以分为四步:
1. 安装 Keycloak:
- 可以使用 Docker 部署 Keycloak,或下载二进制包进行安装。
- 官方文档 提供详细的安装和配置指南。
2. 配置域和用户:
- 通过管理控制台创建和配置域、用户、角色和客户端。
3. 集成应用:
- 使用客户端适配器或直接调用 Keycloak 的 API,将应用与 Keycloak 集成。
- 配置应用的认证和授权策略。
4. 自定义和扩展:
- 根据需求进行自定义,如定制登录界面、邮件模板,或扩展 Keycloak 的功能。
01 安装 Keycloak
Pre Start
1、需要准备本地的云容器环境,可以参考本文最后的往期文章推荐。在之前的文章中我采用的 Rancher Desktop 来构建本地的云环境,是因为 Docker Desktop 开始商用收费,如果是个人电脑做研究,还是可以直接使用 Docker Desktop 的。两者总体体验下来差异不大。
2、官方给出的 quick start demo 实在是太简单了,没有高可用,没有数据存储,我会调整脚本以支持高可用 HA、MySQL 数据库存储防止数据丢失等,最终以 K8S yaml 脚本进行部署。
3、Keycloak的数据默认持久化在本地文件中,在生产环境中需要通过数据库来持久化数据,我这里使用容器化部署的 MySQL。
4、通过 Ingress 配置域名,并且自动配置 HTTPS,可以参考往期文章,这里因为是本地安装,暂时改用 NodePort 方式访问。
Docker
本地快速启动体验可以使用这种方式。
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:25.0.1 start-dev安装成功后通过本地 8080 端口进行访问,用户名和密码分别为 admin、admin。
Kubernetes
脚本编写:
支持高可用 HA K8S,同时数据持久化到 MySQL 中。其中k8s secret 中的配置按需进行调整,完整的脚本如下:
apiVersion: v1
kind: Service
metadata:
name: keycloak
labels:
app: keycloak
spec:
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 30008
selector:
app: keycloak
type: NodePort
---
apiVersion: v1
kind: Secret
metadata:
name: keycloak-secret
labels:
app: keycloak
type: Opaque
data: # changme
keycloak_admin_user: YWRtaW4= # admin
keycloak_admin_password: YWRtaW4= # admin
db_provider: bXlzcWw= # mysql
db_url: amRiYzpteXNxbDovL215c3FsLmRldm9wcy5zdmMuY2x1c3Rlci5sb2NhbDozMzA2L2tleWNsb2FrP2NoYXJhY3RlckVuY29kaW5nPVVURi04 # jdbc:mysql://mysql.devops.svc.cluster.local:3306/keycloak?characterEncoding=UTF-8
db_user: cm9vdA== # root
db_password: cGFzc3dvcmQ= # password
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak
labels:
app: keycloak
spec:
replicas: 2 # changeme
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:25.0.1
args:
- --verbose
- start
env:
- name: KEYCLOAK_ADMIN
valueFrom:
secretKeyRef:
name: keycloak-secret
key: keycloak_admin_user
- name: KEYCLOAK_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-secret
key: keycloak_admin_password
- name: KC_PROXY
value: "edge"
- name: KC_DB
valueFrom:
secretKeyRef:
name: keycloak-secret
key: db_provider
- name: KC_DB_URL
valueFrom:
secretKeyRef:
name: keycloak-secret
key: db_url
- name: KC_DB_USERNAME
valueFrom:
secretKeyRef:
name: keycloak-secret
key: db_user
- name: KC_DB_PASSWORD
valueFrom:
secretKeyRef:
name: keycloak-secret
key: db_password
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /realms/master
port: 8080
resources:
limits:
cpu: "4"
memory: 2048M
requests:
cpu: "2"
memory: 1024M核心的环境变量如下表所示:
| 环境变量 | 取值 | 说明 |
|---|---|---|
| KC_HOSTNAME | test.keycloak.org | 访问 Keycloak 时的域名 |
| KC_HTTP_PORT | 8080 | HTTP 端口 |
| KC_HTTPS_PORT | 8443 | HTTPS 端口 |
| KC_HTTPS_CERTIFICATE_FILE | /mnt/certificates/tls.crt | 生产环境需通过 HTTPS 访问,这里是之前部署的 SSL 证书 |
| KC_HTTPS_CERTIFICATE_KEY_FILE | /mnt/certificates/tls.key | 生产环境需通过 HTTPS 访问,这里是之前部署的 SSL 证书 |
| KC_DB | mysql | 数据库类型,这里用 mysql |
| KC_DB_URL | jdbc:mysql://mysql-db-service.mysql:3306/keycloak?characterEncoding=UTF-8 | 数据库连接地址,这里通过之前部署的 mysql 的 Service 进行访问 |
| KC_DB_USERNAME | root | mysql账号 |
| KC_DB_PASSWORD | testpassword | mysql密码 |
| KC_HEALTH_ENABLED | true | 开启健康检查 |
| KC_METRICS_ENABLED | true | 开启监控指标 |
| KC_CACHE | ispn | |
| KC_CACHE_STACK | kubernetes | |
| KC_PROXY | passthrough | |
| KEYCLOAK_ADMIN | admin | 初始admin账号 |
| KEYCLOAK_ADMIN_PASSWORD | changeme | 初始admin密码 |
| KC_TRUSTSTORE_PATHS | /var/run/secrets/kubernetes.io/serviceaccount/ca.crt | 用于访问 k8s 集群 |
部署:
$ kubectl apply -f keycloak.yaml -n dev
$ kgp -n dev
NAME READY STATUS RESTARTS AGE
keycloak-f6cff9b6c-lc6bj 1/1 Running 0 115s
keycloak-f6cff9b6c-nbm2w 1/1 Running 0 115s登录验证:
使用上面的指定端口 30008 进行访问

登录 MySQL 控制台可以看到初始化了很多表和基本数据:

登录成功后页面:

02 配置 Keycloak
1)创建一个名为eric-realm的新Realm,Realm 在 Keycloak 中代表租户:

2)创建用户 eric:

3)设置用户密码:

Eric用户首次登录,因 Temporary 开启了,首次登录需要修改密码:
登录Keycloak的地址为
https://${Keycloak 服务域名}/realms/${用户所在realm}/account。


4)Client端配置:
Client为请求Keycloak对用户进行身份验证的客户端。用户设置完成后,需完成Client的设置。


设置Valid redirect URIs为http://* ,用于设置浏览器登录成功后有效的重定向URL,本例的http://* 表示匹配所有HTTP重定向的网址。然后单击Save。

5)添加 Client Scope:在多个客户端之间共享的一组通用协议Mapper和Role。

添加 client scope 到 client:

6)验证 Client 登录
以下命令需要替换成你的 keycloak 域名、realm 名称、用户名、用户密码、以及 client secret。
curl -ks -X POST http://localhost:30008/realms/eric-realm/protocol/openid-connect/token \
-d grant_type=password -d client_id=eric-client \
-d username=eric -d password=password -d scope=openid \
-d client_secret=WYK8RN2k5VQoARy87LnXpPi27h0cPCSP
成功后,可以获取到 access_token:

7)token 过期时长配置:

03 集成 Keycloak 和 自定义扩展
配置好 keycloak 后,我们还要进行如下集成工作:
1、前端界面自定义一个符合企业的 SSO 界面,使用用户名和密码登录,请求上面keycloak提供的获取 access_token 接口
2、批量用户导入:之前看到如果一个新建,数据量大的话,简直。。。初步看有两种解决思路,通过 admin console 获取到 create user 的 api 接口和参数,通过代码批量进行创建;还有一种方式通过 MySQL 数据库直接导入;以上两种方式没有亲自做过,但是落地可行性应该没问题。
3、Two-factor 的支持:目前官网来看,只支持Google Authenticator 和 FreeOTP,需要额外进行定制。我简单查询了下,网络上已经有解决方案了,但是我本地没有发送短信、邮件的服务器,暂时无法验证,感兴趣的可以看下:https://github.com/dasniko/keycloak-2fa-sms-authenticator
4、多种密码策略的支持

总结
使用 Keycloak 可以快速搭建起来一个 SSO 单点登录系统,同时基于 MySQL 进行数据的持久化,就算 k8s deployment 应用 crash 或者 强制将应用删除等,当我们重新启动应用时,数据还是可以通过 DB 进行加载,不会出现数据丢失的情况,同时使用 K8S 云原生技术,可以根据企业的用户体量和未来持续增长的数据量,进行动态的副本扩容,真正做到企业级高可用的 SSO 系统。
千万要切记,官方的 demo 不用用于生产环境!!!用于个人学习即可。
Keycloak 有非常多的有点,但个人认为会有如下几点不足:
1、终究使用的现有的工具,不一定满足企业对于安全的所有诉求,而且企业对于安全相关一般都是集团统一制定,需要严格遵守,这点非常重要。
2、扩展定制化有一定的技术门槛,需要阅读该平台的开源代码,从而进行代码扩展,然后打包成 Docker 镜像的等
往期文章推荐
参考资料
什么是 SSO?- 单点登录简介 - AWS (amazon.com)
架构设计推荐阅读
如果你对企业级单点登录(SSO)、微服务鉴权体系与安全性设计感兴趣,推荐阅读:
- 单点登录系统(SSO)自建方案总结
- 微服务鉴权设计:SpringBoot 3.x + OAuth2
- 微服务鉴权源码解析:SpringBoot 3.x + OAuth2
- 使用 Sa-Token 平替 Spring Security,告别繁琐的认证与鉴权
👉 查阅更多企业级系统设计与工程实战,欢迎访问 架构设计专栏完整目录。


