Пользовательская страница входа в систему имеет значение SPContext.Current, которое всегда равно null
-
10-12-2019 - |
Вопрос
Найдено решение
Решение состояло в добавлении корневого семейства сайтов (поскольку код выполнялся на подсайте, без корневого сайта).
Видеть Это решение
Важное обновление (основываясь на отзывах, полученных к настоящему времени)
У меня не установлена Visual Studio на сервере SharePoint 2013.Весь код компилируется в Visual Studio 2012 удаленно с использованием ссылок на необходимые библиотеки DLL SharePoint 2013 и имеет цифровую подпись, чтобы его можно было развернуть в глобальном кэше сборок на сервере SharePoint 2013.
На самом базовом уровне это утверждение терпит неудачу в точке
Page_Load()
;SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback( SPContext.Current.Site.Zone );
Потому что
SPContext.Current
является всегдаnull
.
Фон
Клиенту необходимо, чтобы сайт SharePoint был доступен с использованием проверки подлинности на основе форм для внешних пользователей и проверки подлинности Windows для внутренних пользователей (корпоративных).Прошли и настроили обоих поставщиков аутентификации и теперь получаете вход по умолчанию.
Хотя все работает, проверка подлинности 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>
Сборка модуля mycustomsignin
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;
}
}
}
Процесс
У меня есть тестовый экземпляр Sharepoint, запущенный на виртуальной машине, но Visual Studio не установлена, поскольку вся разработка выполняется удаленно.Текущий процесс, которому я следую;
- Создайте библиотеку классов C# (это будет
MyCustomSignInModule.Dll
) - Знак
MyCustomSignInModule.Dll
таким образом, он может быть помещен в GAC виртуальной машины. - Зарегистрировать
MyCustomSignInModule.Dll
в 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.Current равен null почему?
То NullReferenceException
вызывается SPContext.Current
значение null, но я не могу понять, почему оно равно null, и никакое количество статей, которые я просмотрел, до сих пор не помогло мне решить эту проблему.Я надеюсь, что, опубликовав весь мой процесс здесь, кто-нибудь сможет указать мне на то, что я упускаю или делаю неправильно.
Решение
Наконец-то разобрались с причиной проблемы, во-первых, хотели бы поблагодарить обоих @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.Current равен 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
Я думаю, поскольку вы используете проект библиотеки классов C# в Visual Studio, вы не находитесь в веб-контексте и не можете использовать SPContext.Current, как описано в статье MSDN выше.