My Blog

Use ASP.NET SqlMemberShipProvider in WCF self-hosted service

by lupok on martedì 28 gennaio 2014 13:43

L'utilizzo dei meccanismi di autenticazione di ASP.NET in WCF risulta essere molto comodo per gestire l'autenticazione degli utenti che voglioni accedere al servizio mediante i costrutti di base che in generale .NET mette a disposizione gestire le autorizzazioni all'esecuzione di porzioni di codice differenti. 

Questo esempio e' realizzato mediante webHttpBinding e basicHttpBinding in quanto i piu' utilizzati ma anche i piu' ostici per via dei numerosi vincoli imposti dalla tecnologia su cui sono basati. Il provider ASP.NET che viene utilizzato e' il SqlMemberShipProvider, quindi e' necessario creare un database utile a contenere tutte le tabelle di gestione utenti, ruoli, diritti, ecc... Per create un database e tutte le tabelle necessarie e' possibile aprire un prompt comandi di visual studio e eseguire il comando riportato di seguito:

 

aspnet_regsql.exe -E -S .\SqlExpress2012 -A m 

 

Dopo aver creato un WCF Service Library ed una console applicazione atta ad ospitare il servizio occorre procedere alla cofigurazione mediante modifica del file app.config, di seguito riporto per semplicita' il file completo:

 

xml version="1.0"?>
<configuration>
   <startup>
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
   startup>
   <connectionStrings>
      <add name="mySqlConnection" connectionString="Data Source=.\SQLEXPRESS2012;Integrated Security=SSPI;Initial Catalog=aspnetdb;"/>
   connectionStrings>
   <system.web>
      <compilation debug="true"/>
      
      <membership defaultProvider="MySqlMembershipProvider" userIsOnlineTimeWindow="15">
         <providers>
            <clear/>
            <add name="MySqlMembershipProvider" 
                 type="System.Web.Security.SqlMembershipProvider" 
                 connectionStringName="mySqlConnection" 
                 applicationName="UsersManagementNavigationApplication" 
                 enablePasswordRetrieval="false" 
                 enablePasswordReset="false" 
                 requiresQuestionAndAnswer="false" 
                 requiresUniqueEmail="true" 
                 passwordFormat="Hashed"/>
         providers>
      membership>
      
      <roleManager enabled="true" defaultProvider="MySqlRoleProvider">
         <providers>
            <clear/>
            <add name="MySqlRoleProvider" type="System.Web.Security.SqlRoleProvider" 
                 connectionStringName="mySqlConnection" applicationName="UsersManagementNavigationApplication"/>
         providers>
      roleManager>
   system.web>
   <system.serviceModel>
      <bindings>
         <webHttpBinding>
            <binding name="webBinding">
               <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Basic"/>
               security>
            binding>
         webHttpBinding>
         <basicHttpBinding>
            <binding name="basicBindingConfiguration">
               <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Basic"/>
               security>
            binding>
         basicHttpBinding>
      bindings>
      <behaviors>
         <endpointBehaviors>
            <behavior name="webEndpointBehavior">
               <webHttp/>
            behavior>
         endpointBehaviors>
         <serviceBehaviors>
            <behavior name="webServiceBehavior">
               <serviceMetadata httpGetEnabled="true"/>
               <serviceThrottling/>
               <serviceDebug/>
            behavior>
            <behavior name="myServiceBehavior">
               
               <serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="MySqlRoleProvider">
               serviceAuthorization>
               <serviceCredentials>
                  
                  <userNameAuthentication userNamePasswordValidationMode="Custom" 
                                          customUserNamePasswordValidatorType="WcfServiceHTTPSelfHosted.MyCustomValidator, WcfServiceHTTPSelfHosted"/>
               serviceCredentials>
               
               <serviceMetadata httpGetEnabled="true"/>
               
               <serviceDebug includeExceptionDetailInFaults="false"/>
            behavior>
         serviceBehaviors>
      behaviors>
      <services>
         <service behaviorConfiguration="myServiceBehavior" name="WcfServiceHTTPSelfHosted.WcfServiceHTTPSelfHosted">
            <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicBindingConfiguration"
               contract="WcfServiceHTTPSelfHosted.IWcfServiceHTTPSelfHosted" />
            <endpoint address="web" behaviorConfiguration="webEndpointBehavior"
               binding="webHttpBinding" bindingConfiguration="webBinding"
               contract="WcfServiceHTTPSelfHosted.IWcfServiceHTTPSelfHosted" />
            <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
               contract="IMetadataExchange" />
            <host>
               <baseAddresses>
                  <add baseAddress="http://localhost:50002/WcfServiceHTTPSelfHosted/" />
               baseAddresses>
            host>
         service>
      services>
   system.serviceModel>
configuration>

La cosa a cui porre particolare attenzione e' l'utilizzo di un userNamePasswordValidationMode di tipo Custom, necessaria in quanto stiamo utilizzando il servizio esternamente a IIS, il tipo di validatore implementato utilizza poi il SqlMemberShipProvider nel seguente modo:

 

using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Text;
using System.Web.Security;
 
namespace WcfServiceHTTPSelfHosted
{
   public class MyCustomValidator : UserNamePasswordValidator
   {
 
      public MyCustomValidator()
      {
 
      }
 
      public override void Validate(string userName, string password)
      {
         if (!Membership.ValidateUser(userName, password))
         {
            throw new SecurityTokenException("Users validation failed: " + userName);
         }
      }
   }
}

 

Nel codice riportato sopra si vede che per validare l'utente di utilizza la funzione Membership.ValidateUser che in funzione della configurazione impostata richiama il SqlMemberShipProvider. Modifichiamo il contratto del servizio WCF secondo le nostre esigenze:

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
 
namespace WcfServiceHTTPSelfHosted
{
   [ServiceContract]
   public interface IWcfServiceHTTPSelfHosted
   {
      [OperationContract]
      [WebGet(UriTemplate = "GetData1?value={value}")]
      String GetData1(Int32 value);
 
      [OperationContract]
      [WebGet(UriTemplate = "GetData2?value={value}")]
      String GetData2(Int32 value);
 
      [OperationContract]
      [WebGet(UriTemplate = "GetData3?value={value}")]
      String GetData3(Int32 value);
   }
}

 

e la rispettiva implementazione:

 

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Permissions;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
 
namespace WcfServiceHTTPSelfHosted
{
   public class WcfServiceHTTPSelfHosted : IWcfServiceHTTPSelfHosted
   {
      [PrincipalPermission(SecurityAction.Demand, Role = "Managers")]
      public string GetData1(int value)
      {
         return string.Format("GetData1 you entered: {0}", value);
      }
 
      public string GetData2(int value)
      {
         if (System.Web.Security.Roles.IsUserInRole("Registered Users"))
         {
            return string.Format("GetData2 you entered: {0}", value);
         }
         else
         {
            throw new Exception("Role validation failed");
         }
      }
 
      public string GetData3(int value)
      {
         return string.Format("GetData3 you entered: {0}", value);
      }
   }
}

 

Dall'implemntazione dei metodi esposti dal contratto WCF si vede che GetData1 richiede che l'utente autenticato sia nel gruppo dei Managers, GetData2 sia tra i Registered Users e GetData3 richiede solo che l'utente sia autenticato. 
WcfServiceHTTPSelfHostedSqlMemberShip.zip

Blogs Parent Separator My Blog
Author
lupok

My Blog

Tags