基于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流程分析

此图片说明了以下步骤。
1.用户尝试访问WebApp1。

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

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

扫描二维码关注公众号,回复: 12395520 查看本文章

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的单点登录应用,认知有限,不对之处请各位前辈指点。同时借此博文分享我的学习心得,抛砖引玉。

猜你喜欢

转载自blog.csdn.net/weixin_45584768/article/details/107792403