Página De entrada personalizada tem um SPContext.Atual que é sempre nulo
-
10-12-2019 - |
Pergunta
Encontrei a Solução
A solução foi adicionar um conjunto de Sites Raiz (como o código foi executado em um sub-site, sem que um site de raiz).
Ver Esta Solução
Atualização Importante (com base no feedback recebido até o momento)
Eu não tiver o Visual Studio instalado no Servidor SharePoint 2013.Todo o código é compilado em Visual Studio 2012 remotamente com o uso de referências necessárias para que o SharePoint 2013 DLLs e assinado digitalmente por isso ele pode ser implantado na Cache de Assemblagem Global no SharePoint Server 2013.
No nível mais básico esta declaração de falha no ponto de
Page_Load()
;SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback( SPContext.Current.Site.Zone );
Porque
SPContext.Current
é semprenull
.
Fundo
Cliente necessidades de site do SharePoint para ser acessado usando Autenticação Baseada em Formulário para usuários externos e a Autenticação do Windows para usuários internos (empresa).Passaram e ter o programa de configuração, tanto os Provedores de Autenticação e agora obter o login padrão.
Apesar de tudo funciona a Autenticação do Windows ainda exibe a seleção de Provedor de tela quando o cliente deseja a Autenticação do Windows para apenas passar automaticamente através de e exibir uma Autenticação Baseada em Formulários tela de login se que falha.
Foi lutando com isso por dias depois de olhar vários blogs e artigos que eu sinto que eu não sou mais adiante, e que é deprimente.No momento eu não posso treino se eu estou faltando alguma coisa realmente fundamental.
Página De Entrada Personalizada
A página é baseado fora de um ajuntamento de;
%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\15\Template\IdentityModel\Login\default.aspx
%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\15\Template\IdentityModel\Forms\default.aspx
Esta é apenas uma primeira tentativa e perceber que não precisa de todos esses controles, eventualmente, só queria ver como as coisas se relacionam.
<%@ 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 Assembleia
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;
}
}
}
O Processo
Eu tenho um teste instância do Sharepoint em execução na máquina virtual, mas não o Visual Studio instalado como todo o desenvolvimento é feito remotamente.O atual processo de eu seguir;
- Construir Biblioteca de classes C# (este será
MyCustomSignInModule.Dll
) - Assinar
MyCustomSignInModule.Dll
assim, ele pode ser colocado na VM GAC. - Registar
MyCustomSignInModule.Dll
no GAC na VM instância. - Salvar
SignInForm.aspx
no%CommonProgramFiles%\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\
o que é equivalente a<SharePointSiteRoot>/_layouts/
diretório virtual no IIS. - No site Administração Central do SharePoint definir a Página De entrada Personalizada para a minha página
~/_layouts/SignInForm.aspx
. - Redefinir o IIS usando
iisreset
. - Teste acessando o site do SharePoint (este é o lugar onde ele cai).
O Problema
Se não é claro que o erro é;
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.A corrente é nula por quê?
O NullReferenceException
está a ser causado por SPContext.Current
sendo nulo, mas eu não treino por que é nulo e nenhuma quantidade de artigos que eu olhei até agora não resultou em me ajudar a resolver isso.Eu estou esperando pelo lançamento de todo o meu processo aqui alguém pode ser capaz de apontar para mim que estou em falta ou fazendo de errado.
Solução
Finalmente cheguei ao fundo do que estava causando o problema, em primeiro lugar gostaria de agradecer a ambos @MdMazzotti e @Hugo Madeira por sua persistente de ajuda.
No final, eu tropecei em uma solução que acabou por ser, em parte, porque a maneira como o site foi construído.No meu caso particular, o site foi construído como um sub-site do SharePoint, por qualquer motivo, isso significava que não havia nenhum conjunto de Sites na raiz do web site do SharePoint que foi a causa da Null
SPContext
no meu código.
Depois de adicionar um conjunto de Sites Raiz do meu código começou a funcionar como esperado.
Outras dicas
Eu só estou supondo aqui, mas aqui estão algumas coisas que eu acho que poderia ajudar.
Adicionar este código JavaScript para a página de entrada personalizada (antes da declaração if):
SP.SOD.executeFunc('SP.js', 'SP.ClientContext');
Você pode também adicionar esse JavaScript para a sua página para testar se ele funciona ou não, usando o console:
var clientContext = new SP.ClientContext.get_current();
console.log(clientContext);
Se ela retorna algo, então temos de progresso!Se não, então eu vou ter de continuar a minha pesquisa, desde que eu encontrar a sua pergunta interessante.
EDITAR
Em vez de usar este código no lado do servidor:
if (_iisSettings == null)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
_iisSettings = SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(SPContext.Current.Site.Zone);
});
}
return _iisSettings;
Tente usar esta vez:
if (_iisSettings == null)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
_iisSettings = Microsoft.SharePoint.SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(Microsoft.SharePoint.SPContext.Current.Site.Zone);
});
}
return _iisSettings;
Eu procurei em volta e notou que em alguns dos exemplos de código que eu encontrei, o _iisSettings foi diferente da sua.Talvez seja por isso que o seu SPContext.A corrente é nula?
Por favor, verifique esta resposta:https://stackoverflow.com/questions/5081251/how-can-this-throw-a-nullreferenceexception
SPContext.Atual não funciona com Privilégios Elevados, então você tem que reescrever como na resposta acima.
Você pode verificar este artigo do MSDN: http://msdn.microsoft.com/en-us/library/office/ms468609(v=office.14).aspx
Eu acho que porque você usa uma Biblioteca de classes C# projeto no Visual studio, você não está em um contexto web e não pode usar SPContext.Atual, como explicado no artigo do MSDN acima.