Exemple d'application métier pour Silverlight 3 et .NET RIA Services - Partie 26 - Authentification et personnalisation

| Brad Abrams | Philippe Vialatte (Traduction) |

Cet article fait partie d'une série de traductions d'articles de Brad Abrams sur le développement d'applications métier avec Silverlight et .NET RIA Services.

Retrouvez l'ensemble des articles de la série sur cette page : Exemple d'application métier pour Silverlight 3 et .NET RIA Services.

Commentez cet article : Commentez . ♪

- Titre : Exemple d'application métier pour Silverlight 3 et .NET RIA Services - Partie 26 - Authentification et personnalisation

- Auteurs : Brad Abrams, Philippe Vialatte (Traduction)

- Parution : 25 mars 2011

- Mise à jour : 25 mars 2011

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2011 VIALATTE, Philippe. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.

Sommaire

- I. Introduction

- II. L'authentification de base

- III. Personnalisation

- IV. Interface d'administration

- V. Conclusion

- VI. Remerciements

I - 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) :

- VS2008 SP1;

- Silverlight 3 RTM;

- .NET RIA Services,

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

II - 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.

[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 :

[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é.

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

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 :

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

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é.

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();
};
}

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

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

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.

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

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

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 :

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.

<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é.

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é.

<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.

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 :

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

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

<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>

private void TextBlock_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
new LoginWindow().Show();
}

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.

III - 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.

<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.

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 :

<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 :

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 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.

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.

<Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}"
DataContext="{StaticResource RiaContext}"
Background="{Binding Path=User.PageBackgroundColor}">

Puis, quand nous nous connectons :

Et si nous changeons la couleur en bleu :

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

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

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

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 :

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

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.

<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.

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.

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…

protected override void OnNavigatedTo(NavigationEventArgs e)
{
LoadUserState();

Et lorsque l'utilisateur ouvre une session.

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é.

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.

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 :

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

puis qu'on se déconnecte.

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

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

IV - 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.

[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.

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.

<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.

<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?

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 ».

Sélectionnons « Sécurité ».

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

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

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.

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.

V - Conclusion

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

VI - Remerciements

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