天天看点

基于SAML协议 实现SP 单点登录基于SAML协议及SpringSecurity 实现单点登录 SAML SP端

基于SAML协议及SpringSecurity 实现单点登录 SAML SP端

一. 什么是SAML协议

首先还是简单介绍一下什么是SAML协议:

SAML 即安全断言标记语言,英文全称是 Security Assertion Markup Language。它是一个基于 XML 的标准,用于在不同的安全域(security domain)之间交换认证和授权数据。在 SAML 标准定义了身份提供者 (identity provider) 和服务提供者 (service provider),这两者构成了前面所说的不同的安全域。 SAML 是 OASIS 组织安全服务技术委员会(Security Services Technical Committee) 的产品。

SAML(Security Assertion Markup Language)是一个 XML 框架,也就是一组协议,可以用来传输安全声明。比如,两台远程机器之间要通讯,为了保证安全,我们可以采用加密等措施,也可以采用 SAML 来传输,传输的数据以 XML 形式,符合 SAML 规范,这样我们就可以不要求两台机器采用什么样的系统,只要求能理解 SAML 规范即可,显然比传统的方式更好。SAML 规范是一组 Schema 定义。

可以这么说,在 Web Service 领域,schema 就是规范,在 Java 领域,API 就是规范。

没用过SpringSecurity 的点这里这里有官方文档

二. SAML的作用及相关定义

1. 作用

认证声明:声明用户是否已经认证,通常用于单点登录。

属性声明:声明某个 Subject 所具有的属性。

授权决策声明:声明某个资源的权限,即一个用户在资源 R 上具有给定的 E权限而能够执行 A 操作。

想要更深刻的了解SAML协议的 ,看这里,这里附上(浅夏、未央)有关于SAML协议的详细讲解(有一、二、三章节)

2. 组成

SP(Service Provider): 向用户提供正式商业服务的实体,通常需要认证一个用户的身份;

IDP(Identity Provider): 提供用户的身份鉴别,确保用户是其所声明的身份

断言 (Assertions) 即信息:断言是在 SAML 中用来描述认证的对象,包括用户在什么时间、以什么方式被认证,还可以包括一些扩展信息,比如用户的 Email 地址和电话。

协议 (Protocol) 即通信:协议规定如何执行不同的行为。这些行为被细化成一些列的 Request 和 Response 对象,而在这些请求和相应的对象中包含了行为所特别需要的信息。比如,认证请求协议(AuthnRequest Protocol)就规定了一个 SP 如何请求去获得一个被认证的与用户。

绑定 (Binding) 即传输:绑定定义了 SAML 信息如何使用通信协议被传输的。比如,HTTP 重定向绑定,即声明 SAML 信息将通过 HTTP 重定向消息传输;再比如 SAML SOAP 绑定,声明了通过 SOAP 来传递 SAML 消息。比如上面 AuthnRequest 就声明了 Http-POst 绑定

元数据 (MetaData):SAML 的元数据是配置数据,其包含关于 SAML 通信各方的信息,比如通信另一方的 ID、Web Service 的 IP 地址、所支持的绑定类型以及通信中实用的密钥等等。

三. SAML流程分析

基于SAML协议 实现SP 单点登录基于SAML协议及SpringSecurity 实现单点登录 SAML SP端

此图片说明了以下步骤。

1.用户尝试访问WebApp1。

2.WebApp1 生成一个 SAML 身份验证请求。SAML 请求将进行编码并嵌入到SSO 服务的网址中。包含用户尝试访问的 WebApp1 应用程序的编码网址的 RelayState 参数也会嵌入到 SSO 网址中。该 RelayState 参数作为不透明标识符,将直接传回该标识符而不进行任何修改或检查。

3.WebApp1将重定向发送到用户的浏览器。重定向网址包含应向SSO 服务提交的编码 SAML 身份验证请求。

4.SSO(统一认证中心或叫Identity Provider)解码 SAML 请求,并提取 WebApp1的 ACS(声明客户服务)网址以及用户的目标网址(RelayState 参数)。然后,统一认证中心对用户进行身份验证。统一认证中心可能会要求提供有效登录凭据或检查有效会话 Cookie 以验证用户身份。

5.统一认证中心生成一个 SAML 响应,其中包含经过验证的用户的用户名。按照 SAML 2.0 规范,此响应将使用统一认证中心的 DSA/RSA 公钥和私钥进行数字签名。

6.统一认证中心对 SAML 响应和 RelayState 参数进行编码,并将该信息返回到用户的浏览器。统一认证中心提供了一种机制,以便浏览器可以将该信息转发到 WebApp1 ACS。

7.WebApp1使用统一认证中心的公钥验证 SAML 响应。如果成功验证该响应,ACS 则会将用户重定向到目标网址。

8. 用户将重定向到目标网址并登录到 WebApp1。

四.Git中的开源项目

1.完整的SAML SP及IDP实现通信的简单Demo地址

https://github.com/OpenConext/Mujina
           

2.基于Spring Security实现的SAML SP(注意这里只有SP) 项目地址

https://github.com/vdenotaris/spring-boot-security-saml-sample.git
           

3.这里是我自己基于上一条实现的配置版SP的Demo,小白代码比较辣鸡欢迎大神指点

https://github.com/jwu09188/saml-sso-sp-spring-security
           

这个项目是做成一个简单的SP 框架 内部不具备启动类 想要使用 只能新建项目实现外部作为jar包引用,注意(自己的启动类上记得加上bean扫描注解扫描)需要自己配置yml,列子如下:

server:
  port: 9090
logging:
  file: logs/file.log
  level:
    com:
      jiujin:
        web:
          saml: DEBUG
    org:
      opensaml: DEBUG
      springframework:
        security:
          saml: DEBUG
sso:
  saml:
    sp:
      # Entity ID of the SP  
      sp_entity_id: com:saml:spring:sp
      # Certificate
      sp_Certificate: MIIEzTCCAzWgAwIBAgIUdsfVXEJJbdL+K7FmAiBlXgLEg/owDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NJQ0hVQU4xEDAOBgNVBAcMB0NIRU5HRFUxDzANBgNVBAoMBkpJVUpJTjELMAkGA1UECwwCUkQxEDAOBgNVBAMMB0NHV1lDT00xEzARBgkqhkiG9w0BCQEWBFVVVVUwHhcNMjAwNzE1MDgyMTU3WhcNMzAwNzE1MDgyMTU3WjB2MQswCQYDVQQGEwJDTjEQMA4GA1UECAwHU0lDSFVBTjEQMA4GA1UEBwwHQ0hFTkdEVTEPMA0GA1UECgwGSklVSklOMQswCQYDVQQLDAJSRDEQMA4GA1UEAwwHQ0dXWUNPTTETMBEGCSqGSIb3DQEJARYEVVVVVTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBANWpWb41MrSLIWsdelCYRxySClXcvEF6l69f+EX877xY89EVH48xnckH6u4IciFXGaAavRR2RLOe4uBf+RU8Kk9+EuJL5tFVYOo4Wf0p4c6Bf62IhiUOpDKPTaaGCnDBR2ZiC6enpMYbMHxJ/HSbWCoBodH81le7NBekE0YNtGLaesX9so5mYbVURnKhpM5oX0OAo20wvZBomK76Zi6pEj9zJzzbCahWtkkutJLf2Z8DQHweEEouKzoo3USNpD7/VdRjvMw/mJwAIPupipJvQH7s2jMkt/CDFdd7SQr+CxccVPC1phkBQygwDK4SZok0qhQv4FFpSKWUVQTUPMw37cPG2dZ+Up4bPYp48tmDp1qP1PPpbmNGUBqrwR6B9vQ635xrr2By7i9PlkzUz2BQ3hvlXZ39aW4cmzRomN3AldWuoj7nzEdyeJJI5/8zs7ETqInnVqrEjKwU4bkSZu6hZR5+YKgr1KZ37itXryyXxv324g1QLZ7FMaXtlNmTRIF7BwIDAQABo1MwUTAdBgNVHQ4EFgQUQQzvKnZNr/6tZaDBEjcoCN5fiuYwHwYDVR0jBBgwFoAUQQzvKnZNr/6tZaDBEjcoCN5fiuYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAYEAYwrZZ+OgsVXVJlYVHHVrOLXOvaee/c3DFarpwu1VW3hsRWOToGrSMybly0atY5pAYT/rka6rDHW/8W1RB0uaDLI8HYcwyH/7VQvq3uqO8QO/QAbXUAQYjAQSexIu0DV2JFw/SQN4KOt5j/A4gL4Cw4bg0ZosHbH40ZHgeo49MLvyYDzXk1SbwFVO1HonAlEtElcrwcvYLNiqr7Arvb9tb8pmDQIR94LwfNEc4jikcuAOH5Lh/G3tiaewQdkxeUPG+HXgwR8OV51dVTOkOhJVQeioE1ZUKniIcuukCDY/vFBihYCy+Pfver0C39ECllu9vfYuSogfBQ09c5HPkDG0Q428RHDHQbKLP9K3pd6Vw3x2a5Yj3SgcDQ19I5IChS6WmIlU4pTfljWc1kfc+wQ/Fp7jXXzEz9gOVPtf9840SJX3119g9d5T19mH8mj5MbeiY10BOAJzZXQNeWcNBEJu7yNQ6/gBoCaZXAYI6xLZk7oFmZNGpjmCbw9BhtbJ6WDZ
      # Passphrase of the keystore
      sp_passphrase: secret
      # private key
      sp_private_key: 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
      # Resource URL for the idp metadata idp  
      idp_metadata_url: idp的metadata地址  
      # IDP Discovery Service fill in what you need to be found controller
      idp_discovery_service: /saml-sso/sp/login-redirect
      # jump address after successful login url
      redirection_after_successful_url: https://www.baidu.com
           

注意!!配置文件中的 idp_discovery_service: /saml-sso/sp/login-redirect发现服务这里配置的是 你需要被idp 发现的 自己定义的controller

私钥和证书的生成方式不会的小伙伴可以点击这里有(拖鞋公子)的生成教程

前缀由你自己定义的本机端口示例:

localhost:9090/saml-sso/sp/metadata
           
SP metadata获取地址: .../saml-sso/sp/metadata
登出地址: .../saml-sso/sp/logout-redirect
登录地址: .../saml-sso/sp/login
           

以上地址皆可在 WebSecurityConfig 类 samlFilter() 方法中自行修改为自己需要的,注意:方法中传入的 samlEntryPoint()方法 你自己需要点进去看有没有设置的有地址 有的话也需要修改

/**
     * 定义安全筛选器链,以便通过使用SAML 2.0支持SSO身份验证
     *
     * @return Filter chain proxy
     * @throws Exception
     */
    @Bean
    public FilterChainProxy samlFilter() throws Exception {
        List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/login/**"),
                samlEntryPoint()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/logout-redirect/**"),
                samlLogoutFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/metadata/**"),
                metadataDisplayFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/assert"),
                samlWebSSOProcessingFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/single-logout/**"),
                samlLogoutProcessingFilter()));
        chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml-sso/sp/login-redirect/**"),
                samlIDPDiscovery()));
        return new FilterChainProxy(chains);
    }
           

指定登出成功后自己需要跳转地址修改 successLogoutHandler() 方法,设置下文中 successLogoutHandler.setDefaultTargetUrl(); 设置自己需要的地址

@Bean
    public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
        SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
        successLogoutHandler.setDefaultTargetUrl("/saml-sso/sp/login-redirect");
        return successLogoutHandler;
           

还有最后一点需要注意的就是

项目中的其他方法就不全部介绍了项目里面都有注释

这里是本人的第一篇博客只是粗略研究了基于SAML的单点登录应用,认知有限,不对之处请各位前辈指点。同时借此博文分享我的学习心得,抛砖引玉。