Partager via


Utilizzo di attestazioni SAML, SharePoint, WCF, attestazioni per il servizio token Windows e delega vincolata per l'accesso a SQL Server

Articolo originale pubblicato domenica 07 agosto 2011

Questo probabilmente sarà il più lungo post di blog che abbia mai scritto, ma volevo essere sicuro di esaminare tutte le tecnologie pertinenti prese in esame. Questa è un'area di cui ho sentito molto parlare ultimamente, ma in realtà si tratta semplicemente di come utilizzare un'attestazione SAML e ottenere un contesto Windows per accedere ad altre applicazioni. SharePoint 2010 dispone di un supporto limitato per quanto riguarda l'uso delle attestazioni per il servizio token Windows (definito anche c2wts), ma solo per gli utenti di attestazioni Windows che dispongono di un ridotto numero di applicazioni di servizio. In molti si chiedono per quale motivo non si possa utilizzare un utente di attestazioni SAML con un'attestazione UPN valida, e in realtà non esiste un motivo tecnologico che lo impedisca. Pertanto tra la limitazione dei tipi di autenticazione e la limitazione delle applicazioni di servizio che potete utilizzare, è probabile che vi troviate nella necessità di creare un modo per connettere gli utenti SAML alle altre applicazioni tramite i loro account Windows. Questo post dovrebbe riuscire a farvi capire essenzialmente come assolvere a questo scopo.

L'approccio di base di questo scenario consiste nel creare un'applicazione di servizi WCF che elabori tutte le richieste di dati degli utenti finali dall'altra applicazione, che nel nostro caso è SQL Server. Intendo pertanto scegliere un utente SAML che sta visitando il sito SharePoint e creare una richiesta utilizzando l'account Windows di tale utente SAML quando recupero i dati da SQL Server. NOTA: sebbene questo articolo faccia riferimento agli utenti di attestazioni SAML, la stessa identica metodologia può essere utilizzata per gli utenti di attestazioni Windows, poiché essi ottengono un'attestazione UPN per impostazione predefinita quando effettuano l'accesso. Di seguito potete osservare un diagramma che illustra l'intero processo:

Configurazione di SQL Server

Iniziamo dal lato SQL Server. Nel mio scenario, SQL Server è in esecuzione su un server denominato “SQL2”. Il servizio SQL invece viene eseguito come servizio di rete, pertanto non devo creare un nome SPN per il servizio. Se fosse stato eseguito con un account di dominio, allora avrei dovuto creare un nome SPN per tale account di servizio per MSSQLSvc. Per questo scenario specifico, utilizzerò il database Northwind per recuperare i dati. Voglio dimostrare con facilità l'identità dell'utente che sta effettuando la richiesta, quindi ho modificato la stored procedure Ten Most Expensive Products in modo che abbia il seguente aspetto:

 

CREATE procedure [dbo].[TenProductsAndUser] AS

SET ROWCOUNT 10

SELECT Products.ProductName AS TenMostExpensiveProducts, Products.UnitPrice, SYSTEM_USER As CurrentUser

FROM Products

ORDER BY Products.UnitPrice DESC

 

La cosa fondamentale da notare è che ho aggiunto SYSTEM_USER all'istruzione SELECT, che restituisce l'utente corrente nella colonna. Di conseguenza quando eseguo una query e ottengo i risultati, vedrò una colonna nella mia griglia che contiene il nome dell'utente corrente pertanto possono individuare facilmente se la query viene eseguita con l'identità dell'utente corrente o meno. In questo scenario specifico ho concesso a tre utenti Windows i diritti per eseguire questa stored procedure, mentre tutti gli altri utenti non possono eseguirla (sarà anche un esempio utile nel risultato finale).

Creazione dell'applicazione di servizio WCF

Ho quindi creato un'applicazione di servizio WCF che ha recuperato i dati da SQL. Ho seguito le linee guida che ho delineato in precedenza nella seconda parte del post relativo al kit CASI (https://blogs.technet.com/b/speschka/archive/2010/11/06/the-claims-azure-and-sharepoint-integration-toolkit-part-2.aspx) allo scopo di stabilire l'attendibilità tra la farm SharePoint e l'applicazione WCF. Tutto ciò è necessario per poter ottenere le attestazioni dell'utente che effettua la richiesta. Non voglio semplicemente passare il valore dell'attestazione UPN come parametro, poiché chiunque potrebbe eseguire lo spoofing dell'identità di un altro utente passando un valore di attestazione UPN diverso. Dopo aver configurato correttamente l'attendibilità tra WCF e SharePoint, posso procedere con la creazione del metodo che eseguirà le operazioni seguenti:

  • Estrarre l'attestazione UPN
  • Rappresentare l'utente tramite le attestazioni per il servizio token Windows
  • Recuperare i dati da SQL con l'identità di tale utente

 

Di seguito potete trovare il codice che ho utilizzato per questo scopo:

 

//the following added for this code sample:

using Microsoft.IdentityModel;

using Microsoft.IdentityModel.Claims;

using System.Data;

using System.Data.SqlClient;

using System.Security.Principal;

using Microsoft.IdentityModel.WindowsTokenService;

using System.ServiceModel.Security;

 

 

public DataSet GetProducts()

{

 

   DataSet ds = null;

 

   try

   {

       string conStr = "Data Source=SQL2;Initial Catalog=

       Northwind;Integrated Security=True;";

 

       //ask for the current claims identity

       IClaimsIdentity ci =

          System.Threading.Thread.CurrentPrincipal.Identity as IClaimsIdentity;

 

       //make sure the request had a claims identity attached to it

       if (ci != null)

       {

          //see if there are claims present before running through this

          if (ci.Claims.Count > 0)

          {

              //look for the UPN claim

              var eClaim = from Microsoft.IdentityModel.Claims.Claim c in ci.Claims

              where c.ClaimType == System.IdentityModel.Claims.ClaimTypes.Upn

              select c;

 

              //if we got a match, then get the value for login

              if (eClaim.Count() > 0)

              {

                 //get the upn claim value

                 string upn = eClaim.First().Value;

 

                 //create the WindowsIdentity for impersonation

                 WindowsIdentity wid = null;

 

                 try

                 {

                     wid = S4UClient.UpnLogon(upn);

                 }

                 catch (SecurityAccessDeniedException adEx)

                 {

                           Debug.WriteLine("Could not map the upn claim to " +

                     "a valid windows identity: " + adEx.Message);

                 }

 

                 //see if we were able to successfully login

                 if (wid != null)

                 {

                        using (WindowsImpersonationContext ctx = wid.Impersonate())

                    {

                       //request the data from SQL Server

                        using (SqlConnection cn = new SqlConnection(conStr))

                        {

                           ds = new DataSet();

                           SqlDataAdapter da =

                               new SqlDataAdapter("TenProductsAndUser", cn);

                           da.SelectCommand.CommandType =

                               CommandType.StoredProcedure;

                           da.Fill(ds);

                        }

                     }

                 }

              }

          }

       }

   }

   catch (Exception ex)

   {

       Debug.WriteLine(ex.Message);

   }

 

   return ds;

}

 

In realtà non è un codice molto complicato, quindi ecco una breve sintesi di quello che abbiamo fatto. Verifico innanzitutto di disporre di un contesto di identità per le attestazioni valido e, in tal caso, eseguo una query nell'elenco di attestazioni per cercare l'attestazione UPN. Supponendo di aver trovato l'attestazione UPN, ne estraggo il valore ed eseguo una chiamata alle attestazioni per il servizio token Windows allo scopo di accedere a S4U con l'identità di tale utente. Se l'accesso viene completato, ritorno a un'identità di Windows. Utilizzo quindi tale identità di Windows per creare un contesto di rappresentazione. Dopo aver rappresentato l'utente, creo una connessione a SQL Server e recupero i dati. Di seguito potete trovare alcuni suggerimenti per la risoluzione dei problemi:

  1. Se non avete configurato le attestazioni per il servizio token Windows in modo che il pool di applicazioni possano utilizzarle, riceverete un errore relativo al blocco catch esterno. Il messaggio di errore indicherà che il chiamante non è autorizzato ad accedere il servizio, fornendo maggiori dettagli e un collegamento per la configurazione delle attestazioni per il servizio token Windows.
  2. Se la delega vincolata Kerberos non è impostata correttamente, provate a eseguire la stored procedure con la riga di codice da.Fill(ds);, genererà un'eccezione in cui è indicato che un utente anonimo non dispone dei diritti necessari per eseguire la stored procedure. Di seguito vi indico alcuni suggerimenti sulla configurazione della delega vincolata per questo scenario.

Configurazione delle attestazioni per il servizio token Windows

Le attestazioni per il servizio token Windows vengono configurate per impostazione predefinita per a) essere avviate manualmente e b) impedirne l'uso a tutti. Ho modificato questa configurazione in modo che a) vengano avviate automaticamente e b) il pool di applicazioni per l'applicazione di servizio WCF sia autorizzato a utilizzarle. Invece di illustrare in dettaglio la configurazione di questa autorizzazione, vi consiglio di leggere l'articolo seguente, in cui le informazioni di configurazione sono poste alla fine: https://msdn.microsoft.com/en-us/library/ee517258.aspx. È tutto ciò che vi serve. Per ulteriori informazioni complementari sulle attestazioni per il servizio token Windows, vi consiglio inoltre di consultare https://msdn.microsoft.com/en-us/library/ee517278.aspx.

 

NOTA: nell'ultimo articolo è contenuto un errore importante: si consiglia di creare una dipendenza per le attestazioni per il servizio token Windows eseguendo questo codice: sc config c2wts depend=cryptosvc. NON FATELO C'è un errore di ortografia e “cryptosvc” non è un nome di servizio valido, almeno non in Windows Server 2008 R2. Se eseguite quel codice le attestazioni per il servizio token Windows non verranno più avviate in quanto la dipendenza risulta contrassegnata per l'eliminazione o mancante. Mi sono trovato in questa situazione ed ho modificato la dipendenza in iisadmin (che è una scelta logica in quando, almeno nel mio caso, l'host WCF deve essere in esecuzione per poter utilizzare le attestazioni per il servizio token Windows). Senza questa modifica non avrebbe funzionato.

Configurazione della delega vincolata Kerberos

Prima di addentrarmi in questo argomento desidero fare alcune premesse:

  1. Non intendo approfondire i dettagli dell'uso della delega vincolata Kerberos, poiché esiste una considerevole documentazione in proposito.
  2. Se può interessarvi, questa parte funzionava perfettamente quando ho scritto questo post.

 

Esaminiamo quindi i requisiti per la delega. Come ho menzionato in precedenza, il servizio SQL Server è in esecuzione come servizio di rete, pertanto non devo apportare alcune modifica in quel senso. Il pool di applicazioni WCF è inoltre in esecuzione con un account di dominio denominato vbtoys\portal. Devo pertanto eseguire due operazioni:

  1. Creare un nome SPN HTTP, utilizzando sia il nome NetBIOS che il nome completo del server da cui eseguirò la delega. Nel mio caso il nome del server WCF è AZ1, pertanto ho creato due nomi SPN seguenti: 
    1. setspn -A HTTP/az1 vbtoys\portal
    2. setspn -A HTTP/az1.vbtoys.com vbtoys\portal
  2. Devo configurare il mio account in modo che venga considerato attendibile per la delega vincolata Kerberos nei servizi SQL Server in esecuzione sul “SQL2”. Per questo scopo ho aperto Utenti e computer di Active Directory dal controller di dominio. Ho fatto doppio clic sull'utente vbtoys\portal e quindi ho selezionato la scheda Delega per configurare l'attendibilità. Ho impostato l'attendibilità della delega solo per servizi specifici, utilizzando qualsiasi tipo di protocollo. Di seguito potete trovare un collegamento a un'immagine che riproduce la configurazione della delega:

 

Ho dovuto quindi configurare il server applicazioni WCF in modo da essere considerato attendibile per la delega vincolata. Fortunatamente il processo è esattamente identico a quello descritto in precedenza per l'utente, dovrete solo trovare l'account del computer in Utenti e computer di Active Directory e configurarlo. Di seguito potete trovare un collegamento a un'immagine che riproduce tale configurazione:

 

 

Con questo abbiamo concluso la configurazione di tutti gli aspetti non relativi a SharePoint. L'ultima cosa di cui abbiamo bisogno è una web part per il test.

Creazione della web part di SharePoint

Il processo di creazione della web part è molto semplice, ho seguito lo schema descritto precedentemente per effettuare le chiamate WCF a SharePoint e passare l'identità dell'utente corrente (https://blogs.technet.com/b/speschka/archive/2010/09/08/calling-a-claims-aware-wcf-service-from-a-sharepoint-2010-claims-site.aspx). Avrei potuto utilizzare anche il kit CASI per stabilire la connessione e chiamare il servizio WCF, ma ho deciso di procedere manualmente in modo da illustrare meglio il processo. I passaggi essenziali per creare la web part sono i seguenti:

  1. Create un nuovo progetto di SharePoint 2010 in Visual Studio 2010.
  2. Create un riferimento del servizio all'applicazione di servizio WCF.
  3. Aggiungete una nuova web part.
  4. Aggiungete alla web part il codice per recuperare i dati da WCF e visualizzarli in una griglia.
  5. Aggiungete tutte le informazioni del file app.config generate nel progetto Visual Studio alla sezione <system.ServiceModel> del file web.config per l'applicazione Web in cui sarà ospitata la web part.

NOTA: il file app.config conterrà un attributo denominato decompressionEnabled, dovete eliminarlo prima di aggiungere il file web.config. Se non lo eliminate, la web part genererà un errore durante il tentativo di creare un'istanza del proxy di riferimento del servizio.

Tutti i passaggi appena descritti dovrebbero essere abbastanza chiari tranne il numero 4, pertanto non esaminerò gli altri in dettaglio. Di seguito vi indico tuttavia il codice per la web part:

private DataGrid dataGrd = null;

private Label statusLbl = null;

 

 

protected override void CreateChildControls()

{

   try

   {

       //create the connection to the WCF and try retrieving the data

       SqlDataSvc.SqlDataClient sqlDC = new SqlDataSvc.SqlDataClient();

 

       //configure the channel so we can call it with FederatedClientCredentials

       SPChannelFactoryOperations.ConfigureCredentials<SqlDataSvc.ISqlData>(

       sqlDC.ChannelFactory, Microsoft.SharePoint.SPServiceAuthenticationMode.Claims);

 

       //create the endpoint to connect to

       EndpointAddress svcEndPt =

          new EndpointAddress("https://az1.vbtoys.com/ClaimsToSqlWCF/SqlData.svc");

 

       //create a channel to the WCF endpoint using the

       //token and claims of the current user

       SqlDataSvc.ISqlData sqlData =

          SPChannelFactoryOperations.CreateChannelActingAsLoggedOnUser

          <SqlDataSvc.ISqlData>(sqlDC.ChannelFactory, svcEndPt);

 

       //request the data

       DataSet ds = sqlData.GetProducts();

 

       if ((ds == null) || (ds.Tables.Count == 0))

       {

          statusLbl = new Label();

          statusLbl.Text = "No data was returned at " + DateTime.Now.ToString();

          statusLbl.ForeColor = System.Drawing.Color.Red;

          this.Controls.Add(statusLbl);

       }

       else

       {

          dataGrd = new DataGrid();

          dataGrd.AutoGenerateColumns = true;

          dataGrd.DataSource = ds.Tables[0];

          dataGrd.DataBind();

          this.Controls.Add(dataGrd);

       }

   }

   catch (Exception ex)

   {

       Debug.WriteLine(ex.Message);

   }

}

 

Anche in questo caso penso che sia tutto molto chiaro. La prima parte riguarda la creazione della connessione al servizio WCF in modo che avvenga il passaggio delle attestazioni dell'utente corrente. Per maggiori dettagli, utilizzate il collegamento al mio precedente post di blog su questo argomento riportato sopra. Tutto il resto riguarda il recupero di un set di dati e la sua associazione a una griglia se sono presenti dati oppure, se il recupero ha esito negativo, mostrando un'etichetta in cui è indicato che non sono presenti dati. Per illustrare il funzionamento di tutte queste parti insieme ho incluso sotto tre schermate. Le prime due mostrano il funzionamento per due utenti diversi, che potete vedere nella colonna CurrentUser. Nella terza schermata viene mostrato il caso di un utente a cui non sono stati concessi i diritti necessari per eseguire la stored procedure.

 

 

Questo è tutto. Ho allegato il codice per l'applicazione di servizio WCF e la web part a questo post, insieme al documento di Word originale in cui ho scritto questo articolo poiché la formattazione di questi post non è ideale.

Questo è un post di blog localizzato. L'articolo originale è disponibile in Using SAML Claims, SharePoint, WCF, Claims to Windows Token Service and Constrained Delegation to Access SQL Server