配置 OAuth 委派授权
Windchill 可作为
ThingWorx 平台上构建的应用程序和混搭的资源提供者参与单一登录 (SSO)。在此用例中,用户可以登录到一个启用 SSO 的
ThingWorx 应用程序,并授权正在与其交互的应用程序来访问其
Windchill 数据。这种 SSO 实施即称为委派授权,因为用户会授权服务提供者 (应用程序) 代表他们从资源提供者 (
Windchill) 那里检索其信息,并且用户只需登录一次即可。有关受支持的 SSO 用例以及在 PTC 产品之间设置 SSO 联合所需配置步骤的完整说明,请参阅
PTC Single Sign-on Architecture and Configuration Overview Guide (《PTC 单一登录架构和配置概述指南》)。
PTC 产品平台 SSO 解决方案依赖基于 OAuth 标准的访问令牌交换来管理产品应用程序之间的委派授权。要在 Windchill 中启用 OAuth,必须对 securityContext.properties 和 Web.xml 这两个 Windchill 文件进行编辑。
OAuth 委派授权的通用配置
• 将 wt.oauth2.token.userNameAttribute 特性的值更新为 IDP 配置中指定的用户属性名称。例如,如果 PingFederate 为 IDP,则其值为 Username。
• 更新 wt.oauth2.token.tokenType 特性以在 OPAQUE 与 JWT 之间切换令牌验证。默认值为 OPAQUE。
• 更新 wt.oauth2.token.scopeAttribute 特性以配置 OPAQUE 和 JWT 令牌的范围属性。默认值为 scope。
OPAQUE 令牌的 OAuth 委派授权配置
要配置与中央授权服务器的连接,并指定由访问令牌和范围保护的系统资源,请按如下所述编辑 securityContext.properties 文件。此特性文件的路径为 <Windchill>\codebase\WEB-INF\security\config\securityContext.properties。
1. 标识哪些资源应受 OAuth 2 令牌保护。受保护资源需从服务提供者处获取有效的 OAuth 2 令牌才能对其授予访问权限。要执行此操作,请在以下字符串后以 Ant 样式的模式列出资源:com.ptc.eauth.identity.oauth2.rs=/oauth/**
例如,要保护 Windchill 文档,请将按如下方式配置该字符串:
com.ptc.eauth.identity.oauth2.rs=/oauth/documents/**
在特性文件中只能指定单个阵列,即只能指定一行。因此,需要定义顶层 URL,以充分涵盖想要由 OAuth 2 令牌保护的所有资源。在步骤 4 中,指定应附加到 OAuth 令牌的范围,以便更好地控制对资源的访问。
2. 标识令牌验证端点。此值应设置为将验证访问令牌的 CAS 服务器。
org.springframework.security.oauth2.provider.token.RemoteTokenServices.checkTokenEndpointUrl=https://{cas-server}
.ptc.com:9031/as/introspect.oauth2
3. 提供为资源提供者创建的 PingFederate (CAS) OAuth 客户端中所指定的客户端 ID 和客户端密码。有关 Windchill OAuth 客户端的信息,请与 CAS 管理员联系。
org.springframework.security.oauth2.provider.token.RemoteTokenServices.clientId=rs_client
org.springframework.security.oauth2.provider.token.RemoteTokenServices.clientSecret={client secret}
4. 至少添加一个特性,以将 WINDCHILL_READ 范围与您想要提供给服务提供者的 Windchill 资源进行配对。
要配置由 WINDCHILL_READ 范围所公开的资源,必须添加相应的特性来定义对 securityContext.properties 文件的这种访问。在 Windchill 中,支持配置单一范围。测试是通过一个名为 WINDCHILL_READ 的范围执行的。在此文件中注册的范围也必须在用于管理 SSO 网络中访问令牌交换的中央授权服务器中以及在将要请求数据的服务提供者应用程序中进行注册。
每个范围特性均由以下几部分组成:
◦ URL 前缀,用于指示受 OAuth 保护的资源,且在存在有效访问令牌的情况下可供访问。例如:
com.ptc.eauth.identity.oauth2.rs.InMemoryResourceScopeService.resourceScopes.
◦ 指向将公开的资源的目录路径。例如,/Windchill/oauth/documents。
◦ Ant 样式的路径模式。例如,/** 公开在任务之前所提供的路径下所有目录中的资源。
◦ 范围值 (名称)。例如,=WINDCHILL_READ。范围值不能包含空格。(空格用于分隔多个范围值,但当前不支持多个范围方案。)
以下是一个示例范围特性,要求 OAuth 访问令牌必须具有 WINDCHILL_READ 范围,才允许传递 /Windchill/oauth/documents 资源数据:
com.ptc.eauth.identity.oauth2.rs.InMemoryResourceScopeService.resourceScopes.
/Windchill/oauth/documents=WINDCHILL_READ
传入的请求必须具有一个访问令牌,且该令牌的必需范围是在给定资源的特性中定义的。这些特性为附加特性,也就是说,如果为同一资源定义了其他范围,则受保护数据的传入请求必须包含已为该资源定义的所有范围。例如,如果除了上述示例范围特性之外,您还创建了另一个特性,且该特性需要 /Windchill/oauth/documents 资源的 "ABC" 范围,则传入的访问令牌将需要 "WINDCHILL_READ" 和 "ABC" 范围才能访问资源。
JWT 令牌的 OAuth 授权配置
要使用 JWT 令牌为 Azure Active Directory 配置 OAuth 授权,请按照下述步骤进行操作:
1. 通过 site.xconf 在 wt.properties 文件中添加下列特性:
wt.jwt.oauth2.token.issueAtTime
|
指定 JWT 访问令牌的发行时间。由此可以确定 JWT 访问令牌的期限。默认值为 900000 millisecond/15min。
|
wt.jwt.oauth2.token.azure.tenantId
|
指定 Tenant ID,这是与您的组织名称或域不同的全局唯一标识符 (GUID)。
|
wt.jwt.oauth2.token.audience
|
指定客户端 ID。
|
wt.jwt.oauth2.token.tokenIssuer
|
将其值指定为 https://sts.windows.net/<tenant_id>/。
|
wt.jwt.oauth2.token.algorithm
|
指定用于验证 JWT 访问令牌签名的算法。默认值为 RS256。
|
wt.jwt.oauth2.token.kidUrl
|
将其值指定为 https://login.microsoftonline.com/<tenant_id>/discovery/v2.0/keys。
|
2. 将 securityContext.properties 文件中 wt.jwt.oauth2.token.jwtIdpType 特性的值更新为受支持的第三方 IDP 标识符 (例如 wt.jwt.oauth2.token.jwtIdpType = azure),该标识符将会发送 JWT 令牌。如果不需要 Azure 特殊验证,则可以选择不为此特性提供任何值。在这种情况下,仅执行标准 JWT 声明验证。
编辑 Web.xml 文件
对 Web.xml 文件执行下面两个编辑操作:
1. 将 securityContext.properties 文件的位置添加到 WEB-INF/web.xml 文件中。
WEB-INF/web.xml 文件必须引用 securityContext.properties 文件。Web.xml 文件位于 <Windchill 安装位置>/codebase/WEB-INF/Web.xml 下。
将此位置添加到 Spring root Web 应用程序上下文位置的参数值中。如果已将 securityContext.properties 文件保存在其默认目录中,则以下示例就是有效的。将路径 WEB-INF/security/config/securityContext.xml 添加到 <param-value>config/mvc/applicationContext.xml</param-value> 中。上下文参数的结构应如下所示:
<context-param>
<description>Location of Spring root web application context</description>
<param-name>contextConfigLocation</param-name>
<param-value>config/mvc/applicationContext.xml
WEB-INF/security/config/securityContext.xml
</param-value>
</context-param>
|
securityContext.properties 文件的位置也在 securityContext.xml 中被引用。如果已更改 securityContext.properties 的目录位置,则必须在 Web.xml 和 securityContext.xml 中更新对新位置的引用。
|
2. 添加 SpringSecurityFilterChain 代码段。
找到 <filter-mapping> 文件中的第一个 Web.xml 标记,并在该标记之前添加以下代码。
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/oauth/*</url-pattern>
</filter-mapping>
找到 <servlet-mapping> 文件中的第一个 Web.xml 标记,并在该标记之前添加以下代码。
<servlet>
<description>Bridge Servlet for Oauth Access</description>
<servlet-name>OauthAuthBridgeServlet</servlet-name>
<servlet-class>wt.servlet.AuthBridgeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>OauthAuthBridgeServlet</servlet-name>
<url-pattern>/oauth/*</url-pattern>
</servlet-mapping>
重新启动 Windchill 和 Apache 服务器
编辑 securityContext.properties 和 Web.xml 文件后,重新启动 Windchill 和 Apache 服务器并执行测试,以验证 Windchill 是否通过 SSO 网络实现授权委派。
自定义 UserAuthenticationConverter 接口
根据自省端点的响应,需要实现 UserAuthenticationConverter 接口才能转换和提取要在访问令牌中使用的用户信息。例如:
• 如果响应格式为如下所示的嵌套用户名声明,请使用 securityContext.xml 文件中的 com.ptc.eauth.identity.oauth2.rs.PingFederateUserAuthenticationConverter。
{
"access_token":
{
"USERNAME":"wcadmin" }
"scope":"WINDCHILL_READ"
"token_type":"urn:pingidentity.com:oauth2:validated_token",
"expires_in";5289,
"client_id";"TestJWTClient",
}
• 如果响应格式为如下所示的扁平结构,则使用预设 com.ptc.eauth.identity.oauth2.rs.IntrospectionUserAuthenticationConverter,而不需要对此文件进行任何更新。
{ "aud":"TEST_AUD",
"scope":"WINDCHILL_READ",
"iss":"TEST_ISS",
"active":true,
"USERNAME":"wcadmin",
"token_type":"Bearer",
"exp":1640609559,
"client_id":"TestJWTClient"
}
对于任何其他格式,都需要根据上述示例中的指导思路,正确读取响应中的用户名声明。您必须更改 extractAuthentication 方法中的代码来提取正确的声明详细信息。必须更新 <WT_HOME>/codebase/WEB-INF/security/config/ securityContext.xml 文件中的自定义 UserAuthenticationConverter,位置如下:
<!--Customize this depending on the exact nature of the Authorization server's "check token" endpoint -->
<bean_id="userTokenConverter" class="com.ptc.eauth.identity.oauth2.rs.PingFederateUserAuthenticationConverter">
<property name="userNameAttribute" value="${wt.oauth2.token.userNameAttribute}"/>
</bean>
使用下面的示例代码:
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
public class IntrospectionUserAuthenticationConverter implements UserAuthenticationConverter {
public static final String DEFAULT_USERNAME_ATTR = "username";
private String userNameAttribute = DEFAULT_USERNAME_ATTR;
public Map<String, ?> convertUserAuthentication(Authentication userAuthentication) { Map<String, String> map = new HashMap<>(); map.put(this.userNameAttribute, userAuthentication.getName()); return map; }
/** * This method extracts user defined claim name from the token * @param map - token returned from introspection end point * @return - user details extracted from token */
public Authentication extractAuthentication(Map<String, ?> map){
Object principal = null;
for (String key : map.keySet()) {
if (key.equalsIgnoreCase(this.userNameAttribute)) { principal = map.get(key);
break;
}
}
if (principal != null)
{
PreAuthenticatedAuthenticationToken response = new PreAuthenticatedAuthenticationToken(principal, null);
response.setDetails(map);
return (Authentication)response; }
return null; }
public void setUserNameAttribute(String userNameAttribute) {
this.userNameAttribute = userNameAttribute;
}
}