使用onelogin开源工具库java-saml-toolkit实现基于SAML2的SSO

1.为什么使用SAML2

saml是用于web浏览器单点登录的基于XML的标准,由OASIS安全服务技术委员会定义。该标准自2002年以来一直存在,但近来由于其以下优点而变得流行:

  • 可用性-从门户或Intranet的一键式访问,深层链接,消除密码和自动续订会话,使用户的生活更轻松。
  • 安全性-SAML基于强大的数字签名进行身份验证和完整性,是世界上最大,最具安全意识的企业所依赖的安全单点登录协议。
  • 速度-SAML速度很快。
    一种浏览器重定向就是将用户安全登录到应用程序所需的全部操作。
  • 网页仿冒防范-如果您没有应用程序密码,就不会被欺骗在虚假的登录页面上输入密码。
  • IT友好-SAML简化了IT的使用寿命,因为它集中了身份验证,提供了更大的可见性并使目录集成更加容易。

2.SAML Java工具包使我们可以将Java应用程序转换为可以连接到IdP(身份提供者)的SP(服务提供者)。

支持:

  • SSO和SLO(由SP启动和由IdP启动).
  • 断言和nameId加密.
  • 断言签名.
  • 消息签名: AuthNRequest, LogoutRequest, LogoutResponses.
  • 启用 断言消费者服务(ACS) 端点.
  • 启用 单一注销服务(SLS) 端点.
  • 发布SP metadata(可以签名).

3.开始使用

1.工具包引入

Maven引入方式

1
2
3
4
5
<dependency>
<groupId>com.onelogin</groupId>
<artifactId>java-saml</artifactId>
<version>2.5.0</version>
</dependency>

lib方式引入

只需要 把 java-saml.jar 与java-saml-core.jar 添加即可(可以通过拉下来的项目直接打包 支持源码修改以适应具体使用情况)

2.使用java-saml-toolkit所提供的简单SP demo工程

git上面获取demo项目

项目包含三哥模块,其中core是核心包, toolkit是对外工具集合,samples是项目提供的SP demo 工程.

samples项目介绍

  • resources 目录下的onelogin.saml.properties 是该工具包的核心配置文件(所有配置都在这边文件里,正式项目中需要放入class 根目录即可读取到)
  • index.jsp Web应用程序的首页。
  • dologin.jsp SP启动的SSO端点。
  • dologout.jsp SP启动的SLO端点。
  • acs.jsp服务提供商声明消费者服务端点。
  • attrs.jsp显示从SAMLResponse收集的属性。
  • sls.jsp服务提供者单一注销服务端点。
  • metadata.jsp发布SP元数据。

4.IDP 服务搭建

这里我们使用oneLogin 提供的IDP服务来进行开发测试SAML协议,该服务需要注册开发者账户后获取.下面将介绍如何搭建

1.在OneLogin中创建应用连接器
  • 登录 OneLogin.
  • Go to Apps > Add Apps.
  • 搜索 SAML Test Connector.
  • 选择 SAML Test Connector (IdP w/ attr) app.
  • 如果需要,请编辑显示名称。如果使用demo1应用程序,请输入demo1
  • 接受默认值并单击保存。
2.在onelogin.saml.properties中配置(IDP)身份提供者参数
  • 从SSO选项卡中复制值,并将其粘贴到onelogin.saml.properties的“ idp”(身份提供者,以onelogin.saml2.idp开头的参数)部分,如下所示
复制SSO标签字段值 settings.json位置
Issuer URL onelogin.saml2.idp.entityid
SAML 2.0 Endpoint (HTTP) onelogin.saml2.idp.single_sign_on_service.url
SLO Endpoint (HTTP) onelogin.saml2.idp.single_logout_service.url
X.509 Certificate > View Details onelogin.saml2.idp.x509cert

保存配置后进入下一步

3.在onelogin.saml.properties中定义(SP)服务提供商的参数

以我们demo项目根路径(http://localhost:8080/)为例 我们的具体参数为以下

对于onelogin.saml2.sp.nameid格式,将unspecified更改为emailAddress.在正式项目中根据实际情况来进行配置,这里是目前是OneLogin使用的值。

保存配置后进入onelogin连接器的配置选项卡,然后将值从onelogin.saml.properties复制到``配置’’选项卡字段中,如下所示。

复制settings.json值 配置选项卡字段
onelogin.saml2.sp.assertion_consumer_service.url ACS (Consumer) URL
onelogin.saml2.sp.assertion_consumer_service.url Recipient
onelogin.saml2.sp.single_logout_service.url Single Logout URL
onelogin.saml2.sp.entityid Audience
  • RelayState 参数可以不用填写 在发起sso代码处可以指定
  • 现在,将ACS(消费者)URL验证器设置为。*。
    最后进行保存.

IDP端的配置可以实时修改保存(在 正式项目环境中我们通常 用metadata文件来进行配置交互)

4.部署运行samples项目

5.onelogin.saml.properties配置文件解读

以下是我的demo环境 的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# 该配置表示加密环境 正式中为true(只要是报文加密都为true)
onelogin.saml2.strict = true
# debug 模式 调试日志打印
onelogin.saml2.debug = true

# SP端配置
# 协议配置 基本上默认 没有特殊修改

# SP entityId
# Identifier of the SP entity (must be a URI)
onelogin.saml2.sp.entityid = http://localhost:8080/metadata.jsp

# SP 断言解析服务地址
# Specifies info about where and how the <AuthnResponse> message MUST be
# returned to the requester, in this case our SP.
# URL Location where the <Response> from the IdP will be returned
onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/acs.jsp

# SAML protocol binding to be used when returning the <Response>
# message. Onelogin Toolkit supports for this endpoint the
# HTTP-POST binding only
onelogin.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST

#单点登出 服务地址 主要是提供给 IDP端用于接收登出响应的
# Specifies info about where and how the <Logout Response> message MUST be
# returned to the requester, in this case our SP.
onelogin.saml2.sp.single_logout_service.url = http://localhost:8080/sls.jsp

# SAML protocol binding to be used when returning the <LogoutResponse> or sending the <LogoutRequest>
# message. Onelogin Toolkit supports for this endpoint the
# HTTP-Redirect binding only
onelogin.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect

# nameID 格式 一般使用 unspecified 默认参数
# Specifies constraints on the name identifier to be used to
# represent the requested subject.
# Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported
onelogin.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress


# sp端用于报文加密签名使用到的证书
# 这里我们可以自签的x509格式证书 也可以使用pem格式通过 以下方式转换(我使用的是https证书 other下载而来的)
# 公钥转换 openssl x509 -in x509cert.pem -text -out Cert.pem
# 私钥转换 openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem
# 证书粘贴注意不要前后注释 并保持在一行上 证书解析对空格换行符敏感

# Usually x509cert and privateKey of the SP are provided by files placed at
# the certs folder. But we can also provide them with the following parameters
onelogin.saml2.sp.x509cert =MIICRDCCAa2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADA/MQswCQYDVQQGEwJjbjEOMAwGA1UECAwFaHViZWkxDDAKBgNVBAoMA1RTTDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTIwMDMwOTE0MzAxN1oXDTIxMDMwOTE0MzAxN1owPzELMAkGA1UEBhMCY24xDjAMBgNVBAgMBWh1YmVpMQwwCgYDVQQKDANUU0wxEjAQBgNVBAMMCTEyNy4wLjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsi27eDSbpKh4J3snBktGjLxSDVotU3BEwuz9AZs8rXzC6aZmfLa7E6TOgq8+aaaOgjMGkSCotsLv91REvD/lnc4wsO4AXRePzL4duPFyJZK/XHxpKS+kSLredQwgt5yN0KeJotXOor5HKuGV8lS9yIYRkxpFA8Rglr1NS2WQSy0CAwEAAaNQME4wHQYDVR0OBBYEFJaUIDCkzQqhVE08KDn8WiSrPvvIMB8GA1UdIwQYMBaAFJaUIDCkzQqhVE08KDn8WiSrPvvIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQENBQADgYEAJhLq5AQerT29AdoYUIvyxo8ce8BS2qqp+WU6dnWB8pUEQ163nC1goPZfE7gKRjpR3MdGU4FWz1PAe4ZAolcvGbqAcOdd4IxEFcXQam1QWEvGaRCflA5KlvhdPyQglhaM5EnuwJ4eFry9VgFFgmaFtBuq+aPo6o2W+E6DQfsXIXY=

# Requires Format PKCS#8 BEGIN PRIVATE KEY
# If you have PKCS#1 BEGIN RSA PRIVATE KEY convert it by openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem
onelogin.saml2.sp.privatekey =MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALItu3g0m6SoeCd7JwZLRoy8Ug1aLVNwRMLs/QGbPK18wummZny2uxOkzoKvPmmmjoIzBpEgqLbC7/dURLw/5Z3OMLDuAF0Xj8y+HbjxciWSv1x8aSkvpEi63nUMILecjdCniaLVzqK+RyrhlfJUvciGEZMaRQPEYJa9TUtlkEstAgMBAAECgYA4zCE1aTewHk/m7ff7pqU/mYxiWzuVnHUe2eKwz2ZhOyL6ziNfX/R0h5WYzPoNPZ2x3UgbnRiK4csSwVcD1y5/PZzMoXaedxEl7ertKUkomvQQVPz8SAEyzlufqxmQVAdNJIW3EZlZq/YDtEv5ZYEXHxDi5cE65Kf7rgxHlFWBoQJBAOj1jfTfCbkyhefnTp7TDrMHAaORWUi1h0BUAiIvfE7oHXE4O0x+R6dTn8w80bP0ZdD7GssAqLxv13MRDq6j8rUCQQDDzSc7/2UdykO5MliTg9/W5riHYuFgY6M0S4+Eq/diSaUyF5H36gGkr5YVcBhpdz0iayiKO2oXuaYVPKVn6GmZAkA8vOERKiG/3nNZPk6aTE710GrV+ax8r4+e7whLX3Qaopwii9WyIO6PqtbsCiNmtt7g+MdIjFhyIPrcbmMUl3xNAkBXHqkeYPy0zJJljKksubiW/gGM+8ocATlUw2oQNhPUf8ApEaO5Ez238QhucXnrM0rYTaW0G8uQ0uG7AUj3esnBAkB7jjXJloxpWaz/cT8Ystqe/oL6JKgyXsbHj9nVYnKwXCZY1TM9X9tE3nP9pyTO7w0Np4WadkH1goyVKRpkdMbL

# IDP端配置

# IDP entityId
# Identifier of the IdP entity (must be a URI)
onelogin.saml2.idp.entityid = https://app.onelogin.com/saml/metadata/2edb5038-be70-40f5-ad3b-2de9d00ab1a3

# SSO endpoint info of the IdP. (Authentication Request protocol)
# URL Target of the IdP where the SP will send the Authentication Request Message
onelogin.saml2.idp.single_sign_on_service.url = https://westinfosoft-dev.onelogin.com/trust/saml2/http-post/sso/2edb5038-be70-40f5-ad3b-2de9d00ab1a3
# SAML protocol binding to be used when returning the <Response>
# message. Onelogin Toolkit supports for this endpoint the
# HTTP-Redirect binding only
onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect

# SLO endpoint info of the IdP.
# URL Location of the IdP where the SP will send the SLO Request
onelogin.saml2.idp.single_logout_service.url =https://westinfosoft-dev.onelogin.com/trust/saml2/http-redirect/slo/1095020

# Optional SLO Response endpoint info of the IdP.
# URL Location of the IdP where the SP will send the SLO Response. If left blank, same URL as onelogin.saml2.idp.single_logout_service.url will be used.
# Some IdPs use a separate URL for sending a logout request and response, use this property to set the separate response url
onelogin.saml2.idp.single_logout_service.response.url =

# SAML protocol binding to be used when returning the <Response>
# message. Onelogin Toolkit supports for this endpoint the
# HTTP-Redirect binding only
onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect

# 正式项目中 sp端都是从SP_metadata 中获取 的
# Public x509 certificate of the IdP
onelogin.saml2.idp.x509cert =MIID6zCCAtOgAwIBAgIUFzBUjmLFkpZwWqy5YSpvAcWFUOswDQYJKoZIhvcNAQEFBQAwSjEVMBMGA1UECgwMd2VzdGluZm9zb2Z0MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxGjAYBgNVBAMMEU9uZUxvZ2luIEFjY291bnQgMB4XDTIwMDMwOTEyMjIwMVoXDTI1MDMwOTEyMjIwMVowSjEVMBMGA1UECgwMd2VzdGluZm9zb2Z0MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxGjAYBgNVBAMMEU9uZUxvZ2luIEFjY291bnQgMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2ooweqHRLI/L7SPe1DrlvdyeRQGUnmXbBPZ/lj6pWEBhu6mgwHDLcC+4wknZKQqq4EGCtoE9Tk1/bMRDxU7kPdzyZfhh/W3M8TDIB7gR/nbkMKTPBfcC2xNQ7n2gK6q9801B6zwcAoPhdfkV0M6O9mIMlqwscLG3Xyrn/+82wrpMSVl85tlkS9BprzYVdre6B599QAcTSh3WSE9cbIKyVxb27XlfuRcmbSYD5EW67PtlutOE03WlQKagX6HHrnpdIUXZWSt8xjXnVxBINZeq8GPU4kow6glLZG6jrd2kh7qTa1meCHxdtjjvJXpsJASKlizlmUCPjpKtncdju6aTQIDAQABo4HIMIHFMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOsEW3B51SXIjuFbKqN3d5eaMTJsMIGFBgNVHSMEfjB8gBTrBFtwedUlyI7hWyqjd3eXmjEybKFOpEwwSjEVMBMGA1UECgwMd2VzdGluZm9zb2Z0MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxGjAYBgNVBAMMEU9uZUxvZ2luIEFjY291bnQgghQXMFSOYsWSlnBarLlhKm8BxYVQ6zAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQEFBQADggEBABaMiNIq/SYKeYezFvKJx7RB9eECSzbyBZYjpYt55XtJV0UWKaHvjvtRiT7XTgqQ/S9uUFPBqOLk09jBAOt1UiLELyuzX6KFC0ghwL6y7BLWL/W7AsxUQCiOAimyGUMTxAcjvFsqWQX3n6eSUr9y0LWqVz9esTj0joxApbejRNiW7Y4Mz/b1OEHT40UN9HxIC2IvQ5/tiU/c84wopOOeHtxb/hYKk1HQnPY2tfkJvKqJJZY8SIG8GlEPlVfwcm1mRrts8VMvbysTAsOJdLhLGLh8SsFkZ6jTTRed3XU+fwKARgg25+MsAQbsaVuXvMOUFlpYwcBhmmU5VseEPpJt3HU=

# 以下是 指纹模式的配置 不过官方不建议使用 应为hash碰撞的问题
# Instead of use the whole x509cert you can use a fingerprint
# (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
# or add for example the -sha256 , -sha384 or -sha512 parameter)
#
# If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
# let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512
# 'sha1' is the default value.
# onelogin.saml2.idp.certfingerprint = 3E:3B:0D:FA:F2:80:B2:0E:95:46:36:07:9A:78:BD:04:CC:76:CE:A8
# onelogin.saml2.idp.certfingerprint_algorithm = sha1


# Security settings

#安全配置 在演示项目中使用不多 不过在正式环境中 需要注意开启对应的加密项

# Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
# will be encrypted.
onelogin.saml2.security.nameid_encrypted = false

#认证请求的加密
# Indicates whether the <samlp:AuthnRequest> messages sent by this SP
# will be signed. [The Metadata of the SP will offer this info]
onelogin.saml2.security.authnrequest_signed = false

#登出请求的加密
# Indicates whether the <samlp:logoutRequest> messages sent by this SP
# will be signed.
onelogin.saml2.security.logoutrequest_signed = false

#登出响应的加密
# Indicates whether the <samlp:logoutResponse> messages sent by this SP
# will be signed.
onelogin.saml2.security.logoutresponse_signed = false

# Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest> and
# <samlp:LogoutResponse> elements received by this SP to be signed.
onelogin.saml2.security.want_messages_signed = false

# Indicates a requirement for the <saml:Assertion> elements received by this SP to be signed.
onelogin.saml2.security.want_assertions_signed = false

# Indicates a requirement for the Metadata of this SP to be signed.
# Right now supported null (in order to not sign) or true (sign using SP private key)
onelogin.saml2.security.sign_metadata =

# Indicates a requirement for the Assertions received by this SP to be encrypted
onelogin.saml2.security.want_assertions_encrypted = false

# Indicates a requirement for the NameID received by this SP to be encrypted
onelogin.saml2.security.want_nameid_encrypted = false

# Authentication context.
# Set Empty and no AuthContext will be sent in the AuthNRequest
# You can set multiple values (comma separated them)
onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac:classes:Password

# Allows the authn comparison parameter to be set, defaults to 'exact'
onelogin.saml2.security.onelogin.saml2.security.requested_authncontextcomparison = exact


# Indicates if the SP will validate all received xmls.
# (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true).
onelogin.saml2.security.want_xml_validation = true

# Algorithm that the toolkit will use on signing process. Options:
# 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
# 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
onelogin.saml2.security.signature_algorithm = http://www.w3.org/2000/09/xmldsig#rsa-sha1

# Organization
onelogin.saml2.organization.name = SP Java
onelogin.saml2.organization.displayname = SP Java OA
onelogin.saml2.organization.url = http://
onelogin.saml2.organization.lang =

# Contacts
onelogin.saml2.contacts.technical.given_name = Technical Guy
onelogin.saml2.contacts.technical.email_address = technical@example.com
onelogin.saml2.contacts.support.given_name = Support Guy
onelogin.saml2.contacts.support.email_address = support@@example.com

6.SP端 代码解读

keyStores

Auth构造函数支持从KeyStore读取SP公共证书/私钥的功能。
必须为KeyStoreSettings对象提供KeyStore,别名和KeyEntry密码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.FileInputStream;
import java.security.KeyStore;
import com.onelogin.saml2.Auth
import com.onelogin.saml2.model.KeyStoreSettings

String keyStoreFile = "oneloginTestKeystore.jks";
String alias = "keywithpassword";
String storePass = "changeit";
String keyPassword = "keypassword";

KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keyStoreFile), storePass.toCharArray());

KeyStoreSettings keyStoreSettings = new keyStoreSettings(ks, alias, keyPassword);
Auth auth = new Auth(KeyStoreSettings keyStoreSetting);

动态配置

可以以编程方式建立设置。您可以从其他来源(例如文件,数据库或生成的值)加载值。SettingsBuilder类公开了fromValues(Map <String,Object> samlData)方法,该方法可让您动态构建设置。密钥字符串与属性文件中的相同

1
2
3
4
5
6
7
8
9
10
11
Map<String, Object> samlData = new HashMap<>();
samlData.put("onelogin.saml2.sp.entityid", "http://localhost:8080/java-saml-tookit-jspsample/metadata.jsp");
samlData.put("onelogin.saml2.sp.assertion_consumer_service.url", new URL("http://localhost:8080/java-saml-tookit-jspsample/acs.jsp"));
samlData.put("onelogin.saml2.security.want_xml_validation",true);
samlData.put("onelogin.saml2.sp.x509cert", myX509CertInstance);

SettingsBuilder builder = new SettingsBuilder();
Saml2Settings settings = builder.fromValues(samlData).build();
//实例化您编写的Auth类
Auth auth = new Auth(settings, request, response);

关于 HttpRequest

java-saml依赖于javax.servlet:servlet-api,而Auth和ServletUtils类使用HttpServletRequest和HttpServletResponse对象。如果要使用与javax.servlet.http不同的任何内容,则需要基于HTTP请求/响应的新表示重新实现Auth和ServletUtils。

1.发起单点登录

用于向IDP发送AuthNRequest

1
2
Auth auth = new Auth(request, response);
auth.login();

AuthNRequest将根据安全设置“ onelogin.saml2.security.authnrequest_signed”以签名或未签名的形式发送。然后,IdP将把SAML响应返回给用户的客户端。然后,使用此信息将客户端转发到SP的属性消费者服务。我们可以为登录函数设置一个“ returnTo” URL参数,并将其转换为“ RelayState”参数:

1
2
3
//指定登录成功后跳转的地址
String targetUrl = "https://github.com/onelogin/java-saml";
auth.login(targetUrl);

SP 端点

三个重要的端点,sp元数据(SP_metadata),ACS(断言解析服务),SLS(单点登出响应解析服务)

SP Metadata

这段代码将根据设置文件中提供的信息提供SP的XML元数据文件。

1
2
3
4
5
6
7
8
9
10
11
12
Auth auth = new Auth();
Saml2Settings settings = auth.getSettings();
String metadata = settings.getSPMetadata();
List<String> errors = Saml2Settings.validateMetadata(metadata);
if (errors.isEmpty()) {
out.println(metadata);
} else {
response.setContentType("text/html; charset=UTF-8");
for (String error : errors) {
out.println("<p>"+error+"</p>");
}
}

Attribute Consumer Service(ACS)

此代码处理IdP通过用户客户端转发到SP的SAML响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Auth auth = new Auth(request, response);
//具体的 响应断言解析
auth.processResponse();
if (!auth.isAuthenticated()) {
out.println("Not authenticated");
}

List<String> errors = auth.getErrors();
if (!errors.isEmpty()) {
out.println(StringUtils.join(errors, ", "));
if (auth.isDebugActive()) {
String errorReason = auth.getLastErrorReason();
if (errorReason != null && !errorReason.isEmpty()) {
out.println(auth.getLastErrorReason());
}
}
} else {
Map<String, List<String>> attributes = auth.getAttributes();
String nameId = auth.getNameId();
String nameIdFormat = auth.getNameIdFormat();
String sessionIndex = auth.getSessionIndex();
String nameidNameQualifier = auth.getNameIdNameQualifier();
String nameidSPNameQualifier = auth.getNameIdSPNameQualifier();

//关键参数
session.setAttribute("attributes", attributes);
session.setAttribute("nameId", nameId);
session.setAttribute("nameIdFormat", nameIdFormat);
session.setAttribute("sessionIndex", sessionIndex);

session.setAttribute("nameidNameQualifier", nameidNameQualifier);
session.setAttribute("nameidSPNameQualifier", nameidSPNameQualifier);

String relayState = request.getParameter("RelayState");

if (relayState != null && relayState != ServletUtils.getSelfRoutedURLNoQuery(request)) {
response.sendRedirect(request.getParameter("RelayState"));
} else {
if (attributes.isEmpty()) {
out.println("You don't have any attributes");
}
else {
Collection<String> keys = attributes.keySet();
for(String name :keys){
out.println(name);
List<String> values = attributes.get(name);
for(String value :values) {
out.println(" - " + value);
}
}
}
}
}

在尝试获取属性之前,请检查用户是否已通过身份验证。如果用户未通过身份验证,则将返回一个空的Map。例如,如果我们在auth.processResponse之前调用getAttributes,则getAttributes()将返回一个空Map。

Single Logout Service (SLS)

以下代码处理注销请求和注销响应。

1
2
3
4
5
6
7
8
9
10
Auth auth = new Auth(request, response);
auth.processSLO();
List<String> errors = auth.getErrors();
if (errors.isEmpty()) {
out.println("Sucessfully logged out");
} else {
for(String error : errors) {
out.println(error);
}
}

如果SLS端点收到注销响应,则该响应将得到验证,并且HttpRequest的会话可能会关闭。如果SLS端点接收到注销请求,则该请求将得到验证,会话将关闭,并且注销响应将发送到IdP的SLS端点。如果我们不希望该processSLO破坏会话,则将keepLo​​calSession参数作为true传递给processSLO方法。

2.发起单点登出

用于发送Logout Request到IdP

注意 此方式 是通过SP端发起的单点登出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Auth auth = new Auth(request, response);

String nameId = null;
if (session.getAttribute("nameId") != null) {
nameId = session.getAttribute("nameId").toString();
}
String nameIdFormat = null;
if (session.getAttribute("nameIdFormat") != null) {
nameIdFormat = session.getAttribute("nameIdFormat").toString();
}
String nameidNameQualifier = null;
if (session.getAttribute("nameidNameQualifier") != null) {
nameIdFormat = session.getAttribute("nameidNameQualifier").toString();
}
String nameidSPNameQualifier = null;
if (session.getAttribute("nameidSPNameQualifier") != null) {
nameidSPNameQualifier = session.getAttribute("nameidSPNameQualifier").toString();
}
String sessionIndex = null;
if (session.getAttribute("sessionIndex") != null) {
sessionIndex = session.getAttribute("sessionIndex").toString();
}
auth.logout(null, nameId, sessionIndex, nameIdFormat);

将根据安全设置“ onelogin.saml2.security.logoutrequest_signed”以签名或未签名的形式发送注销请求。IdP将通过用户客户端将注销响应返回到SP的单一注销服务。最终在sp的sls中获取登出结果

7 工作流

  • 运行我们示例项目。首次访问主视图http//localhost:8080/index.jsp时,您可以选择登录并返回同一视图,或登录并重定向到属性视图(属性)。
  • 当您单击链接时,:
  • 1。在第一个链接中,我们被重定向到/dologin.jsp视图。AuthNRequest发送到IdP,我们在IdP上进行身份验证,然后将响应发送到SP,特别是Assertion Consumer Service视图:/acs.jsp。在此验证SAMLResponse,提取NameID和用户属性并将其存储在会话中。注意,将RelayState参数设置为启动该过程的URL,即dologin.jsp url,但是我们没有将用户重定向到该视图,而是在/acs.jsp视图上显示了用户数据。
  • 2。在第二个链接中,我们使用“ attrs” GET参数重定向到/dologin.jsp视图。AuthNRequest通过/attrs.jsp视图作为RelayState参数发送到IdP,我们在IdP上进行身份验证,然后将响应发送到SP,特别是到断言消费者服务视图:/acs.jsp。在那里验证了SAMLResponse,提取了NameID和用户属性并将其存储在会话中,然后将我们重定向到RelayState视图,attrs.jsp视图,在该视图中从会话中读取用户数据并进行提示。单一注销功能可以通过两种方法进行测试。
  • 3。SLO由SP发起。单击SP上的“注销”链接,之后,我们将重定向到/dologout.jsp视图,在该视图中,将注销请求发送到IdP,IdP上的会话关闭,并向SP答复注销响应(已发送)到“单一注销服务”端点)。SP的SLS端点/sls.jsp处理注销响应,如果有效,则关闭本地应用程序的用户会话。请注意,SLO工作流在SP处开始和结束。
  • 4。SLO由IdP发起。在这种情况下,该操作在IdP端发生,注销过程在IdP上启动,它向SP(SLS端点,/ sls.jsp)发送注销请求。SP的SLS端点处理注销请求,如果有效,则关闭本地应用程序上的用户会话,然后将注销响应发送到IdP(发送到IdP的SLS端点)。IdP接收注销响应,对其进行处理并在IdP上关闭会话。请注意,SLO工作流程在IdP处开始和结束。

8.相关文档和工具地址

IDP 连接器搭建文档

java-saml工具库代码地址

saml助手网站

saml调试谷歌插件 SAML-tracer


使用onelogin开源工具库java-saml-toolkit实现基于SAML2的SSO
https://www.xuwx.top/2020/09/03/使用onelogin开源工具库java-saml-toolkit实现基于SAML2的SSO/
作者
Shine_ssr
发布于
2020年9月3日
许可协议