使用 Axis 和 Flex 构建和查询安全的 SSO 服务






4.87/5 (4投票s)
本文档将介绍如何在 Flex 客户端和基于 Axis 的服务之间使用单点登录 (Single Sign-On) 技术进行通信。
引言
SSO 是 Single Sign-On 的缩写,是独立软件系统中一种访问控制的属性。通过 SSO,用户只需登录一次,即可访问所有已配置为支持它的系统。SSO 有用的一个领域是客户端应用程序访问多个安全服务提供者的情况。为了建立这种支持 SSO 的环境,基础设施的所有参与者(身份/服务提供者和客户端应用程序)都应该知道如何使用 SSO 技术相互操作。
本文基于该领域的研究,介绍了如何创建具有高级安全控制策略的 Axis Web 服务,以及如何使用 SSO 的强大功能编写可以访问和授权这些服务的 Flex 客户端应用程序。
本文引用的所有示例及其完整源代码都包含在本文附加的 zip 档案中。为最大程度地减小文章篇幅,一些配置相关信息已从文章移至档案根目录下的 Appendix.rtf 文档。
模型交互
我们的模型基础设施将由一系列 Web 服务表示,这些 Web 服务都与同一个身份提供者进行交互。在客户端成功登录身份提供者后,它就可以使用连接到该身份提供者的任何服务,而无需在每个服务上单独登录。
为了设置基础设施,我们将定义要使用的具体技术。Sun OpenSSO 将作为身份提供者。服务提供者将使用 Apache Axis2 for Java 编写。上述所有服务都将在 Apache Tomcat 服务器上运行。客户端应用程序将使用 Flex 技术编写。
现在,我们需要定义客户端、服务提供者和身份提供者之间如何交互(授权和检索数据)的方式。可能的流程之一如下:
- 未经授权的客户端调用 Web 服务提供者。
- Web 服务检查客户端请求中是否存在身份提供者的安全令牌(包含已登录用户信息对象)。由于没有令牌,服务会向客户端发送“需要登录”消息。
- 客户端接收到“需要登录”消息,显示登录对话框,然后直接使用用户提供的凭据调用身份提供者。
- 如果该用户在身份提供者已注册,它将为该用户创建一个安全令牌,并将该令牌发回客户端应用程序。
- 当客户端应用程序收到安全令牌后,它会重新尝试请求 Web 服务(将令牌包含在请求中)。
- Web 服务接着将收到的令牌传递给身份提供者。
- 身份提供者通过使用提供的令牌,检查该令牌属于哪个用户,获取分配给该用户的角色集,并将该角色集发送给 Web 服务。
- Web 服务根据其自身的角色到功能映射,决定是否允许该角色访问所请求的功能。如果用户获得允许,则将客户端请求的数据发送回客户端。否则,将发送相应的错误消息。
准备环境
我们需要一个准备好的环境来继续。为简单起见,我们将使用单个 Tomcat 安装,身份提供者和服务提供者在此运行,并且 Flex 客户端应用程序也从此执行。在实际环境中,这些应用程序可能位于不同的机器上,对于这种配置,需要执行一些额外的任务(请参阅附录中的OpenSSO 身份服务配置和Flash 安全策略配置以获取详细信息)。
首先,我们需要运行 Sun OpenSSO(身份服务)和 Apache Axis2(Apache SOAP 实现),它们都部署在 Tomcat 服务器上(请参阅附录中提供的OpenSSO 和 Axis2 安装)。
我们的示例 Web 服务使用多个角色,这些角色之后应授予身份提供者用户。如果您不熟悉角色管理,请参阅附录中的在 OpenSSO 中创建用户和组。为使示例 Web 服务正常工作,需要在 OpenSSO 管理中创建的角色如下:
- mathematician
- summator
- multiplier
- divider
- increaser
- decreaser
- editor
- upper
- lower
安全的 Axis2 服务
我们需要做的第一件事是建立 OpenSSO 身份提供者和 Axis 服务之间的通信。为了方便起见,我们编写了一个特殊的服务器端库。有关安装此库的详细信息,请参阅附录中的自定义 SSO 库安装说明。借助该库(请参阅by.intexsoft.ws.sso.OpenSSOServiceInteractor),我们将从身份提供者获取当前登录用户的 SSO 角色列表。
一旦我们有了身份提供者角色列表,我们就可以定义服务方法的安全设置。
一种保护服务方法的方式是在每个方法中添加代码部分,以验证当前登录用户配置文件中是否存在特定角色。
我们决定简化此功能并通过引入权限来扩展它。我们将权限定义为特殊的服务器端安全实体,它们使得可以在 Web 服务上直接进行更灵活的安全控制。权限分配给角色(一个角色可以有多个权限)。
在执行方法调用的安全检查时,系统会查找方法上定义的权限所属的角色,如果该角色已授予当前登录用户,则调用该方法。因此,与 SSO 角色相比,权限将使我们能够提供更细粒度的安全控制,并最大限度地减少在引入新服务时在身份提供者上管理角色的需求。
在我们的实现中,身份提供者角色和服务权限之间的映射使用一个简单的属性文件进行管理,该文件位于 webapps/axis2/WEB-INF/lib/sso.jar/config/PermissionChecker_mapping.properties。此文件包含键值对,其中角色名作为键,逗号分隔的权限名称列表作为值。默认定义了以下角色到权限的映射:
加法 | subtract | multiply | divide | upper | lower | |
Mathematician | ||||||
Summator | ||||||
乘数 | ||||||
Divider | ||||||
Increaser | ||||||
Decreaser | ||||||
编辑器 | ||||||
Upper | ||||||
Lower |
为简化为服务方法定义权限的实际过程,我们决定通过 Java 注解来实现。为了提供此类安全管理,我们认为使用 Spring 框架很有用。此功能需要的功能包含在我们之前提到的自定义库 sso.jar 中。
可以通过以下语法使用注解来保护服务类的每个方法:@Secured("PERMISSION_NAME")
,其中 PERMISSION_NAME 可以是权限映射文件中存在的任何权限。
在开发示例服务时,我们遇到了一个问题,即 Axis2 无法从类中获取必要的数据,因为 Spring 会为类创建一个代理(请参阅 http://issues.apache.org/jira/browse/AXIS2-3258)。为解决此问题,我们为服务类引入了一个接口。
public interface MathService
{
Double add(Double n1, Double n2);
}
public class MathServiceImpl implements MathService
{
public Double add(Double n1, Double n2) {return n1 + n2;}
}
这是一个安全服务类的示例。
public interface MathService
{
@Secured("by.intexsoft.add")
Double add(Double n1, Double n2);
}
请注意,当服务准备好部署时,需要执行一些额外的配置步骤。有关更多信息,请参阅附录中的配置和打包服务。
示例服务位于存档的 services 文件夹中。要将服务部署到 Tomcat 服务器,请将 AAR 文件放置在 webapps/axis2/WEB-INF/services 目录下。
有两个示例服务可用。
- MathService - 提供一组数学运算
- StringService - 可以将提供的字符串在小写和大写之间转换
下表列出了示例服务函数以及调用它们所需的权限。
服务名称 | 操作 (Operation) | 权限 |
数学服务 | Add | by.intexsoft.add |
Subtract | by.intexsoft.subtract | |
Multiply | by.intexsoft.multiply | |
Divide | by.intexsoft.divide | |
字符串服务 | Upper | by.intexsoft.upper |
Lower | by.intexsoft.lower |
从 Flex 访问 SSO 启用的服务
我们的下一步是使 Flex 应用程序能够访问受保护的服务。此应用程序应通过身份提供者进行身份验证,并使用 SSO 技术调用受保护的 Web 服务方法。现有的 Flex 服务访问 API 不提供此类功能。因此,我们创建了自己的库包,公开了一个新 API。为方便使用,我们将新 API 的设计尽可能贴近现有的 Flex 服务访问 API。
要访问安全的 Axis2 Web 服务,您只需要将存档中位于 flex-sso-library 文件夹的 SSOWebServiceClient.swc 库添加到您的项目中(建议将此库作为 RSL 使用)。
现在,您可以使用 by.intexsoft.soap.sso.SSOEnabledServiceHelper
类,从 Flex 应用程序内部将 SSO 调用传递给服务(有关类构造函数参数的详细信息,请参阅附录中的SSOEnabledServiceHelper 参数)。以下是该辅助类的用法示例:
private var webServiceHelper: SSOEnabledServiceHelper;
private function onGetResultClick(): void
{
webServiceHelper = new SSOEnabledServiceHelper(wsdlUrl.text,
"add", onResult, onFault);
webServiceHelper.call(number1.value, number2.value);
}
private function onResult(data: Object, token: Object): void
{
additionResult.text = data.result;
}
private function onFault(data: Object, token: Object): void
{
Alerter.showError(data.fault.faultString, "Error...");
}
重要提示:必须在应用程序中保留 SSOEnabledServiceHelper
的一个链接,以防止垃圾回收器在 `call` 方法执行过程中将其回收。
当调用 `webServiceHelper.call` 方法时,SOAP 请求将被发送到服务提供者。如果服务需要客户端进行身份验证,并且用户尚未登录(SOAP 消息不包含安全令牌),则会提示用户在身份提供者上进行身份验证(用户会看到登录和密码对话框)。使用提供的凭据,系统会联系身份提供者(在本例中为 OpenSSO),并在成功后自动重新将 SOAP 调用发送到服务。如果从服务接收到数据,则执行 `onResult` 处理程序。如果服务无响应,则执行 `onFault` 处理程序。
如果 SSOEnabledServiceHelper
的功能对您来说不够,并且您需要更多灵活性,那么您可以通过更详细的方式访问服务。请参阅附录中的以高级方式访问服务。有关更详细的信息,请参阅源存档中 flex-sso-library 文件夹内的库源代码。
在设计客户端应用程序时,我们遇到了一个众所周知的问题:Flash Player 会忽略任何状态码大于 200 的消息(有关详细信息,请参阅 https://bugs.adobe.com/jira/browse/FB-13337)。为解决此问题,我们创建了一个服务器端过滤器,该过滤器将状态码 500 转换为状态码 200。此过滤器位于我们已经部署的 sso.jar 库中。我们只需要在 Web 服务所在的 Tomcat 服务器的 webapps/axis2/WEB-INF/web.xml 文件(在 <web-app>
标签下)中定义其配置。
<filter>
<filter-name>StatusCode500To200Filter</filter-name>
<filter-class>by.intexsoft.ws.filter.StatusCode500To200Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>StatusCode500To200Filter</filter-name>
<servlet-name>AxisServlet</servlet-name>
</filter-mapping>
示例 Flex 客户端应用程序可在存档的 flex-client 文件夹中找到。基于 Flex 的客户端应用程序应从 Web 服务器执行。在我们的示例中,我们将它放置在同一个 Tomcat 服务器上,方法是将 flex-client 文件夹复制到 Tomcat 的 web-app 目录。您也可以查看客户端应用程序的源代码以深入了解其编写方式。
观看 SSO 实况
最后,我们可以测试本文描述的功能。继续进行的检查表如下:
- 我们已成功运行 OpenSSO 身份提供者,并在其上定义了示例角色。
- 我们已配置 Axis2 安装,并部署了示例服务(MathService 和 StringService)。PermissionChecker_mapping.properties 文件包含示例身份提供者角色和示例服务权限之间的映射。
- 示例客户端应用程序(SSOWSPrototype)可在 Web 服务器上使用。
让我们进入 OpenSSO 管理界面,创建一个新用户“test”,并为其分配“summator”和“upper”角色。
现在,我们通过打开以下 URL 来执行客户端:https://:8080/flex-client/SSOWSPrototype.html。
客户端界面屏幕将打开。
通过此界面,我们可以执行示例数学和字符串服务提供的所有操作。在加法区域输入一些数字,然后单击“=”号按钮。
系统将联系数学服务,并响应我们尚未登录。因此,将显示登录对话框。在此处输入测试用户凭据,然后单击“登录”按钮。
系统在身份提供者上授权我们,然后再次联系数学服务,以便“加法”操作的结果会立即显示。
现在,我们将尝试使用第二个服务(字符串服务)。在文本区域中键入一些文本(例如,小写“sso”),然后单击“UPPER”按钮。
您将看到,提供的字符串已成功转换为大写。这是 SSO 工作方式的一个真实示例。我们已登录数学服务,并且可以使用字符串服务,而无需重复登录过程,使用相同的凭据。
现在,尝试单击“lower”按钮。您将收到一条错误消息:“用户没有必要的权限”。这是因为我们用户拥有的“upper”角色没有使用字符串服务的“lower”操作的权限。
您可以通过进入身份提供者管理界面,更改当前用户的角色,并观察客户端如何响应服务权限的变化,来进一步进行实验。您还可以使用提供的源代码来扩展它们的功能,或者在设计自己的项目时获取灵感。
历史
这是本文的第一个修订版。