1. Introduction

Les données avec lesquelles nous travaillons dans des applications métier sont précieuses. Nous avons besoin de les protéger, parfois en suivant de près qui accède et met à jour les données et d'autres fois en empêchant activement l'accès aux données par des tiers dans lesquels on n'a pas confiance.

Le Web est de plus en plus un lieu qui contient des données personnelles - les applications vous « connaissent » la plupart du temps, ce qui permet aux utilisateurs d'avoir des paramètres personnalisés qui seront partagés par toutes leurs applications.

Dans cet exemple, je vais partir de l'application toujours plus populaire SuperEmployees et l'améliorer pour avoir plus de détails sur l'authentification et la personnalisation.

Vous pouvez voir la série complète ici .

Cette démo nécessite les éléments suivants (tout est 100% gratuit) :

Télécharger tous les fichiers de la démo

2. L'authentification de base

Commençons par voir comment nous assurer que seuls les utilisateurs authentifiés peuvent accéder aux données et tenir un log très simple de qui a eu accès aux données.

À partir de l'exemple original, examinons la possibilité d'ajouter l'authentification à la méthode GetSuperEmployees() sur DomainService dans le projet serveur.

 
Sélectionnez
    [RequiresAuthentication]
public IQueryable<SuperEmployee> GetSuperEmployees()
{

Une fois que nous ajoutons l'attribut RequiresAuthentication, le système fera en sorte que seuls les appels provenant d'utilisateurs authentifiés puissent passer. Cela signifie que nous pouvons ensuite faire très simplement des actions telles que le Loger qui accède aux données et quand :

 
Sélectionnez
    [RequiresAuthentication]
public IQueryable<SuperEmployee> GetSuperEmployees()
{
    File.AppendAllText(@"C:\users\brada\desktop\userslog.txt",
       String.Format("{0}: {1} {2}", DateTime.Now,
       ServiceContext.User.Identity.Name, Environment.NewLine));

Vous pouvez utiliser une bibliothèque telle que Log4net pour une solution de log complète.

Maintenant, lorsque nous exécutons l'application, plus aucun résultat n'est retourné.

Image non disponible

Nous avons besoin de nous connecter pour voir des résultats… Heureusement, le Template Business Application livré avec. NET RIA Services inclut le support idéal pour cela.

Cliquez sur connexion

Image non disponible

Remarquez qu'ici, nous pourrions ne pas utiliser l'authentification Windows pour utiliser a la place la sécurité NTLM, les deux façons fonctionnent très bien.

Maintenant, nous allons nous inscrire :

Image non disponible

et nous pouvons créer un nouvel utilisateur directement depuis le client Silverlight.

Image non disponible

Notez que si vous souhaitez personnaliser le look and feel d'un de ces dialogues, il est facile de le faire depuis Views\LoginControl.xaml, Views\LoginWindow.xaml.

Et si vous voulez contrôler le côté serveur sur la façon dont ceux-ci sont mis en œuvre, vous le pouvez, en regardant dans le projet serveur sous la rubrique Services\AuthenticationService.cs and UserRegistrationService.cs.

Par défaut, cela va à l'encontre du système de rôles et de membres d'ASP.NET, mais vous pouvez les personnaliser comme vous le voulez en substituant simplement ces méthodes-là.

Maintenant, nous avons juste besoin de réagir à l'événement de connexion.

Dans ce cas, je vais tout simplement recharger les données lorsque l'utilisateur se connecte. Les lignes 10 à 13 permettent de s'inscrire à l'événement de connexion, puis recharge les données comme, cette fois-ci, un utilisateur authentifié.

 
Sélectionnez
public Home()
{
    InitializeComponent();

    var context = dds.DomainContext as SuperEmployeeDomainContext;
    originFilterBox.ItemsSource = context.Origins;

    context.Load(context.GetOriginsQuery());

    RiaContext.Current.Authentication.LoggedIn += (s, e) =>
    {
        if (dds != null) dds.Load();
    };
}
Image non disponible

Et remarquez que maintenant, le client sait qui je suis :

Image non disponible

Et le serveur le sait tout autant... Si vous allez voir le fichier journal que nous créons dans le DomainService nous verrons :

Image non disponible

Donc, c'est cool, mais je pense que nous pouvons faire un peu mieux au niveau de l'expérience utilisateur du client. Après tout, je ne reçois aucune erreur pour me dire que je dois ouvrir une session pour voir les données.

Tout d'abord, nous allons suivre les meilleures pratiques pour gérer l'événement DDS.LoadedData et montrer simplement toutes les erreurs qui sont retournées.

 
Sélectionnez
<riaControls:DomainDataSource x:Name="dds"
    AutoLoad="True"
    QueryName="GetSuperEmployeesQuery"
    LoadedData="dds_LoadedData"
    LoadSize="20">

Ensuite, la mise en œuvre est très simple :

 
Sélectionnez

private void dds_LoadedData(object sender, LoadedDataEventArgs e)
{  
    if (e.Error != null)
    {
       var win = new ErrorWindow(e.Error);
       win.Show();
    }
}

Maintenant, quand nous exécutons cette application, nous obtenons cette erreur :

Image non disponible

C'est utile, peut-être pour un développeur, mais pour un utilisateur final, nous voulons peut-être quelque chose de plus explicite.

La première étape est de ne même pas faire la demande si l'utilisateur n'est pas authentifié. Nous savons cela depuis le client, cela va donc être très facile à faire.

D'abord, inscrivez-vous pour l'événement DDS.DataLoading afin de capturer le chargement avant qu'il ne se produise.

 
Sélectionnez
<riaControls:DomainDataSource x:Name="dds"
   AutoLoad="True"
   QueryName="GetSuperEmployeesQuery"
   LoadedData="dds_LoadedData"
   LoadingData="dds_LoadingData"
   LoadSize="20">

Puis nous allons simplement annuler le chargement si l'utilisateur n'est pas authentifié.

 
Sélectionnez
private void dds_LoadingData(object sender, LoadingDataEventArgs e)
{
    e.Cancel = !RiaContext.Current.User.IsAuthenticated;
}

Maintenant, nous allons fournir un autre moyen de dire à l'utilisateur qu'il a besoin de se connecter. Nous allons tout simplement ajouter un peu de texte et le rendre visible uniquement lorsque l'utilisateur n'est pas authentifié.

 
Sélectionnez
<TextBlock Text="Data is only available to authenticated users" Foreground="Red"
      DataContext="{StaticResource RiaContext}"
      Visibility="{Binding Path=User.IsAuthenticated, Converter={StaticResource VisibilityConverter}}">
</TextBlock>

La mise en œuvre de la valeur de conversion est assez simple.

 
Sélectionnez
public class VisibilityConverter : IValueConverter
{
   public object Convert(
       object value,
       Type targetType,
       object parameter,
       CultureInfo culture)
   {
       bool visibility = (bool)value;
       return visibility ? Visibility.Collapsed : Visibility.Visible;
   }

   public object ConvertBack(
       object value,
       Type targetType,
       object parameter,
       CultureInfo culture)
   {
       Visibility visibility = (Visibility)value;
       return (visibility != Visibility.Visible);
   }
}

Maintenant, quand nous exécutons l'application, nous obtenons une belle interface :

Image non disponible

Puis, quand nous nous connectons, c'est très joli.

Image non disponible

Nous pouvons même faire un peu mieux en aidant les utilisateurs à se connecter facilement à partir d'ici :

 
Sélectionnez
<TextBlock Text="Data is only available to authenticated users.  Please click here to log in." Foreground="Red"
      DataContext="{StaticResource RiaContext}"
      Visibility="{Binding Path=User.IsAuthenticated, Converter={StaticResource VisibilityConverter}}"
      MouseLeftButtonUp="TextBlock_MouseLeftButtonUp">
</TextBlock>
 
Sélectionnez
private void TextBlock_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
   new LoginWindow().Show();
}
Image non disponible

Ce que nous avons montré dans cette section est à quel point il est facile de configurer l'authentification pour les données et de créer une super expérience utilisateur sur le client.

3. Personnalisation

Maintenant que nous avons les bases de l'authentification en place, nous allons voir comment nous pouvons fournir une expérience personnalisée. Pour de nombreuses applications, les utilisateurs passent beaucoup de temps dans l'application, nous voulons qu'ils se sentent à l'aise et qu'ils contrôlent leur environnement. Avant tout, nous allons créer un réglage utilisateur pour la couleur de fond de l'application. Chaque utilisateur peut avoir une valeur différente qui devrait la suivre quelle que soit la machine sur laquelle il exécute l'application.

Commençons par la définition d'une propriété de profil dans le fichier web.config.

 
Sélectionnez

<profile enabled="true" >
<properties>
    <add name="PageBackgroundColor"  defaultValue="White"/>
</properties>
</profile>

Ensuite, nous allons ajouter une propriété au fichier AuthenticationService.cs sur le serveur.

 
Sélectionnez
public class User : UserBase
{
   public string PageBackgroundColor { get; set; }
}

Maintenant, nous pouvons tout simplement accéder à cette propriété sur le client. D'abord nous allons définir une page pour définir cette valeur. Dans MyFirstPage.xaml, ajoutons quelques contrôles :

 
Sélectionnez
<StackPanel Orientation="Horizontal" >
   <TextBlock Text="Enter background color: "/>
   <TextBox x:Name="colorTextBox" KeyDown="colorTextBox_KeyDown" Width="100" />
   <Button Content="Save" Click="Button_Click" />
</StackPanel>
<TextBlock x:Name="saveStatus"/>

Nous pouvons gérer le clic sur le bouton comme suit :

 
Sélectionnez
private void Button_Click(object sender, RoutedEventArgs e)
{
   string colorString = this.colorTextBox.Text.Trim().ToLower();
   colorString = colorString.Substring(0, 1).ToUpper() + colorString.Substring(1, colorString.Length - 1);
   RiaContext.Current.User.PageBackgroundColor = colorString;
   this.saveStatus.Text = "setting saving..";
   RiaContext.Current.Authentication.SaveUser((o) =>
               { this.saveStatus.Text = "setting saved"; },
     null);
}

private void colorTextBox_KeyDown(object sender, KeyEventArgs e)
{
   this.saveStatus.Text = "";
}

Notez qu'aux lignes 3-4 nous normalisons le nom de chaîne de la couleur de sorte à ce qu'il soit « XAML compliant ».Puis à la ligne 5 nous affectons la valeur à mettons en place dans la propriété User.PageBackgroundColor. Enfin, dans les lignes 6-9, nous ne faisons que donner des indications à l'utilisateur pendant que nous sauvons cette valeur sur le serveur.

Bien sûr, cela ne fonctionnera que si l'utilisateur est déjà connecté, alors cette fois, soyons proactifs et encourageons l'utilisateur à se connecter quand il accède à la page pour la première fois.

 
Sélectionnez
protected override void OnNavigatedTo(NavigationEventArgs e)
{
   if (!RiaContext.Current.User.IsAuthenticated)
   {
       new LoginWindow().Show();
   }
}

La dernière étape est ici de prendre en compte cette valeur quand elle est fournie. Cela s'avère assez facile dans ce cas. Il suffit d'aller dansMainPage.xaml et de lier la couleur de fond du LayoutRoot à cette valeur.

 
Sélectionnez
<Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}"
    DataContext="{StaticResource RiaContext}"
   Background="{Binding Path=User.PageBackgroundColor}">
Image non disponible

Puis, quand nous nous connectons :

Image non disponible

Et si nous changeons la couleur en bleu :

Image non disponible

Et remarquez que le changement de couleur affecte l'application entière.

Image non disponible

Et si j'accède à l'application depuis une autre machine, sur un autre navigateur, ma configuration est préservée. Avant de se connecter, nous obtenons la valeur par défaut :

Image non disponible

mais quand nous nous connectons… nos paramètres apparaissent.

Image non disponible

Maintenant, comme c'est un paramètre utilisateur, si je crée un nouvel utilisateur "Glenn" et que je définis sa couleur d'arrière-plan en rose :

Image non disponible

cela n'affecte pas la couleur de fond pour Darb…

Image non disponible
Image non disponible

OK, la couleur de fond c'est rigolo, mais ce qui pourrait être encore plus utile est de stocker la façon dont j'ai quitté l'application. Cela garantit que lorsque j'accède à l'application dans le futur, le contexte de mon travail est préservé.

Donc, nous allons ajouter quelques champs en plus à notre profil.

 
Sélectionnez
<profile enabled="true" >
 <properties>
   <add name="PageBackgroundColor"  defaultValue="White"/>
   <add name="SortOrder"  type="Int32" defaultValue="0"/>
   <add name="SortProperty"  defaultValue="Name"/>
   <add name="OriginFilter"  defaultValue=""/>
 </properties>
</profile>

Puis mettre à jour la classe utilisateur.

 
Sélectionnez
public class User : UserBase
{
   public string PageBackgroundColor { get; set; }
   public int SortOrder { get; set; }
   public string SortProperty { get; set; }
   public string OriginFilter { get; set; }
}

Nous avons besoin de définir l'interface utilisateur en fonction des paramètres de l'utilisateur.

 
Sélectionnez
void LoadUserState()
{
   var user = RiaContext.Current.User;
   if (user.OriginFilter != null)
       originFilterBox.Text = user.OriginFilter;
   else
       originFilterBox.Text = string.Empty;

   if (user.SortProperty != null)
   {
       dds.SortDescriptors.Add(new SortDescriptor(user.SortProperty,
                               (SortDirection)user.SortOrder));
   }
}

Et nous avons besoin d'y faire appel lorsque l'on accède à la page…

 
Sélectionnez
protected override void OnNavigatedTo(NavigationEventArgs e)
{
   LoadUserState();

Et lorsque l'utilisateur ouvre une session.

 
Sélectionnez
RiaContext.Current.Authentication.LoggedIn += (s, e) =>
{
   User user = RiaContext.Current.User;
   if (dds != null)
   {
       dds.Load();
       LoadUserState();
   }
};

Ensuite nous avons besoin de stocker les valeurs sur le serveur au bon moment. Cette méthode SaveUserState récupère les bonnes valeurs depuis l'interface utilisateur et les sauvegarde si les valeurs ont changé.

 
Sélectionnez
string lastSave;
void SaveUserState()
{
   User user = RiaContext.Current.User;
   if (!user.IsAuthenticated) return;

   var order = dds.SortDescriptors.LastOrDefault();
   if (order != null)
   {
       user.SortProperty = order.PropertyPath.Value.ToString();
       user.SortOrder = (int)order.Direction;
   }
   user.OriginFilter = this.originFilterBox.Text;

   if (lastSave != user.SortProperty + user.SortOrder + user.OriginFilter)
   {
        RiaContext.Current.Authentication.SaveUser();
        lastSave = user.SortProperty + user.SortOrder + user.OriginFilter;
   } 
}

Nous avons besoin d'appeler cette méthode lorsque l'utilisateur navigue vers l'extérieur.

 
Sélectionnez
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
   SaveUserState();
}

Et, périodiquement, nous vérifions si nous avons besoin d'enregistrer les modifications sur le serveur. Nous avons donc ajouté cela dans le constructeur de formes :

 
Sélectionnez
Timer = new DispatcherTimer();
Timer.Interval = TimeSpan.FromSeconds(10);
Timer.Tick += (o, e) => SaveUserState();
Timer.Start();

Maintenant, quand nous exécutons l'application et qu'on configure l'ordre de tri et un filtre

Image non disponible

puis qu'on se déconnecte.

Image non disponible

Lorsque nous nous connectons à nouveau (depuis une autre machine) nous voyons que l'application nous renvoie là où nous nous sommes quittés.

Image non disponible

Nous avons vu dans cette section comment personnaliser l'expérience utilisateur sur la base des préférences de celui-ci.

4. Interface d'administration

Dans cette dernière section, penchons-nous sur la façon de construire une interface d'administration. Ce que nous voulons faire est de fournir une page qui permet aux administrateurs de voir tous les utilisateurs et de modifier leurs paramètres de profil.

Tout d'abord, allons dans AuthenticationService ajouter des méthodes qui vont renvoyer tous les utilisateurs. Nous devons être sûrs que seuls les utilisateurs ayant le rôle d'administrateur peuvent accéder à ce service.

 
Sélectionnez
[EnableClientAccess]
public class AuthenticationService : AuthenticationBase<User>
{
   [RequiresRoles("Admin")]
   public IEnumerable<User> GetAllUsers()
   {
       return Membership.GetAllUsers().Cast<MembershipUser>().Select(mu => this.GetUserForMembershipUser(mu));
   }

   private User GetUserForMembershipUser(MembershipUser membershipUser)
   {
       return this.GetAuthenticatedUser(
           new GenericPrincipal(new GenericIdentity(membershipUser.UserName), new string[0]));
   }

Maintenant, ajoutons une interface utilisateur en Silverlight pour utiliser ces infos. Nous allons créer une nouvelle page appelée "Admin". La première chose que nous voulons faire est de demander à l'utilisateur de se connecter s'il n'est pas déjà identifié en tant qu'utilisateur ayant un rôle d'administrateur.

 
Sélectionnez
protected override void OnNavigatedTo(NavigationEventArgs e)
{
   if (!RiaContext.Current.User.Roles.Contains("Admin"))
   {
       new LoginWindow().Show();
       RiaContext.Current.Authentication.LoggedIn += (s, ev) =>
       {
           if (dds != null) dds.Load();
       };
   }
}

Ensuite, nous définissons un DomainDataSource pour accéder à AuthenticationService.

 
Sélectionnez
<riaControls:DomainDataSource x:Name="dds"
       AutoLoad="True"
       QueryName="GetAllUsersQuery"
       LoadSize="20">

   <riaControls:DomainDataSource.DomainContext>
       <App:AuthenticationContext/>
   </riaControls:DomainDataSource.DomainContext>

</riaControls:DomainDataSource>

Puis nous définissons des interfaces graphiques simples pour travailler avec les données.

 
Sélectionnez
<activity:Activity IsActive="{Binding IsBusy, ElementName=dds}">
   <StackPanel>
       <dataControls:DataForm x:Name="dataForm1"
                              Height="393" Width="331"
          VerticalAlignment="Top"       
          Header="User Data"
          ItemsSource="{Binding Data, ElementName=dds}"                     
          HorizontalAlignment="Left" >
       </dataControls:DataForm>

       <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
           <Button Content="Submit" Width="105" Height="28"
             Click="SubmitButton_Click"    />

       </StackPanel>

   </StackPanel>
</activity:Activity> 

Maintenant, nous lançons l'application... On se connecte, mais on ne voit pas toutes les données, pourquoi?

Image non disponible

Well, the user we created is not an Admin.

Eh bien, l'utilisateur que nous avons créé n'est pas un administrateur. Pour en faire une administration, accédons à l'outil Web Admin et ajoutons-le au rôle "Admin".

Image non disponible

Sélectionnons "Sécurité".

Image non disponible

Puis, sous les rôles, ajoutons un nouveau rôle pour "Admin"

Image non disponible

et sous Utilisateurs, "Gestionnaire des utilisateurs", ici vous pouvez facilement ajouter votre utilisateur au rôle.

Image non disponible

Maintenant, quand j'ouvre une session et que j'accède à la page d'administration, je peux accéder à l'ensemble des paramètres utilisateur.

Image non disponible

Cette section vous a montré comment construire une interface d'administration pour vos applications.

J'espère que vous avez trouvé cet article utile pour prendre en charge l'authentification et la personnalisation des services RIA. Encore une fois, vous pouvez télécharger les fichiers démo complète ou consultez la série complète ici.

5. Conclusion

Cet article conclut la série. Pour retrouver les autres articles de la série, rendez-vous sur la page récapitulative.

6. Remerciements

Je tiens ici à remercier Brad Abrams pour nous avoir autorisé à traduire son article.
Je remercie également jacques_jean pour sa relecture et ses propositions.