文章

单点登录(SSO)系统自建方案总结

一篇文章带你了解单点登录系统,包括不限于:SSO 是什么、SSO 的常用协议、SSO 的登录流程、SSO 的几种常见解决方案以及最后的企业级SSO 系统搭建。

什么是 SSO

单点登录(SSO)是一种身份验证解决方案,可让用户通过一次性用户身份验证登录多个应用程序和网站。这意味着用户只需输入一次用户名和密码,即可访问所有相互信任的系统,而无需在每个系统中单独登录。鉴于当今的用户经常直接从其浏览器访问应用程序,因此组织正在优先考虑改善安全性和用户体验的访问管理策略。SSO 兼具这两方面的优点,因为一旦验证身份,用户就可以访问所有受密码保护的资源,而无需重复登录。

SSO 的主要优点包括:

  1. 用户体验改进:用户只需记住一个用户名和密码,减少了重复登录的麻烦。

  2. 安全性增强:通过集中管理认证信息,可以更好地控制和保护用户数据,降低密码泄露的风险。

  3. 效率提升:减少了因多次登录导致的时间浪费,提高了工作效率。

在 SSO(单点登录)系统中,身份提供者(Identity Provider,IdP)和服务提供者(Service Provider,SP)是两个关键角色。SSO 依赖于身份提供者(Identity Provider,IdP)和服务提供者(Service Provider,SP)之间的信任关系。

  1. 身份提供者(Identity Provider, IdP)

    定义:IdP 是负责验证用户身份并提供身份信息的实体。它管理用户的认证信息(如用户名、密码、令牌等)并提供这些信息给信任它的服务提供者。

    例子:

    • 企业内部 IdP:如 Microsoft Active Directory Federation Services(AD FS),它为企业内部应用提供单点登录服务。

    • 第三方 IdP:如 Google、Facebook、Okta 等,它们为各种第三方应用提供认证服务。

  2. 服务提供者(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 的登录流程图

image-20240629213114963

详细步骤说明:

  • 步骤 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

核心的环境变量如下表所示:

https://www.keycloak.org/server/all-config

环境变量

取值

说明

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 进行访问

image-20240630143915327

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

image-20240630144010516

登录成功后页面:

image-20240630144707022

02 配置 Keycloak

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

image-20240630144933016

2)创建用户 eric:

image-20240630145258720

3)设置用户密码:

image-20240630145439211

Eric用户首次登录,因 Temporary 开启了,首次登录需要修改密码:

登录Keycloak的地址为https://${Keycloak 服务域名}/realms/${用户所在realm}/account

这里我们需要登录:http://localhost:30008/realms/eric-realm/account

image-20240630145744245

image-20240630150340935

4)Client端配置:

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

image-20240630150509502

image-20240630150551097

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

image-20240630150750761

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

image-20240630154135553

添加 client scope 到 client:

image-20240630154246617

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

image-20240630154606639

成功后,可以获取到 access_token:

image-20240630154857707

7)token 过期时长配置:

image-20240630160150548

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、多种密码策略的支持 image-20240630160519944

总结

使用 Keycloak 可以快速搭建起来一个 SSO 单点登录系统,同时基于 MySQL 进行数据的持久化,就算 k8s deployment 应用 crash 或者 强制将应用删除等,当我们重新启动应用时,数据还是可以通过 DB 进行加载,不会出现数据丢失的情况,同时使用 K8S 云原生技术,可以根据企业的用户体量和未来持续增长的数据量,进行动态的副本扩容,真正做到企业级高可用的 SSO 系统。

千万要切记,官方的 demo 不用用于生产环境!!!用于个人学习即可。

Keycloak 有非常多的有点,但个人认为会有如下几点不足:

1、终究使用的现有的工具,不一定满足企业对于安全的所有诉求,而且企业对于安全相关一般都是集团统一制定,需要严格遵守,这点非常重要。

2、扩展定制化有一定的技术门槛,需要阅读该平台的开源代码,从而进行代码扩展,然后打包成 Docker 镜像的等

往期文章推荐

打造高效 K8S 本地环境

打造高效MacOS系统环境

kubernetes Ingress 自动化 HTTPS

参考资料

什么是 SSO?- 单点登录简介 - AWS (amazon.com)

Keycloak 官网

Keycloak Github

Keycloak quickstart github

License:  CC BY 4.0