一文带你对接对应的Okta单点登录实现接口服务的落地

什么是SAML协议

SAML(Security Assertion Markup Language)是一种基于XML的标准,用于在不同的安全域之间传递身份验证和授权数据。SAML2.0是SAML协议的最新版本,它提供了一种标准的方式来实现单点登录(SSO)和跨域身份验证(Cross-Domain Authentication)。

SAML2.0协议定义了三种角色:身份提供者(Identity Provider,IdP)服务提供者(Service Provider,SP)用户(User) 。其中,身份提供者 是负责认证用户身份的系统,服务提供者是提供服务的系统,用户是需要访问服务的个体。

SAML 2.0协议的流程

  1. 用户向服务提供者发起请求,服务提供者检查用户是否已经登录。
  2. 如果用户没有登录,则服务提供者将用户重定向到身份提供者。
  3. 身份提供者验证用户身份,并生成一个SAML响应,其中包含用户的身份信息和授权信息。
  4. 身份提供者将SAML响应发送给服务提供者。
  5. 服务提供者验证SAML响应的签名,并提取用户的身份信息和授权信息。
  6. 服务提供者使用用户的身份信息和授权信息来授权用户访问服务。

SAML2.0协议的优点

SAML 2.0协议的优点在于它提供了一种标准的方式来实现跨域身份验证和单点登录,从而简化了用户的登录流程,提高了用户体验。此外,SAML 2.0协议还提供了强大的安全性和灵活性,可以满足各种不同的安全需求。

SAML2.0协议的特点

总之,SAML 2.0协议是一种重要的安全标准,它可以帮助企业实现跨域身份验证和单点登录,提高用户体验和安全性。

SAML开发实战指南

建立SAML协议的SP服务提供者-服务

首先,我们需要先建立出来一个具有支持SAML协议的认证的SP(服务提供者)。使用Java工具包使我们可以将Java应用程序转换为可以连接到IdP(身份提供者)的SP(服务提供者)

SP需要支持功能和特性:


建立SAML协议的IDP服务

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

developers.onelogin.com/

当然你也可以通过其他第三方的IDP进行实现也可,例如Okta或者Azure服务都可以,此处暂时不做过多的赘余豁免。


开始对接实现SP服务提供者

Maven引入方式


    com.onelogin
    java-saml
    2.5.0

复制代码

采用OneLogin实现Saml协议认证开发

配置对应的IDP身份的相关参数

默认需要在OneLogin的配置文件onelogin.saml.properties中配置(IDP)身份提供者参数:

SSO的标记属性

Settings的配置属性

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

主要面向的就是配置onelogin.saml.properties的“ idp”(身份提供者,以onelogin.saml2.idp开头的参数)部分。

配置对应的SP身份的相关参数

在OneLogin中定义(SP)服务提供商的参数

SSO的标记属性

Settings的配置属性

Audience

onelogin.saml2.sp.entityid

Single Logout URL

onelogin.saml2.sp.single_logout_service.url

Recipient

onelogin.saml2.sp.assertion_consumer_service.url

ACS (Consumer) URL

onelogin.saml2.sp.assertion_consumer_service.url

RelayState

参数可以不用填写 在发起sso代码处可以指定

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

部署运行SP项目

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

onelogin.saml2.sp.entityid = http://localhost:8080/metadata.jsp
onelogin.saml2.sp.assertion_consumer_service.url = http://localhost:8080/acs.jsp
onelogin.saml2.sp.single_logout_service.url = http://localhost:8000/sls.jsp
复制代码

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

onelogin.saml.properties配置文件解读

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  message MUST be
#  returned to the requester, in this case our SP.
# URL Location where the  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 
# 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  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  or sending the 
# 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 =

# Requires Format PKCS#8   BEGIN PRIVATE KEY	     
# If you have     PKCS#1    convert it by   openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem
onelogin.saml2.sp.privatekey =
复制代码

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 
# 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 
# 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 =

# 以下是 指纹模式的配置 不过官方不建议使用 应为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  sent by this SP
# will be encrypted.
onelogin.saml2.security.nameid_encrypted = false

#认证请求的加密
# Indicates whether the  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  messages sent by this SP
# will be signed.
onelogin.saml2.security.logoutrequest_signed = false

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

# Indicates a requirement for the ,  and
#  elements received by this SP to be signed.
onelogin.saml2.security.want_messages_signed = false

# Indicates a requirement for the  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
复制代码

SP端代码

keyStores

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

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 samlData)方法,该方法可让您动态构建设置,密钥字符串与属性文件中的相同。

Map 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);
复制代码

发起单点登录

用于向IDP发送AuthNRequest

Auth auth = new Auth(request, response);
auth.login();
复制代码

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

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

SP 端点

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

SP Metadata

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

Auth auth = new Auth();
Saml2Settings settings = auth.getSettings();
String metadata = settings.getSPMetadata();
List errors = Saml2Settings.validateMetadata(metadata);
if (errors.isEmpty()) {
   out.println(metadata);
} else {
   response.setContentType("text/html; charset=UTF-8");
   for (String error : errors) {
       out.println("

"+error+"

"); } } 复制代码

Attribute Consumer Service(ACS)

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

Auth auth = new Auth(request, response);
//具体的 响应断言解析
auth.processResponse();
if (!auth.isAuthenticated()) {
   out.println("Not authenticated");
}

List 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> 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 keys = attributes.keySet();
            for(String name :keys){
                out.println(name);
                List values = attributes.get(name);
                for(String value :values) {
                    out.println(" - " + value);
                }
            }
        }
    }
}
复制代码

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

Single Logout Service (SLS)

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

Auth auth = new Auth(request, response);
auth.processSLO();
List 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端发起的单点登出

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的单一注销服务。

展开阅读全文

页面更新:2024-05-21

标签:单点   断言   提供者   属性   接口   证书   身份   协议   参数   代码   用户

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top