SharePoint 2013 低信任提供程序托管的加载项设置指南





5.00/5 (2投票s)
1引言
本文旨在提供设置低信任提供商托管的 SharePoint 加载项的步骤说明,其中外部应用程序托管在 IIS 中。外部应用程序将设置为针对 SharePoint 执行 CRUD(创建/读取/更新/删除)操作,而无需从 SharePoint 内部启动应用程序。
2设置步骤
2.1配置本地 SharePoint 以使用 Azure 访问控制服务 (ACS) 来授权提供商托管的加载项
2.1.1安装 PowerShell 模块
2.1.1.1 Microsoft Online Services Sign-In Assistant
- 从以下位置下载 Microsoft Online Services Sign-In Assistant PowerShell 模块:https://www.microsoft.com/en-us/download/details.aspx?id=41950
- 在您的 SharePoint 2013 环境中安装该模块
2.1.1.2 Microsoft Azure Active Directory
- 从以下位置下载 Microsoft Azure Active Directory PowerShell 模块:
http://connect.microsoft.com/site1164/Downloads/DownloadDetails.aspx?DownloadID=59185
- 在您的 SharePoint 2013 环境中安装该模块
2.1.2创建证书
- 在您的 SharePoint 2013 服务器上,打开 IIS 管理器
- 单击您的服务器名称
- 双击“服务器证书”
- 在右侧单击“创建自签名证书”
- 输入证书的名称,然后按“确定”
- 右键单击证书,然后选择“导出”
- 选择一个位置来保存证书,并输入密码
- 单击“确定”
2.1.3将证书设置为 SharePoint 2013 环境的安全令牌服务 (STS) 证书
- 在您的 SharePoint 2013 服务器上,以管理员身份打开 SharePoint 管理中心 Shell
- 运行以下 PowerShell 脚本
$certPrKPath = "c:\location_of_your_.pfx_file"
$certPassword = "your_password"
$stsCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $certPrKPath, $certPassword, 20
Set-SPSecurityTokenServiceConfig -ImportSigningCertificate $stsCertificate -confirm:$false
2.1.4准备您的 SharePoint 2013 环境以使用 Azure 访问控制服务 (ACS)
- 在您的 SharePoint 2013 服务器上,转到以下路径:C:\windows\system32\windowspowershell\V1.0\modules
- 创建一个名为:Connect-SP-Farm-To-AAD 的新文件夹
- 将以下脚本复制到文本文件中,并将其命名为 Connect-SP-Farm-To-AAD.psm1
function Connect-SPFarmToAAD {
param(
[Parameter(Mandatory)][String] $AADDomain,
[Parameter(Mandatory)][String] $SharePointOnlineUrl,
#Specify this parameter if you don't want to use the default SPWeb returned
[Parameter()][String] $SharePointWeb,
[Parameter()][System.Management.Automation.PSCredential] $O365Credentials,
#Use these switches if you're replacing an existing connection to AAD.
[Parameter()][Switch] $RemoveExistingACS,
[Parameter()][Switch] $RemoveExistingSTS,
[Parameter()][Switch] $RemoveExistingSPOProxy,
#Use this switch if you're replacing the Office 365 SharePoint site.
[Parameter()][Switch] $RemoveExistingAADCredentials,
#Use this switch if you don't want to use SSL when you launch your app.
[Parameter()][Switch] $AllowOverHttp
)
#Prompt for credentials right away.
if (-not $O365Credentials) {
$O365Credentials = Get-Credential -Message "Admin credentials for $AADDomain"
}
Add-PSSnapin Microsoft.SharePoint.PowerShell
#Import the Microsoft Online Services Sign-In Assistant.
Import-Module -Name MSOnline
#Import the Microsoft Online Services Module for Windows Powershell.
Import-Module MSOnlineExtended -force -verbose
#Set values for Constants.
New-Variable -Option Constant -Name SP_APPPRINCIPALID -Value '00000003-0000-0ff1-ce00-000000000000' | Out-Null
New-Variable -Option Constant -Name ACS_APPPRINCIPALID -Value '00000001-0000-0000-c000-000000000000' | Out-Null
New-Variable -Option Constant -Name ACS_APPPROXY_NAME -Value ACS
New-Variable -Option Constant -Name SPO_MANAGEMENT_APPPROXY_NAME -Value 'SPO Add-in Management Proxy'
New-Variable -Option Constant -Name ACS_STS_NAME -Value ACS-STS
New-Variable -Option Constant -Name AAD_METADATAEP_FSTRING -Value 'https://accounts.accesscontrol.windows.net/{0}/metadata/json/1'
New-Variable -Option Constant -Name SP_METADATAEP_FSTRING -Value '{0}/_layouts/15/metadata/json/1'
#Get the default SPWeb from the on-premises farm if no $SharePointWeb parameter is specified.
if ([String]::IsNullOrEmpty($SharePointWeb)) {
$SharePointWeb = Get-SPSite | Select-Object -First 1 | Get-SPWeb | Select-Object -First 1 | % Url
}
#Configure the realm ID for local farm so that it matches the AAD realm.
$ACSMetadataEndpoint = $AAD_METADATAEP_FSTRING -f $AADDomain
$ACSMetadata = Invoke-RestMethod -Uri $ACSMetadataEndpoint
$AADRealmId = $ACSMetadata.realm
Set-SPAuthenticationRealm -ServiceContext $SharePointWeb -Realm $AADRealmId
$LocalSTS = Get-SPSecurityTokenServiceConfig
$LocalSTS.NameIdentifier = '{0}@{1}' -f $SP_APPPRINCIPALID,$AADRealmId
$LocalSTS.Update()
#Allow connections over HTTP if the switch is specified.
if ($AllowOverHttp.IsPresent -and $AllowOverHttp -eq $True) {
$serviceConfig = Get-SPSecurityTokenServiceConfig
$serviceConfig.AllowOAuthOverHttp = $true
$serviceConfig.AllowMetadataOverHttp = $true
$serviceConfig.Update()
}
#Step 1: Set up the ACS proxy in the on-premises SharePoint farm. Remove the existing ACS proxy
#if the switch is specified.
if ($RemoveExistingACS.IsPresent -and $RemoveExistingACS -eq $True) {
Get-SPServiceApplicationProxy | ? DisplayName -EQ $ACS_APPPROXY_NAME | Remove-SPServiceApplicationProxy -RemoveData -Confirm:$false
}
if (-not (Get-SPServiceApplicationProxy | ? DisplayName -EQ $ACS_APPPROXY_NAME)) {
$AzureACSProxy = New-SPAzureAccessControlServiceApplicationProxy -Name $ACS_APPPROXY_NAME -MetadataServiceEndpointUri $ACSMetadataEndpoint -DefaultProxyGroup
}
#Remove the existing security token service if the switch is specified.
if ($RemoveExistingSTS.IsPresent) {
Get-SPTrustedSecurityTokenIssuer | ? Name -EQ $ACS_STS_NAME | Remove-SPTrustedSecurityTokenIssuer -Confirm:$false
}
if (-not (Get-SPTrustedSecurityTokenIssuer | ? DisplayName -EQ $ACS_STS_NAME)) {
$AzureACSSTS = New-SPTrustedSecurityTokenIssuer -Name $ACS_STS_NAME -IsTrustBroker -MetadataEndPoint $ACSMetadataEndpoint
}
#Update the ACS Proxy for OAuth authentication.
$ACSProxy = Get-SPServiceApplicationProxy | ? Name -EQ $ACS_APPPROXY_NAME
$ACSProxy.DiscoveryConfiguration.SecurityTokenServiceName = $ACS_APPPRINCIPALID
$ACSProxy.Update()
#Retrieve the local STS signing key from JSON metadata.
$SPMetadata = Invoke-RestMethod -Uri ($SP_METADATAEP_FSTRING -f $SharePointWeb)
$SPSigningKey = $SPMetadata.keys | ? usage -EQ "Signing" | % keyValue
$CertValue = $SPSigningKey.value
#Connect to Office 365.
Connect-MsolService -Credential $O365Credentials
#Remove existing connection to an Office 365 SharePoint site if the switch is specified.
if ($RemoveExistingAADCredentials.IsPresent -and $RemoveExistingAADCredentials -eq $true) {
$msolserviceprincipal = Get-MsolServicePrincipal -AppPrincipalId $SP_APPPRINCIPALID
[Guid[]] $ExistingKeyIds = Get-MsolServicePrincipalCredential -ObjectId $msolserviceprincipal.ObjectId -ReturnKeyValues $false | % {if ($_.Type -ne "Other") {$_.KeyId}}
Remove-MsolServicePrincipalCredential -AppPrincipalId $SP_APPPRINCIPALID -KeyIds $ExistingKeyIds
}
#Step 2: Upload the local STS signing certificate
New-MsolServicePrincipalCredential -AppPrincipalId $SP_APPPRINCIPALID -Type Asymmetric -Value $CertValue -Usage Verify
#Step 3: Add the service principal name of the local web application, if necessary.
$indexHostName = $SharePointWeb.IndexOf('://') + 3
$HostName = $SharePointWeb.Substring($indexHostName)
$NewSPN = '{0}/{1}' -f $SP_APPPRINCIPALID, $HostName
$SPAppPrincipal = Get-MsolServicePrincipal -AppPrincipalId $SP_APPPRINCIPALID
if ($SPAppPrincipal.ServicePrincipalNames -notcontains $NewSPN) {
$SPAppPrincipal.ServicePrincipalNames.Add($NewSPN)
Set-MsolServicePrincipal -AppPrincipalId $SPAppPrincipal.AppPrincipalId -ServicePrincipalNames $SPAppPrincipal.ServicePrincipalNames
}
#Remove the existing SharePoint Online proxy if the switch is specified.
if ($RemoveExistingSPOProxy.IsPresent -and $RemoveExistingSPOProxy -eq $True) {
Get-SPServiceApplicationProxy | ? DisplayName -EQ $SPO_MANAGEMENT_APPPROXY_NAME | Remove-SPServiceApplicationProxy -RemoveData -Confirm:$false
}
#Step 4: Add the SharePoint Online proxy
if (-not (Get-SPServiceApplicationProxy | ? DisplayName -EQ $SPO_MANAGEMENT_APPPROXY_NAME)) {
$spoproxy = New-SPOnlineApplicationPrincipalManagementServiceApplicationProxy -Name $SPO_MANAGEMENT_APPPROXY_NAME -OnlineTenantUri $SharePointOnlineUrl -DefaultProxyGroup
}
}
- 将 Connect-SP-Farm-To-AAD.psm1 复制到 C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Connect-SP-Farm-To-AAD
- 以管理员身份打开 SharePoint 管理中心 Shell,并运行以下 cmdlet 以验证 Connect-SP-Farm-To-AAD 模块是否已列出
Get-Module –listavailable
- 运行以下 cmdlet 以导入模块
import-module Connect-SP-Farm-To-AAD
- 运行以下 cmdlet 以验证 Connect-SPFarmToAAD 函数是否已列为模块的一部分
Get-Command -module Connect-SP-Farm-To-AAD
- 运行以下 cmdlet 以验证 Connect-SPFarmToAAD 函数是否已加载。
ls function:\ | where {$_.Name -eq "Connect-SPFarmToAAD"}
2.1.5配置您的 SharePoint 2013 环境以使用 Azure 访问控制服务 (ACS)
- 在您从上一步 (2.1.4) 打开的同一个 SharePoint 管理中心 Shell 窗口中,以场管理员身份运行以下脚本
注意:系统将提示您输入租户管理员凭据。请准备好。
Connect-SPFarmToAAD -AADDomain 'your_company_name.onmicrosoft.com' -SharePointOnlineUrl https://your_company_name.sharepoint.com -SharePointWeb http://your_on-prem_web_application -AllowOverHttp
示例
Connect-SPFarmToAAD -AADDomain 'MyO365Domain.onmicrosoft.com' -SharePointOnlineUrl https://MyO365Domain.sharepoint.com -SharePointWeb http://ecmspotsbx –AllowOverHttp
注释
- SharePointWeb:是承载我们希望外部应用程序访问的网站集(site collection)的本地 Web 应用程序
- 如果您的 SharePointWeb 是 HTTPS 并且其证书不是自签名证书,则您不需要:-AllowOverHttp
2.2创建 IIS 站点来托管外部应用程序
2.2.1创建 IIS 站点
- 在要托管外部应用程序的服务器上,打开 IIS 管理器
- 展开服务器名称
- 右键单击“站点”,然后单击“添加网站…”
- 填写以下字段,然后按“确定”
- 站点名称:输入您站点的名称
- 主机名:输入与站点名称相同的名称
- 物理路径:在 C:\inetpub\wwwroot 下创建一个新文件夹,并将该文件夹命名为您为站点选择的相同名称
- 启用目录浏览
- 单击刚刚创建的 IIS 站点
- 双击“目录浏览”
- 在右侧单击“启用”
2.2.2将 IIS 站点添加到 Hosts 文件
- 以管理员身份打开记事本
- 导航到:C:\Windows\System32\Drivers\etc
- 在右下角选择“所有文件”
- 打开 Hosts 文件
- 添加以下条目以用于 IIS 站点
127.0.0.1 your_site_name
2.2.3测试 IIS 站点设置
- 在 IIS 管理器中,在“站点”下,单击您的站点
- 在右侧,单击“浏览 your_site_name on *:80 (http)”
- 确保没有出现任何错误
- 您应该会看到一个类似于下图的页面
2.3在 Azure 访问控制服务 (ACS) 中注册加载项
2.3.1注册加载项
- 转到外部应用程序将要访问的网站集
- 在网站集 URL 的末尾,紧跟在网站集名称之后,添加:/_layouts/15/appregnew.aspx
示例:http://ecmspotsbx/sites/DevSC/_layouts/15/appregnew.aspx
- 您将看到加载项注册页面
- 对于“应用程序 ID”和“应用程序密钥”,请单击“生成”
- 对于“标题”,输入加载项的标题
- 对于“应用程序域名”,输入在步骤 2.2.1 -> 4 中创建 IIS 站点时输入的名称。
- 将“重定向 URL”留空
- 单击“创建”
- 您将看到一个应用程序标识符已成功创建的页面
- 保存应用程序 ID、应用程序密钥和应用程序域名
2.3.2授予加载项访问 SharePoint 的权限
- 转到外部应用程序将要访问的网站集
- 在网站集 URL 的末尾,紧跟在网站集名称之后,添加:/_layouts/15/appinv.aspx
示例: http://ecmspotsbx/sites/DevSC/_layouts/15/appinv.aspx
- 输入您的应用程序 ID,然后按“查找”。
- 您将看到您的应用程序注册信息
- 将以下权限添加到“权限请求 XML”
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
</AppPermissionRequests>
这将授予加载项对网站集的完全控制权限,并告知 SharePoint 加载项将仅使用加载项的权限,而不会使用运行加载项的用户的权限。
- 单击“创建”
- SharePoint 将询问您是否要授予这些权限给加载项
- 单击“信任”
2.3.3获取 SharePoint 环境的区域 (Realm)
- 转到外部应用程序将要访问的网站集
- 在网站集 URL 的末尾,紧跟在网站集名称之后,添加:/_layouts/15/appprincipals.aspx
示例: http://ecmspotsbx/sites/DevSC/_layouts/15/appprincipals.aspx
- 找到您的加载项
- 保存“@”符号后的 ID。这就是您的区域 (Realm)。
2.4创建加载项
2.4.1创建 SharePoint 加载项项目
- 在我们在步骤 2.2 中创建了 IIS 站点的服务器上,以管理员身份打开 Visual Studio 2015 或更高版本。
- 文件 -> 新建 -> 项目
- 选择“Office/SharePoint”->“应用”->“App for SharePoint”
注意:根据您的 Visual Studio 版本,路径可能是:
Office/SharePoint -> Office Add-ins -> SharePoint Add-in
- 输入您的项目名称,然后按“确定”
- 输入外部应用程序将要访问的网站集的 URL
- 选择“提供商托管”
- 单击“下一步”
- 选择“SharePoint 2013”
- 单击“下一步”
- 选择“ASP.Net Web Forms Application”
- 单击“下一步”
- 选择“使用 Windows Azure 访问控制服务 (适用于 SharePoint 云应用)”
- 单击“完成”
2.4.2删除应用项目
- 在“解决方案资源管理器”中,右键单击应用项目,然后单击“删除”
- Visual Studio 将告知您应用项目将被删除
- 单击“确定”
2.4.3将外部应用程序发布到 IIS
- 在“解决方案资源管理器”中,右键单击 Web 项目,然后单击“属性”
- 在“Web”->“服务器”下
- 将“IIS Express”更改为“本地 IIS”
- 在“项目 URL”中,将 localhost 替换为我们在步骤 2.2 -> 2.2.1 中创建的 IIS 站点的名称
- 单击“创建虚拟目录”
- 您将收到一条消息,指示虚拟目录已成功创建
- 单击“确定”
- 单击“保存”
2.4.4确认虚拟目录已成功创建
- 转到 IIS
- 右键单击“站点”,然后单击“刷新”
- 展开您的 IIS 站点
- 确保您可以看到您的虚拟目录
2.4.5更新外部应用程序代码
2.4.5.1更新 web.config
- 在“解决方案资源管理器”中,在 Web 项目下,打开 web.config
- 使用我们在步骤 2.3 -> 2.3.1 -> 10 中保存的值更新 ClientId 和 ClientSecret 条目。确保值中没有空格。
- 在 ClientSecret 条目之后添加以下条目
<add key="HostedAppHostName" value=""/>
<add key="Realm" value=""/>
<add key="SPHostUrl" value=""/>
<add key="SPAppWebUrl" value=""/>
- 对于 HostedAppHostName 值:输入我们在步骤 2.3 -> 2.3.1 -> 10 中保存的应用程序域名
示例:LowTrustAddIn
- 对于 Realm 值:输入我们在步骤 2.3 -> 2.3.3 -> 4 中保存的区域
示例:fa2e957d-d348-4cf8-94cf-dae799dfbbda
- 对于 SPHostUrl 值:输入外部应用程序将要访问的网站集的 URL
示例:http://ecmspotsbx/sites/DevSC
- 对于 SPAppWebUrl 值:输入上面步骤 2.4.3 的项目 URL,后跟:/Pages/Default.aspx
示例:http://LowTrustAddIn/LowTrustAddInWeb/Pages/Default.aspx
- 您的 appSettings 应类似如下:
2.4.5.2更新 Default.aspx
- 在“解决方案资源管理器”中,在 Web 项目 -> pages 下,打开 Default.aspx
- 删除 <body></body> 之间的所有内容
- 在 <body></body> 之间添加以下内容:
<a name="_Hlk499710851"><</a>form id="form1" runat="server">
<div style="margin-bottom: 10px;">
<b>List:</b>
<asp:Label runat="server" ID="lblListUrl" />
</div>
<div style="margin-bottom: 10px;">
<b>User:</b>
<asp:Label runat="server" ID="lblUserName" />
</div>
<div style="margin-bottom: 10px;">
<b>ID:</b>
<asp:TextBox ID="txtItemId" runat="server"></asp:TextBox>
<b>Status:</b>
<asp:TextBox ID="txtItemStatus" runat="server"></asp:TextBox>
<asp:Button ID="btnUpdateItem" runat="server" Text="Update Status" OnClick="btnUpdateItem_Click" />
</div>
<div>
<span style="color: green"><asp:Label runat="server" ID="lblOperationSuccess" /></span>
<span style="color: red"><asp:Label runat="server" ID="lblOperationFailure" /></span>
</div>
</form>
- 您的最终 Default.aspx 应类似如下:
2.4.5.3更新 Default.aspx.cs
- 在“解决方案资源管理器”中,在 Web 项目 -> pages 下,打开 Default.aspx.cs
- 删除 Page_PreInit() 方法
- 添加对以下命名空间的引用:
- System.Web.Configuration
- Microsoft.IdentityModel.SecurityTokenService
- Microsoft.SharePoint.Client
using System.Web.Configuration;
using Microsoft.IdentityModel.SecurityTokenService;
using Microsoft.SharePoint.Client;
- 在 Page_Load() 方法之前添加以下内容:
private string _siteUrl = "http://ecmspotsbx/sites/DevSC/TS1";
private string _listName = "Test List";
private string _fieldName = "Status";
private List _list;
private ClientContext _clientContext;
- _siteUrl:托管我们要更新列表的网站的 URL
该网站必须位于我们在上面步骤 2.3 中注册加载项的网站集内。
- _listName:我们要更新的列表的名称
- _fieldName:我们要更新的列表字段的名称
- 将 Page_Load() 方法中的所有内容替换为以下内容:
lblOperationSuccess.Text = "";
lblOperationFailure.Text = "";
lblListUrl.Text = _listName + " @ " + _siteUrl;
_clientContext = GetClientContextForUrl(_siteUrl);
// Get user name
var web = _clientContext.Web;
_clientContext.Load(web, w => w.CurrentUser);
_clientContext.ExecuteQuery();
lblUserName.Text = web.CurrentUser.Title;
_list = _clientContext.Web.Lists.GetByTitle(_listName);
- 在 Page_Load() 方法之后添加 btnUpdateItem_Click() 方法
protected void btnUpdateItem_Click(object sender, EventArgs e)
{
try
{
var itemId = txtItemId.Text;
var itemStatus = txtItemStatus.Text;
var listItem = _list.GetItemById(itemId);
listItem[_fieldName] = itemStatus;
listItem.Update();
_clientContext.ExecuteQuery();
lblOperationSuccess.Text = "Operation has been completed successfully!";
}
catch (Exception ex)
{
lblOperationFailure.Text = "Operation failed! " + ex.Message;
}
}
- 在 btnUpdateItem_Click() 方法之后添加 GetClientContextForUrl() 方法
private ClientContext GetClientContextForUrl(string url)
{
var targetWeb = new Uri(url);
var targetRealm = WebConfigurationManager.AppSettings.Get("Realm");
try
{
var responseToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, targetWeb.Authority, targetRealm).AccessToken;
var clientContext = TokenHelper.GetClientContextWithAccessToken(targetWeb.ToString(), responseToken);
return clientContext;
}
catch (RequestFailedException rex)
{
var clientId = WebConfigurationManager.AppSettings.Get("ClientId");
var hostedAppHostName = WebConfigurationManager.AppSettings.Get("HostedAppHostName");
var spHostUrl = WebConfigurationManager.AppSettings.Get("SPHostUrl");
var message = string.Format(@"Token request failed:
1) Verify your App Identifier on {3}/_layouts/15/appPrincipals.aspx it should be '{0}@{1}'.
2) Verify your App Domain on {3}/_layouts/15/appInv.aspx is '{2}' for the App Id '{0}'.",
clientId, targetRealm, hostedAppHostName, spHostUrl);
throw new RequestFailedException(message, rex);
}
}
2.4.6发布代码更改
- 在“解决方案资源管理器”中,右键单击 Web 项目,然后单击“发布”
- 对于“选择发布目标”,选择“自定义”
- 单击“下一步”
- 输入配置文件名
示例:LowTrustAddIn
- 对于“发布方法”:选择“文件系统”
- 对于“目标位置”:这是您的 Visual Studio 项目位置
示例:C:\Users\wfawzi\Documents\visual studio 2015\Projects\LowTrustAddIn\LowTrustAddInWeb
- 单击“发布”
2.5测试外部应用程序
先决条件
- 我们在步骤 2.4.5.3 -> 4 中输入的列表名称必须存在于我们在同一步骤中输入的 URL 所在的网站上
- 该列表必须具有名为“Status”的单行文本列
- 该列表必须至少包含一个项目
- 启动应用程序
在浏览器中导航到上面步骤 2.4.5.1 中的 SPAppWebUrl URL。
示例:http://lowtrustaddin/LowTrustAddInWeb/Pages/Default.aspx
- 您应该会看到一个类似于以下的页面
- 输入您要更新其状态的项目 ID
- 输入新的状态
- 单击“更新状态”
- 您应该会收到一条消息,告知操作已成功完成
- 检查您的列表
- 您应该会发现项目状态已更新
3参考文献
- 使用 Office 365 SharePoint 网站为本地 SharePoint 网站授权提供商托管的加载项
- SharePoint 加载项的上下文令牌 OAuth 流
- SharePoint 中的加载项授权策略类型
https://threewill.com/making-app-only-sharepoint-calls-without-launched-sharepoint/