自定义登录页面有一个SPContext。始终为null的电流
-
10-12-2019 - |
题
找到解决方案
解决方案是添加根网站集(因为代码在没有根网站的子网站中运行)。
见 这个解决方案
重要更新 (根据迄今为止收到的反馈)
我没有在SharePoint2013服务器上安装Visual Studio。所有代码都在Visual Studio2012中使用对所需SharePoint2013Dll的引用进行远程编译,并进行数字签名,以便可以将其部署到SharePoint2013服务器上的全局程序集缓存中。
在最基本的层面上,这句话在
Page_Load()
;SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback( SPContext.Current.Site.Zone );
因为
SPContext.Current
是 永远如此null
.
背景资料
客户端需要使用外部用户的基于表单的身份验证和内部用户(企业)的Windows身份验证访问SharePoint站点。已经完成并设置了两个身份验证提供程序,现在获得了默认登录。
虽然一切正常,但当客户端希望Windows身份验证自动通过时,Windows身份验证仍会显示提供程序选择屏幕,如果失败,则显示基于表单的身份验证登录屏幕。
在看了各种各样的博客和文章之后,我一直在为此奋斗几天,我觉得我没有进一步的进展,这是令人沮丧的。目前我无法确定我是否错过了一些真正基本的东西。
自定义登录页面
该页面基于以下内容的合并;
%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\15\Template\IdentityModel\Login\default.aspx
%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\15\Template\IdentityModel\Forms\default.aspx
这只是第一次尝试,并意识到我最终不需要所有这些控制,只是想看看事情是如何挂在一起的。
<%@ Assembly Name="Microsoft.SharePoint.IdentityModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharepointIdentity" Namespace="Microsoft.SharePoint.IdentityModel" Assembly="Microsoft.SharePoint.IdentityModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Page Language="C#" Inherits="MyCustomSignInModule.SignInForm, MyCustomSignInModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2ae721af94bf9e9" MasterPageFile="~/_layouts/15/errorv15.master"%>
<%@ Import Namespace="Microsoft.SharePoint.WebControls" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" Id="ClaimsLogonPageTitleInTitleArea" />
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" Id="ClaimsFormsPageTitle" />
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" Id="ClaimsLogonPageMessage" />
<br />
<br />
<SharepointIdentity:LogonSelector ID="ClaimsLogonSelector" runat="server" />
<div id="SslWarning" style="color:red;display:none">
<SharePoint:EncodedLiteral runat="server" EncodeMethod="HtmlEncode" Id="ClaimsFormsPageMessage" />
</div>
<script language="javascript" >
if (document.location.protocol != 'https:') {
var SslWarning = document.getElementById('SslWarning');
SslWarning.style.display = '';
}
</script>
<asp:login id="signInControl" FailureText="<%$Resources:wss,login_pageFailureText%>" runat="server" width="100%">
<layouttemplate>
<asp:label id="FailureText" class="ms-error" runat="server"/>
<table width="100%">
<tr>
<td nowrap="nowrap"><SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,login_pageUserName%>" EncodeMethod='HtmlEncode'/></td>
<td width="100%"><asp:textbox id="UserName" autocomplete="off" runat="server" class="ms-inputuserfield" width="99%" /></td>
</tr>
<tr>
<td nowrap="nowrap"><SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,login_pagePassword%>" EncodeMethod='HtmlEncode'/></td>
<td width="100%"><asp:textbox id="password" TextMode="Password" autocomplete="off" runat="server" class="ms-inputuserfield" width="99%"/></td>
</tr>
<tr>
<td colspan="2" align="right"><asp:button id="login" commandname="Login" text="<%$Resources:wss,login_pagetitle%>" runat="server" /></td>
</tr>
<tr>
<td colspan="2"><asp:checkbox id="RememberMe" text="<%$SPHtmlEncodedResources:wss,login_pageRememberMe%>" runat="server" /></td>
</tr>
</table>
</layouttemplate>
</asp:login>
<asp:label id="DebugText" class="ms-error" runat="server"/>
</asp:Content>
MyCustomSignInModule组装
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.IdentityModel.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.IdentityModel;
using Microsoft.SharePoint.IdentityModel.Pages;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.ApplicationRuntime;
using Microsoft.SharePoint.Diagnostics;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using System.IdentityModel.Tokens;
using System.Web.Configuration;
namespace MyCustomSignInModule
{
public class SignInForm : FormsSignInPage
{
protected Label DebugText;
private static SPIisSettings _iisSettings;
internal static SPIisSettings iisSettings
{
get
{
if (_iisSettings == null)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
_iisSettings = SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(SPContext.Current.Site.Zone);
});
}
return _iisSettings;
}
}
protected void Page_Load(object sender, EventArgs e)
{
HttpRequest request = HttpContext.Current.Request;
bool isWindowsAuth = false;
string username = request["username"];
string password = request["password"];
// If no username is provided, it'll probably be Windows Authentication (NTLMv2 protocol)
if (String.IsNullOrEmpty(username))
{
isWindowsAuth = true;
}
try
{
//SPIisSettings iisSettings = SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default];
if (isWindowsAuth)
{
// Windows Authentication it is
if (iisSettings.UseWindowsClaimsAuthenticationProvider)
{
SPAuthenticationProvider provider = iisSettings.WindowsClaimsAuthenticationProvider;
RedirectToLoginPage(provider); // This should cause automatic sign in
}
}
else
{
// FBA authentication it is.
SPFormsAuthenticationProvider formsClaimsAuthenticationProvider = iisSettings.FormsClaimsAuthenticationProvider;
SecurityToken token = SPSecurityContext.SecurityTokenForFormsAuthentication(new Uri(SPContext.Current.Web.Url), formsClaimsAuthenticationProvider.MembershipProvider, formsClaimsAuthenticationProvider.RoleProvider, username, password, SPFormsAuthenticationOption.PersistentSignInRequest);
if (null != token)
{
EstablishSessionWithToken(token);
base.RedirectToSuccessUrl();
}
}
}
catch (Exception ex)
{
DebugText.Text = ex.ToString();
}
}
// Microsoft.SharePoint.IdentityModel.LogonSelector
private void RedirectToLoginPage(SPAuthenticationProvider provider)
{
string components = HttpContext.Current.Request.Url.GetComponents(UriComponents.Query, UriFormat.SafeUnescaped);
string url = provider.AuthenticationRedirectionUrl.ToString();
if (provider is SPWindowsAuthenticationProvider)
{
components = EnsureUrlSkipsFormsAuthModuleRedirection(components, true);
}
SPUtility.Redirect(url, SPRedirectFlags.Default, this.Context, components);
}
// Microsoft.SharePoint.Utilities.SPUtility
private string EnsureUrlSkipsFormsAuthModuleRedirection
(string url, bool urlIsQueryStringOnly)
{
if (!url.Contains("ReturnUrl="))
{
if (urlIsQueryStringOnly)
{
url = url + (string.IsNullOrEmpty(url) ? "" : "&");
}
else
{
url = url + ((url.IndexOf('?') == -1) ? "?" : "&");
}
url = url + "ReturnUrl=";
}
return url;
}
// Microsoft.SharePoint.IdentityModel.Pages.IdentityModelSignInPageBase
private void EstablishSessionWithToken(SecurityToken securityToken)
{
if (securityToken == null)
{
throw new ArgumentNullException("securityToken");
}
SPFederationAuthenticationModule fam = SPFederationAuthenticationModule.Current;
if (fam == null)
{
throw new InvalidOperationException();
}
fam.SetPrincipalAndWriteSessionToken(securityToken);
}
private SPAuthenticationProvider
GetAuthenticationProvider(string providerName)
{
SPIisSettings iisSettings =
SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default];
foreach (SPAuthenticationProvider currentProvider in
iisSettings.ClaimsAuthenticationProviders)
{
if (currentProvider.DisplayName.ToLower() ==
providerName.ToLower())
{
return currentProvider;
}
}
return null;
}
}
}
过程
我有一个在VM上运行的测试Sharepoint实例,但没有安装Visual Studio,因为所有开发都是远程完成的。我遵循的当前过程;
- 构建C#类库(这将是
MyCustomSignInModule.Dll
) - 签署
MyCustomSignInModule.Dll
所以它可以放在VM GAC中。 - 登记册
MyCustomSignInModule.Dll
在VM实例上的GAC中。 - 储蓄
SignInForm.aspx
进入%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\
这相当于<SharePointSiteRoot>/_layouts/
IIS中的虚拟目录。 - 在SharePoint管理中心网站中,将"自定义登录"页面设置为"我的页面
~/_layouts/SignInForm.aspx
. - 使用重置IIS
iisreset
. - 测试访问SharePoint站点(这是它落在哪里)。
问题出在哪里
如果不是那么清楚,错误是;
System.NullReferenceException: Object reference not set to an instance of an object. at
MyCustomSignInModule.SignInForm.b__0() at
Microsoft.SharePoint.SPSecurity.<>c__DisplayClass5.b__3() at
Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) at
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param) at
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) at MyCustomSignInModule.SignInForm.get_iisSettings() at
MyCustomSignInModule.SignInForm.Page_Load(Object sender, EventArgs e)
SPContext。电流为null为什么?
该 NullReferenceException
是由 SPContext.Current
是空的,但我无法解释为什么它是空的,到目前为止我看过的任何文章都没有帮助我解决这个问题。我希望通过在这里发布我的整个过程,有人可能会指出我错过了什么或做错了什么。
解决方案
终于得到了到底是什么导致了这个问题,首先要感谢两个 @MdMazzotti 和 @休伍德 感谢他们坚持不懈的帮助。
最后,我偶然发现了修复,这部分是因为网站的构建方式。在我的特殊情况下,该网站是作为SharePoint中的子网站构建的,无论出于何种原因,这意味着SharePoint网站的根目录没有网站集,这是导致 Null
SPContext
在我的代码中。
添加根网站集后,我的代码开始按预期工作。
其他提示
我只是在这里猜测,但这里有一些我认为可以帮助的事情。
将此JavaScript代码添加到自定义登录页面(在if-语句之前):
SP.SOD.executeFunc('SP.js', 'SP.ClientContext');
您还可以使用控制台将此JavaScript添加到页面中以测试它是否有效:
var clientContext = new SP.ClientContext.get_current();
console.log(clientContext);
如果它返回的东西,那么我们有进步!如果没有,那么我将不得不继续我的搜索,因为我觉得你的问题很有趣。
编辑
而不是使用此服务器端代码:
if (_iisSettings == null)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
_iisSettings = SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(SPContext.Current.Site.Zone);
});
}
return _iisSettings;
尝试使用此代替:
if (_iisSettings == null)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
_iisSettings = Microsoft.SharePoint.SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(Microsoft.SharePoint.SPContext.Current.Site.Zone);
});
}
return _iisSettings;
我四处搜索,注意到在我遇到的一些代码示例中,_iisSettings与您的不同。也许这就是为什么你的SPContext。电流为null?
请检查这个答案:https://stackoverflow.com/questions/5081251/how-can-this-throw-a-nullreferenceexception
SPContext。Current在提升的特权下不起作用,所以你必须像上面的答案一样重写。
你能检查这篇MSDN文章吗: http://msdn.microsoft.com/en-us/library/office/ms468609(v=office.14).aspx
我认为因为您在Visual studio中使用C#类库项目,所以您不在web上下文中,无法使用SPContext。当前如上面的MSDN文章中所述。