非微软技术的信息卡依赖方资源
1. 信息卡交换流程
在使用信息卡登录网站时,会经历一系列的步骤,具体如下:
1. 用户访问网站的登录页面。
2. 向依赖方发送 HTTP GET 请求以获取登录页面。
3. 浏览器收到包含 OBJECT 标签的 HTML 登录页面。
4. 用户点击“使用信息卡登录”按钮,触发身份选择器评估依赖方的策略(在 OBJECT 标签中指定),并找出符合策略的信息卡子集。
5. 身份选择器显示符合条件的信息卡子集。
6. 用户选择要使用的信息卡。
7. 身份选择器向卡发行方(身份提供者)请求安全策略。
8. 收到安全策略。
9. 用户向身份提供者进行身份验证,并请求包含所需声明的安全令牌。
10. 收到安全令牌。
11. 通过 HTTPS POST 将登录页面(包含安全令牌)发送到依赖方。
12. 依赖方返回带有 cookie 的 HTTP 重定向到主页。
13. 向依赖方发送 HTTP GET 请求以获取主页 URL。
14. 浏览器收到 HTML 页面。
15. 用户已通过身份验证,可以访问网站。
以下是该流程的 mermaid 流程图:
graph LR
A[用户访问登录页面] --> B[发送 HTTP GET 请求]
B --> C[返回 HTML 登录页面]
C --> D[用户点击登录按钮]
D --> E[身份选择器评估策略]
E --> F[显示符合条件的信息卡]
F --> G[用户选择信息卡]
G --> H[请求安全策略]
H --> I[收到安全策略]
I --> J[用户身份验证并请求令牌]
J --> K[收到安全令牌]
K --> L[HTTPS POST 登录页面]
L --> M[返回重定向到主页]
M --> N[发送 HTTP GET 请求主页]
N --> O[返回 HTML 页面]
O --> P[用户访问网站]
2. 创建登录页面
依赖方的第一步是创建登录页面,该页面用于指定期望的声明和令牌类型。页面包含一个类型为“application/x-informationCard”的 OBJECT 元素,其中包含 PARAM 子元素,如下表所示:
| Parameter | Definition |
| — | — |
| tokenType | 指定依赖方请求的令牌类型 |
| requiredClaims | 指定依赖方所需的声明 |
| optionalClaims | 指定依赖方可以使用的可选声明 |
以下是一个示例 HTML 页面,指定了 SAML 1.0 令牌和所需的声明:
<html>
<head>
<title>Sample Relying Party</title>
<style>BODY {color:#000;font-family: verdana, arial, sans-serif;}</style>
</head>
<body>
<h2>Log In with Information Card</h2>
<form name='infocard' method='post' action='./infocard' enctype='application/x-www-form-urlencoded'>
<img src="img/card_off.png"
onMouseOver="this.src='img/card_on.png';"
onMouseOut="this.src='img/card_off.png';"
onClick="infocard.submit()"/>
<OBJECT type="application/x-informationCard" name="xmlToken">
<PARAM Name="tokenType" Value="urn:oasis:names:tc:SAML:1.0:assertion">
<PARAM Name="requiredClaims" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
<PARAM Name="optionalClaims" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth http://schemas.xmlsoap.org/ws/2005/05/identity/claims/gender">
</OBJECT>
</form>
<br>Click the image above to log in with an information card.<br>
</body>
</html>
以下是一个 PHP 脚本示例,用于生成类似的登录页面:
<div id="login" style="font-family: arial; font-size: 2em;">
<h2> Log In with an InfoCard</h2>
<left>
<form name="infocard" id="infocard" method="post" action="informationcard-processing.php">
<img src="img/card_off.png"
onMouseOver="this.src='img/card_on.png';"
onMouseOut="this.src='img/card_off.png';"
onClick="infocard.submit()"/>
<OBJECT type="application/x-informationCard" name="xmlToken">
<PARAM Name="tokenType" Value="urn:oasis:names:tc:SAML:1.0:assertion">
<PARAM Name="requiredClaims" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
<PARAM Name="optionalClaims" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth http://schemas.xmlsoap.org/ws/2005/05/identity/claims/gender">
</OBJECT>
</form>
</left>
</div>
当用户点击提交按钮时,会触发上述流程中的步骤 4 到 11。在步骤 11 中,浏览器会提交包含安全令牌的表单,此时依赖方服务器代码开始解密令牌并提取声明。
3. Java 依赖方示例:xmldap.org 的 Java 依赖方
3.1 获取代码
首先,需要从 Google Code 下载 xmldap.org 依赖方的代码,链接为:http://code.google.com/p/openinfocard/source 。
3.2 创建 Java Servlet
以下是完整的 Servlet 代码:
package org.xmldap.rp.servlet;
import org.xmldap.exceptions.InfoCardProcessingException;
import org.xmldap.exceptions.KeyStoreException;
import org.xmldap.rp.Token;
import org.xmldap.util.KeystoreUtil;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.PrivateKey;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class SampleRelyingParty extends HttpServlet {
private PrivateKey privateKey = null;
public void init(ServletConfig config) throws ServletException {
try {
//Get the private key used for decryption - must correspond to the server's SSL cert
KeystoreUtil keystore = new KeystoreUtil("xmldap.jks", "storepassword");
privateKey = keystore.getPrivateKey("certalias", "keypassword");
} catch (KeyStoreException e) {
throw new ServletException("Error accessing PrivateKey", e);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//Get the encrypted token from the request
String encryptedXML = request.getParameter("xmlToken");
if ((encryptedXML == null) || (encryptedXML.equals("")))
throw new ServletException("No token provided");
//Decryt the token
Token token = new Token(encryptedXML, privateKey);
//Check the token's validity
if ((token.isSignatureValid()) && (token.isConditionsValid()) && (token.isCertificateValid())) {
//Print out the provided claims
PrintWriter out = response.getWriter();
Map claims = token.getClaims();
out.println("<h2>You provided the following claims:</h2>");
Set keys = claims.keySet();
Iterator keyIter = keys.iterator();
while (keyIter.hasNext()) {
String name = (String) keyIter.next();
String value = (String) claims.get(name);
out.println(name + ": " + value + "<br>");
}
out.close();
}
} catch (InfoCardProcessingException e) {
throw new ServletException(e);
}
}
}
3.3 代码分析
-
命名空间引用 :除了 Java 提供的命名空间外,该 Servlet 还引用了 xmldap.org 的四个命名空间,如下表所示:
| Namespace | Contains |
| — | — |
| org.xmldap.util.KeyStoreUtil | 用于创建密钥库的 KeystoreUtil 类 |
| org.xmldap.exceptions.KeyStoreException | 可能由 KeystoreUtil 触发的 KeyStoreException 异常 |
| org.xmldap.rp.Token | 用于在依赖方创建和查询解密令牌的 Token 类 |
| org.xmldap.util.exceptions.InfoCardProcessingException | 在依赖方处理信息卡时可能触发的 InfoCardProcessingException 异常 | -
初始化方法 :在
init方法中,使用KeystoreUtil类创建一个新的密钥库,并从中获取用于解密的私钥,将其赋值给成员变量privateKey。
public void init(ServletConfig config) throws ServletException {
try {
KeystoreUtil keystore = new KeystoreUtil("xmldap.jks", "storepassword");
privateKey = keystore.getPrivateKey("certalias", "keypassword");
} catch (KeyStoreException e) {
throw new ServletException("Error accessing PrivateKey", e);
}
}
-
处理 POST 请求
:当表单通过 POST 提交到依赖方时,由
doPost方法处理。首先从请求中获取加密的令牌,验证令牌是否存在,然后创建Token对象进行解密。接着检查令牌的有效性,如果有效,则打印出令牌中的声明。
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String encryptedXML = request.getParameter("xmlToken");
if ((encryptedXML == null) || (encryptedXML.equals("")))
throw new ServletException("No token provided");
Token token = new Token(encryptedXML, privateKey);
if ((token.isSignatureValid()) && (token.isConditionsValid()) && (token.isCertificateValid())) {
PrintWriter out = response.getWriter();
Map claims = token.getClaims();
out.println("<h2>You provided the following claims:</h2>");
Set keys = claims.keySet();
Iterator keyIter = keys.iterator();
while (keyIter.hasNext()) {
String name = (String) keyIter.next();
String value = (String) claims.get(name);
out.println(name + ": " + value + "<br>");
}
out.close();
}
} catch (InfoCardProcessingException e) {
throw new ServletException(e);
}
}
4. PHP 依赖方示例:Kim Cameron 的 PHP 简单信息卡演示
4.1 获取代码
可以从 Kim Cameron 的网站下载示例代码:http://www.identityblog.com/wp-content/resources/simple-infocard-demo/simple-infocard-demo-v3.zip 。下载后将其解压到 Web 服务器可访问的目录。
4.2 请求信息卡
infocard-demo.php
文件类似于前面介绍的登录页面,指定了所需的声明和 SAML 1.0 令牌:
<div id="login" style="font-family: arial; font-size: 2em;">
<p>Simple Login Demo</p>
<left>
<form name="ctl00" id="ctl00" method="post" action="infocard-demo-processing.php">
<img src='wp-images/card.jpg' onClick='ctl00.submit()'/>
<OBJECT type="application/x-informationCard" name="xmlToken">
<PARAM Name="tokenType" Value="urn:oasis:names:tc:SAML:1.0:assertion">
<PARAM Name="requiredClaims" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname~CC http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier">
</OBJECT>
</form>
</left>
</div>
4.3 处理令牌
当表单提交时,会将表单内容(包含身份提供者提供的令牌)发送到
infocard-demo-processing.php
进行处理。该文件包含了
infocard-print-binary.php
、
infocard-post-decrypt.php
和
infocard-post-get-claims.php
,这些文件负责解密令牌和提取声明。
以下是
infocard-demo-processing.php
的代码:
include_once("infocard-print-binary.php");
include_once("infocard-post-decrypt.php");
include_once("infocard-post-get-claims.php");
$claims = array();
$token = "";
$error = "";
// Checking that there is a referrer
if (TRUE != array_key_exists('HTTP_REFERER', $_SERVER)){
$error = "infocard-demo-processing.php cannot be accessed directly";
break;
}
// Checking for people who don't know you need to use HTTPS at this point
if (strncmp("https:", $_SERVER["HTTP_REFERER"], 5) != 0){
$error = "infocards currently must be invoked from an https protected page";
break;
}
// Checking to see whether a token was produced
if (!array_key_exists("xmlToken", $_POST)){
$error = "InfoCard not present";
break;
}
// 处理令牌
if (!$tokenContent=stripslashes($_POST["xmlToken"]))
{
$error = "No xml token";
break;
}
if (array_key_exists("decrypted", $_POST) == FALSE){
// Decrypting the token
$error=infocard_post_decrypt($tokenContent, $token);
if ($error != NULL)
{
$error = "infocard_post_decrypt returns $error";
break;
}
}
else{
$token = $tokenContent;
}
// Checking the signature of what's inside - and getting the claims
if ($error = infocard_post_get_claims($token, $claims))
{
break;
}
// Printing the claims
print "<div style=\"font-family: arial; font-size: 1.2em;\">";
foreach ($claims as $name => $value) {
if ($name == "modulusHash")
continue;
print "claim: $name -- $value <br>";
}
print "</div>";
function get_settings($key)
{
if ($key == "infocard_key") {
$retVal = "-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,9266952B733BFBE0
Z4WmpirV4dXvYjNmfSN99Iu4iYzUWa4/CPZG0NParYSVHMOhb4lsS6iISjgniGG9
zhA862KDwsYUjgoyAIXfJAd5Z3hXiyJYdkygF/DUgeQFcwQjsWmkguq27EDHW6nS
TVq7lTGsSxKPzc6HmcR5jEupq9xFLcqrSbC3Jn3lmiRlQYOw1BLcuv3o1WUoQsqb
...........................
+j40vRpPzY6ngd1QNOfd5jkin7sjW1YlsEsRPV8OzEJvNmBZF274Cw==
-----END RSA PRIVATE KEY-----";
}
else if ($key == "infocard_opener"){
$retVal = "1234567";
}
else {
$retVal = NULL;
}
return($retVal);
}
4.4 代码分析
- 验证请求 :首先检查请求是否有 HTTP 引用,并且引用是否通过 SSL 连接(HTTPS)发送信息。然后验证是否提供了令牌。
-
解密令牌
:如果令牌未解密,则使用
infocard_post_decrypt函数进行解密。 -
提取声明
:使用
infocard_post_get_claims函数检查签名并提取声明。 - 打印声明 :遍历声明并打印到页面上。
5. 其他依赖方项目
5.1 Java 依赖方
- JInformationCard :项目网站为 http://informationcard.sourceforge.net ,由德国的 Fraunhofer Institute FOKUS 开发,遵循 BSD 许可证。该项目旨在展示使用 Java 语言编写的信息卡互操作性,支持 Apache Tomcat、Sun Java 网络服务器和 WebSphere Application Server 平台。提供了一个名为 Bio Mini Shop 的示例依赖方应用程序,可在 https://zeno.fokus.fraunhofer.de/BioMiniShop/home.jsp 访问。
- cardspaceauthn 项目 :为 OpenSSO 和 Sun Access Manager 提供信息卡身份验证的插件模块。项目主页为 https://cardspaceauthn.dev.java.net/ ,遵循 Common Development and Distribution License。
5.2 PHP 依赖方
- Apache Authentication Module for CardSpace :由 PingIdentity 在 2007 年发布,是一个开源模块,允许使用 Apache 服务器的应用程序将信息卡作为额外的身份验证机制。可从 http://www.sourceid.org/download/list 下载,遵循 SourceID Open Software License 2.1。
- PamelaWare Add-In for WordPress :为流行的博客引擎 WordPress 提供信息卡支持,基于 Kim Cameron 的 PHP 示例开发。项目主页为 http://pamelaproject.com/pwwp/ 。
5.3 Ruby on Rails 依赖方
- Information Card Ruby :目前唯一的 Ruby on Rails 依赖方项目,由 Microsoft 和 ThoughtWorks 赞助,遵循 BSD 许可证。项目主页为 http://www.codeplex.com/informationcardruby/ ,最新代码可在 http://rubyforge.org/projects/informationcard/ 找到。该项目旨在为 Ruby on Rails 依赖方 Web 应用程序提供集成个人信息卡的插件和支持库。
综上所述,除了微软技术外,还有多种开发平台可以实现信息卡依赖方,每个平台都有相应的示例代码和开源项目可供参考。
6. 信息卡依赖方技术总结
6.1 不同语言依赖方对比
| 语言 | 示例项目 | 特点 | 适用场景 |
|---|---|---|---|
| Java | xmldap.org、JInformationCard、cardspaceauthn | 有成熟的类库和工具支持,适用于大型企业级应用开发 | 对安全性和稳定性要求较高的企业级 Web 应用 |
| PHP | Kim Cameron 示例、Apache Authentication Module for CardSpace、PamelaWare Add - In for WordPress | 代码简洁,易于上手,与 Web 开发结合紧密 | 快速开发的 Web 应用,如博客、小型网站 |
| Ruby on Rails | Information Card Ruby | 基于 Rails 框架,开发效率高,适合快速迭代的项目 | 敏捷开发的 Web 应用,注重开发速度和灵活性 |
6.2 信息卡依赖方开发关键步骤
以下是使用信息卡实现依赖方的关键步骤 mermaid 流程图:
graph LR
A[创建登录页面] --> B[指定所需声明和令牌类型]
B --> C[用户操作触发身份验证流程]
C --> D[获取加密令牌]
D --> E[解密令牌]
E --> F[验证令牌有效性]
F --> G[提取声明]
G --> H[处理声明并完成登录]
具体步骤如下:
1.
创建登录页面
:页面包含
OBJECT
元素,指定
tokenType
、
requiredClaims
和
optionalClaims
。示例代码如下:
<OBJECT type="application/x-informationCard" name="xmlToken">
<PARAM Name="tokenType" Value="urn:oasis:names:tc:SAML:1.0:assertion">
<PARAM Name="requiredClaims" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
<PARAM Name="optionalClaims" Value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth http://schemas.xmlsoap.org/ws/2005/05/identity/claims/gender">
</OBJECT>
- 用户操作触发身份验证流程 :用户点击登录按钮,触发身份选择器评估依赖方策略,选择合适的信息卡。
-
获取加密令牌
:从请求中获取加密的令牌,如 Java 代码中的
request.getParameter("xmlToken")。 -
解密令牌
:使用私钥解密令牌,不同语言有不同的实现方式,如 Java 中的
Token token = new Token(encryptedXML, privateKey)。 -
验证令牌有效性
:检查令牌的签名、条件和证书是否有效,如 Java 中的
token.isSignatureValid()、token.isConditionsValid()和token.isCertificateValid()。 -
提取声明
:从解密后的令牌中提取所需的声明,如 Java 中的
Map claims = token.getClaims()。 - 处理声明并完成登录 :根据提取的声明进行用户身份验证和登录处理,如将声明信息存储到数据库或创建会话。
7. 开发注意事项
7.1 安全性
- 私钥管理 :私钥用于解密令牌,必须妥善保管,避免泄露。在代码中使用私钥时,要确保其安全性,如使用加密存储和访问控制。
-
HTTPS 连接
:信息卡的使用必须通过 HTTPS 连接,确保数据传输的安全性。在开发过程中,要验证请求是否通过 HTTPS 发送,如 PHP 代码中的
strncmp("https:", $_SERVER["HTTP_REFERER"], 5) != 0。
7.2 兼容性
- 库和版本 :不同语言的依赖库有不同的版本要求,要确保使用的库版本兼容。如 PHP 示例代码依赖于 mcrypt 和 OpenSSL 库,且 OpenSSL 版本需 0.9.6 或更高。
- 浏览器支持 :要确保信息卡功能在主流浏览器中正常工作,进行充分的兼容性测试。
7.3 性能优化
- 缓存机制 :对于频繁使用的信息,如安全策略和令牌验证结果,可以使用缓存机制提高性能。
- 代码优化 :优化代码逻辑,减少不必要的计算和数据库查询,提高系统响应速度。
8. 总结
通过以上介绍,我们了解了在 Java、PHP 和 Ruby on Rails 等不同开发平台上实现信息卡依赖方的方法。每个平台都有其独特的优势和适用场景,开发者可以根据项目需求选择合适的技术。在开发过程中,要注意安全性、兼容性和性能优化等方面的问题,确保信息卡依赖方系统的稳定运行。同时,开源项目和示例代码为我们提供了很好的参考,有助于快速上手和开发出高质量的应用程序。
超级会员免费看
8874

被折叠的 条评论
为什么被折叠?



