Je suis venu à travers un problème avec la liaison à un P asswordBox
. Il semble que ce soit un risque pour la sécurité, mais j'utilise le modèle MVVM, donc je souhaite contourner cela. J'ai trouvé un code intéressant ici (quelqu'un a-t-il utilisé ceci ou quelque chose de similaire?)
http://www.wpftutorial.net/PasswordBox.html
Techniquement, il a fière allure, mais je ne sais pas comment récupérer le mot de passe.
J'ai essentiellement des propriétés dans mon LoginViewModel
pour Username
et Password
. Username
est bien et fonctionne comme c'est un TextBox
.
J'ai utilisé le code ci-dessus comme indiqué et entré ce
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
Quand je l' ai eu PasswordBox
comme TextBox
et Binding Path=Password
puis la propriété dans mon LoginViewModel
été mis à jour.
Mon code est très simple, en gros j'ai un Command
pour moi Button
. Lorsque j'appuie, il CanLogin
est appelé et s'il renvoie vrai, il appelle Login
.
Vous pouvez voir que je vérifie ma propriété Username
ici, ce qui fonctionne très bien.
Dans Login
j'envoie à mon service un Username
et Password
, Username
contient des données de mon View
mais Password
estNull|Empty
private DelegateCommand loginCommand;
public string Username { get; set; }
public string Password { get; set; }
public ICommand LoginCommand
{
get
{
if (loginCommand == null)
{
loginCommand = new DelegateCommand(
Login, CanLogin );
}
return loginCommand;
}
}
private bool CanLogin()
{
return !string.IsNullOrEmpty(Username);
}
private void Login()
{
bool result = securityService.IsValidLogin(Username, Password);
if (result) { }
else { }
}
C'est ce que je fais
<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
MinWidth="180" />
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
J'ai mon TextBox
, ce n'est pas un problème, mais dans mon ViewModel
le Password
est vide.
Suis-je en train de faire quelque chose de mal ou de manquer une étape?
J'ai mis un point d'arrêt et bien sûr le code entre dans la classe d'assistance statique mais il ne met jamais à jour mon Password
dans mon ViewModel
.
Réponses:
Désolé, mais vous vous trompez.
Les gens devraient se faire tatouer les lignes directrices de sécurité suivantes à l'intérieur de leurs paupières:
Ne conservez jamais de mots de passe en texte brut en mémoire.
La raison pour laquelle le WPF / Silverlight
PasswordBox
n'expose pas de DP pour laPassword
propriété est liée à la sécurité.Si WPF / Silverlight devait conserver un DP car
Password
il faudrait que le framework garde le mot de passe lui-même non chiffré en mémoire. Ce qui est considéré comme un vecteur d'attaque de sécurité assez gênant. LePasswordBox
utilise la mémoire cryptée (en quelque sorte) et la seule façon d'accéder au mot de passe est via la propriété CLR.Je suggérerais que lors de l'accès à la
PasswordBox.Password
propriété CLR, vous vous absteniez de la placer dans une variable ou en tant que valeur pour une propriété.Garder votre mot de passe en texte brut sur la RAM de l'ordinateur client est un non-non de sécurité.
Alors débarrassez-
public string Password { get; set; }
vous de ce que vous avez là-haut.Lors de l'accès
PasswordBox.Password
, sortez-le et expédiez-le au serveur dès que possible. Ne conservez pas la valeur du mot de passe et ne le traitez pas comme vous le feriez pour tout autre texte de machine client. Ne conservez pas de mots de passe en texte clair en mémoire.Je sais que cela rompt le modèle MVVM, mais vous ne devriez jamais vous lier à
PasswordBox.Password
Attached DP, stocker votre mot de passe dans le ViewModel ou tout autre manigance similaire.Si vous recherchez une solution sur-architecturée, en voici une:
1. Créez l'
IHavePassword
interface avec une méthode qui renvoie le texte clair du mot de passe.2. Demandez à votre
UserControl
implémenter uneIHavePassword
interface.3. Enregistrez l'
UserControl
instance auprès de votre IoC comme implémentant leIHavePassword
interface.4. Lorsqu'une demande de serveur nécessitant votre mot de passe est en cours, appelez votre IoC pour la
IHavePassword
mise en œuvre et obtenez uniquement le mot de passe tant convoité.Juste mon point de vue.
-- Justin
la source
Mes 2 cents:
J'ai développé une fois une boîte de dialogue de connexion typique (zones utilisateur et mot de passe, plus le bouton "Ok") en utilisant WPF et MVVM. J'ai résolu le problème de liaison de mot de passe en passant simplement le contrôle PasswordBox lui-même comme paramètre à la commande attachée au bouton "Ok". Donc, selon moi, j'avais:
Et dans le ViewModel, la
Execute
méthode de la commande jointe était la suivante:Cela viole légèrement le modèle MVVM puisque maintenant le ViewModel sait quelque chose sur la façon dont la vue est implémentée, mais dans ce projet particulier, je pouvais me le permettre. J'espère que c'est utile pour quelqu'un aussi.
la source
Peut-être que je manque quelque chose, mais il semble que la plupart de ces solutions compliquent les choses et suppriment les pratiques sécurisées.
Cette méthode ne viole pas le modèle MVVM et maintient une sécurité complète. Oui, techniquement, c'est du code, mais ce n'est rien de plus qu'une liaison "cas spécial". Le ViewModel n'a toujours aucune connaissance de l'implémentation de View, ce qui, à mon avis, le fait si vous essayez de transmettre le PasswordBox au ViewModel.
Code Behind! = Violation automatique de MVVM. Tout dépend de ce que vous en faites. Dans ce cas, nous codons juste manuellement une liaison, donc tout cela fait partie de l'implémentation de l'interface utilisateur et est donc correct.
Dans le ViewModel, juste une propriété simple. Je l'ai fait "écrire seulement" car il ne devrait pas être nécessaire de le récupérer de l'extérieur du ViewModel pour une raison quelconque, mais ce n'est pas obligatoire. Notez qu'il s'agit d'une SecureString, pas seulement d'une chaîne.
Dans le xaml, vous configurez un gestionnaire d'événements PasswordChanged.
Dans le code derrière:
Avec cette méthode, votre mot de passe reste à tout moment dans une SecureString et offre donc une sécurité maximale. Si vous ne vous souciez vraiment pas de la sécurité ou si vous avez besoin du mot de passe en texte clair pour une méthode en aval qui l'exige (remarque: la plupart des méthodes .NET qui nécessitent un mot de passe prennent également en charge une option SecureString, donc vous n'aurez peut-être pas vraiment besoin d'un mot de passe en texte clair même si vous pensez le faire), vous pouvez simplement utiliser la propriété Password à la place. Comme ça:
(Propriété ViewModel)
(Code derrière)
Si vous vouliez garder les choses fortement tapées, vous pouvez remplacer la distribution (dynamique) par l'interface de votre ViewModel. Mais vraiment, les liaisons de données "normales" ne sont pas non plus fortement typées, donc ce n'est pas si grave.
Donc, le meilleur de tous les mondes - votre mot de passe est sécurisé, votre ViewModel a juste une propriété comme toute autre propriété, et votre View est autonome sans aucune référence externe requise.
la source
Vous pouvez utiliser ce XAML:
Et cette méthode d'exécution de commande:
la source
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}"
(remarque: nonRelativeSource Self
).Cela fonctionne très bien pour moi.
la source
ICommand
soit implémenté dans le modèle de vue, cette solution violerait le modèle MVVM.Une solution simple sans violer le modèle MVVM consiste à introduire un événement (ou délégué) dans le ViewModel qui récolte le mot de passe.
Dans le ViewModel :
public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;
avec ces EventArgs:
dans la vue , abonnez-vous à l'événement lors de la création du ViewModel et saisissez la valeur du mot de passe.
Dans le ViewModel , lorsque vous avez besoin du mot de passe, vous pouvez déclencher l'événement et récupérer le mot de passe à partir de là:
la source
WeakEventManager<TEventSource, TEventArgs>
pour éviter les fuites de mémoire. Souvent, la vue n'a pas la même durée de vie que le modèle de vue.WeakEventManager<IViewModel, EventArgs>.AddHandler(iViewModelInstance, nameof(IViewModel.Event), eventHandlerMethod);
J'ai passé beaucoup de temps à étudier différentes solutions. Je n'aimais pas l'idée des décorateurs, les comportements gâchent l'interface utilisateur de validation, le code derrière ... vraiment?
La meilleure solution consiste à vous en tenir à une propriété attachée personnalisée et à la lier à votre
SecureString
propriété dans votre modèle de vue. Gardez-le aussi longtemps que vous le pouvez. Chaque fois que vous aurez besoin d'un accès rapide au mot de passe ordinaire, convertissez-le temporairement en une chaîne non sécurisée à l'aide du code ci-dessous:Assurez-vous d'autoriser le GC à collecter votre élément d'interface utilisateur, afin de résister à l'envie d'utiliser un gestionnaire d'événements statique pour l'
PasswordChanged
événement sur lePasswordBox
. J'ai également découvert une anomalie où le contrôle neSecurePassword
mettait pas à jour l'interface utilisateur lors de l'utilisation de la propriété pour la configurer, raison pour laquelle je copie le mot de passe à laPassword
place.Et l'utilisation de XAML:
Ma propriété dans le modèle de vue ressemblait à ceci:
Il
RequiredSecureString
s'agit simplement d'un simple validateur personnalisé qui a la logique suivante:Ici vous l'avez. Une solution MVVM pure complète et testée.
la source
J'ai posté un GIST ici qui est une boîte de mot de passe pouvant être liée.
la source
ContentControl
vous pouvez alors simplement utiliser un PasswordBox en tant que contenu et style en XAML comme vous le souhaitez. Le but deContentControl
est simplement de s'abonner à l'PasswordChanged
événement et d'exposer une propriété pouvant être liée dans deux directions. Dans l'ensemble, c'est 65 lignes de code et à peu près ce que fait cette classe de décoration. Voir ici pour mon résumé des gist.github.com/leidegre/c7343b8c720000fe3132Cette implémentation est légèrement différente. Vous passez une boîte de mot de passe à la liaison View via une propriété dans ViewModel, elle n'utilise aucun paramètre de commande. Le ViewModel reste ignorant de la vue. J'ai un projet VB vs 2010 qui peut être téléchargé depuis SkyDrive. Wpf MvvM PassWordBox Example.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511
La façon dont j'utilise PasswordBox dans une application Wpf MvvM est assez simpliste et fonctionne bien pour moi. Cela ne signifie pas que je pense que c'est la bonne ou la meilleure façon. Il s'agit simplement d'une implémentation de l'utilisation de PasswordBox et du modèle MvvM.
Fondamentalement, vous créez une propriété publique en lecture seule à laquelle la vue peut se lier en tant que PasswordBox (le contrôle réel) Exemple:
J'utilise un champ de support juste pour faire l'auto initialisation de la propriété.
Ensuite, à partir de Xaml, vous liez le contenu d'un ContentControl ou d'un exemple de conteneur de contrôle:
De là, vous avez le contrôle total de la boîte de mot de passe. J'utilise également un PasswordAccessor (juste une fonction de chaîne) pour renvoyer la valeur du mot de passe lorsque vous vous connectez ou tout ce que vous voulez pour le mot de passe. Dans l'exemple, j'ai une propriété publique dans un modèle d'objet utilisateur générique. Exemple:
Dans l'objet utilisateur, la propriété de chaîne de mot de passe est en lecture seule sans aucun magasin de sauvegarde, elle renvoie simplement le mot de passe à partir de PasswordBox. Exemple:
Ensuite, dans le ViewModel, je m'assure que l'accesseur est créé et défini sur la propriété PasswordBox.Password 'Exemple:
Lorsque j'ai besoin que la chaîne de mot de passe dise pour la connexion, j'obtiens simplement la propriété Mot de passe des objets utilisateur qui invoque vraiment la fonction pour récupérer le mot de passe et le renvoyer, puis le mot de passe réel n'est pas stocké par l'objet utilisateur. Exemple: serait dans le ViewModel
Ça devrait le faire. Le ViewModel n'a besoin d'aucune connaissance des contrôles de la vue. La vue se lie simplement à la propriété du ViewModel, pas différente de la liaison de la vue à une image ou à une autre ressource. Dans ce cas, cette ressource (propriété) se trouve être un contrôle utilisateur. Il permet de tester lorsque le ViewModel crée et possède la propriété et que la propriété est indépendante de la vue. Quant à la sécurité, je ne sais pas à quel point cette implémentation est bonne. Mais en utilisant une fonction, la valeur n'est pas stockée dans la propriété elle-même à laquelle elle accède.
la source
Pour résoudre le problème OP sans casser la MVVM, j'utiliserais un convertisseur de valeur personnalisé et un wrapper pour la valeur (le mot de passe) qui doit être récupérée dans la boîte de mot de passe.
Dans le modèle de vue:
Comme le modèle de vue utilise
IWrappedParameter<T>
, il n'a pas besoin de connaîtrePasswordBoxWrapper
niPasswordBoxConverter
. De cette façon, vous pouvez isoler lePasswordBox
objet du modèle de vue et ne pas casser le modèle MVVM.Dans la vue:
la source
Bien que je convienne qu'il est important d'éviter de stocker le mot de passe n'importe où, j'ai toujours besoin de pouvoir instancier le modèle de vue sans vue et exécuter mes tests par rapport à celui-ci.
La solution qui a fonctionné pour moi a été d'enregistrer la fonction PasswordBox.Password avec le modèle de vue et de l'invoquer lors de l'exécution du code de connexion.
Cela ne signifie une ligne de code dans le code - behind de la vue.
Donc, dans mon Login.xaml, j'ai
et dans Login.xaml.cs j'ai
puis dans LoginViewModel.cs j'ai le PasswordHandler défini
et lorsque la connexion doit se produire, le code invoque le gestionnaire pour obtenir le mot de passe de la vue ...
De cette façon, lorsque je veux tester le viewmodel, je peux simplement définir PasswordHandler sur une méthode anonyme qui me permet de fournir le mot de passe que je veux utiliser dans le test.
la source
J'ai pensé que je jetterais ma solution dans le mélange, car c'est un problème si courant ... et avoir beaucoup d'options est toujours une bonne chose.
J'ai simplement enveloppé un
PasswordBox
dans unUserControl
et mis en œuvre unDependencyProperty
pour pouvoir lier. Je fais tout ce que je peux pour éviter de stocker du texte clair dans la mémoire, donc tout se fait via aSecureString
et laPasswordBox.Password
propriété. Pendant laforeach
boucle, chaque personnage est exposé, mais c'est très bref. Honnêtement, si vous craignez que votre application WPF soit compromise par cette brève exposition, vous avez des problèmes de sécurité plus importants qui doivent être traités.La beauté de ceci est que vous ne violez aucune règle MVVM, même celles "puristes", car c'est un
UserControl
, donc il est permis d'avoir du code-behind. Lorsque vous l'utilisez, vous pouvez avoir une communication pure entreView
etViewModel
sans que vous soyez auVideModel
courant d'une partieView
ou de la source du mot de passe. Assurez-vous simplement que vous vous liez àSecureString
votreViewModel
.BindablePasswordBox.xaml
BindablePasswordBox.xaml.cs (Version 1 - Pas de prise en charge de la liaison bidirectionnelle.)
Utilisation de la version 1:
BindablePasswordBox.xaml.cs (Version 2 - Prend en charge la liaison bidirectionnelle.)
Utilisation de la version 2:
la source
if (Password != secure)
sera toujours faux car SecureString ne remplace pas égal. Des pensées?vous pouvez le faire avec la propriété attachée, voyez-le .. PasswordBox avec MVVM
la source
J'ai utilisé cette méthode et passé la boîte de mot de passe, bien que cela viole la MVVM, c'était essentiel pour moi car j'utilisais un contrôle de contenu avec un modèle de données pour ma connexion dans mon shell qui est un environnement de shell complexe. Donc, accéder au code derrière le shell aurait été une merde.
Passer la boîte de mots de passe, je pense que c'est la même chose que d'accéder au contrôle du code derrière autant que je sache. J'accepte les mots de passe, ne les garde pas en mémoire, etc. Dans cette implémentation, je n'ai pas de propriété pour le mot de passe dans le modèle d'affichage.
Commande de bouton
ViewModel
la source
Pour moi, ces deux choses semblent fausses:
PasswordBox
tant que paramètre de commande au ViewModelLe transfert du SecurePassword (instance SecureString) comme décrit par Steve dans CO semble acceptable. je préfère
Behaviors
coder derrière, et j'avais également l'exigence supplémentaire de pouvoir réinitialiser le mot de passe à partir du viewmodel.Xaml (
Password
est la propriété ViewModel):Comportement:
la source
Pour les débutants comme moi, voici un échantillon de travail complet de ce qui est
Konamiman
suggéré ci-dessus. MerciKonamiman
.XAML
ViewModel
la source
C'est une propriété attenante . Ce type de propriété peut être appliqué à tout type de
DependencyObject
, et pas seulement au type dans lequel il est déclaré. Ainsi, même s'il est déclaré dans laPasswordHelper
classe statique, il est appliqué à celuiPasswordBox
sur lequel vous l'utilisez.Pour utiliser cette propriété jointe, il vous suffit de la lier à la
Password
propriété de votre ViewModel:la source
J'ai fait comme:
XAML:
C #:
Ça marche pour moi!
la source
Comme mentionné précédemment, VM ne devrait pas être au courant de la vue, mais passer toute PasswordBox ressemble à l'approche la plus simple. Alors peut-être qu'au lieu de transtyper le paramètre passé dans PasswordBox, utilisez Reflection pour en extraire la propriété Password. Dans ce cas, VM attend une sorte de conteneur de mots de passe avec la propriété Password (j'utilise les RelayCommands de MVMM Light-Toolkit):
Il peut être facilement testé avec une classe anonyme:
la source
Dans l'application universelle Windows
vous pouvez utiliser ce code avec la propriété "Password" et la liaison avec le modelView
la source
Pour toute personne consciente des risques que cette implémentation comporte, pour que le mot de passe se synchronise avec votre ViewModel, ajoutez simplement Mode = OneWayToSource .
XAML
la source
OneWayToSource
?Voici mon point de vue:
L'utilisation d'une propriété attachée pour lier le mot de passe va à l'encontre de l'objectif de sécurisation du mot de passe. La propriété Password d'une zone de mot de passe n'est pas lisible pour une raison.
La transmission de la zone de mot de passe comme paramètre de commande rendra le ViewModel conscient du contrôle. Cela ne fonctionnera pas si vous envisagez de rendre votre plateforme multiplateforme ViewModel réutilisable. Ne faites pas connaître à votre machine virtuelle votre vue ou tout autre contrôle.
Je ne pense pas que l'introduction d'une nouvelle propriété, d'une interface, de l'abonnement à des événements modifiés par mot de passe ou de toute autre chose compliquée ne soit pas nécessaire pour une simple tâche de fourniture du mot de passe.
XAML
Code derrière - l'utilisation du code derrière ne viole pas nécessairement MVVM. Tant que vous n'y mettez aucune logique métier.
ViewModel
la source
Vous trouverez une solution pour PasswordBox dans l'exemple d'application ViewModel de WPF Application Framework (WAF) .
Cependant, Justin a raison. Ne passez pas le mot de passe en texte brut entre View et ViewModel. Utilisez plutôt SecureString (voir MSDN PasswordBox).
la source
J'ai utilisé une vérification d'authentification suivie d'un sous-appel appelé par une classe de médiateur à la vue (qui implémente également une vérification d'authentification) pour écrire le mot de passe dans la classe de données.
Ce n'est pas une solution parfaite; cependant, cela a résolu mon problème de ne pas pouvoir déplacer le mot de passe.
la source
J'utilise une solution conviviale MVVM succincte qui n'a pas encore été mentionnée. Tout d'abord, je nomme le PasswordBox en XAML:
Ensuite, j'ajoute un seul appel de méthode dans le constructeur de vue:
Et c'est tout. Le modèle de vue recevra une notification lorsqu'il est attaché à une vue via DataContext et une autre notification lorsqu'il est détaché. Le contenu de cette notification est configurable via les lambdas, mais il s'agit généralement d'un appel de setter ou de méthode sur le modèle de vue, en passant le contrôle problématique en paramètre.
Il peut être rendu très convivial pour MVVM en ayant l'interface d'affichage de vue au lieu des contrôles enfants.
Le code ci-dessus repose sur une classe d'assistance publiée sur mon blog.
la source
J'ai passé des heures à essayer de faire fonctionner ça. À la fin, j'ai abandonné et j'ai juste utilisé le PasswordBoxEdit de DevExpress.
C'est la solution la plus simple de tous les temps, car elle permet de relier sans tirer d'horribles trucs.
Solution sur le site Web DevExpress
Pour mémoire, je ne suis en aucun cas affilié à DevExpress.
la source
;) facile!
la source
C'est très simple . Créez une autre propriété pour le mot de passe et liez-la avec TextBox
Mais toutes les opérations d'entrée s'effectuent avec la propriété de mot de passe réelle
chaîne privée _Password;
chaîne publique Mot de passe {get {return _Password; }
la source
eh bien ma réponse est plus simple juste pour le modèle MVVM
en classe viewmodel
la propriété de mot de passe de PasswordBox fournie par win ou de WatermarkPasswordBox fournie par XCeedtoolkit génère un RoutedEventArgs afin que vous puissiez le lier.
maintenant en vue xmal
ou
la source
Envoyer un
SecureString
au modèle de vue à l'aide d'un comportement attaché etICommand
Il n'y a rien de mal avec le code-behind lors de l'implémentation de MVVM. MVVM est un modèle architectural qui vise à séparer la vue du modèle / logique métier. MVVM décrit comment atteindre cet objectif de manière reproductible (le modèle). Il ne se soucie pas des détails d'implémentation, comme comment structurer ou implémenter la vue. Il trace simplement les limites et définit ce qu'est la vue, le modèle de vue et ce que le modèle en termes de terminologie de ce modèle.
MVVM ne se soucie pas du langage (XAML ou C #) ou du compilateur (
partial
classes). Être indépendant de la langue est une caractéristique obligatoire d'un modèle de conception - il doit être indépendant de la langue.Cependant, le code-behind présente certains inconvénients, comme rendre votre logique d'interface utilisateur plus difficile à comprendre, lorsqu'elle est largement distribuée entre XAML et C #. Mais la plus importante implémentation de la logique de l'interface utilisateur ou des objets comme les modèles, les styles, les déclencheurs, les animations, etc. en C # est très complexe et laide / moins lisible que l'utilisation de XAML. XAML est un langage de balisage qui utilise des balises et une imbrication pour visualiser la hiérarchie des objets. La création d'interface utilisateur à l'aide de XAML est très pratique. Bien qu'il existe des situations où vous pouvez choisir d'implémenter la logique de l'interface utilisateur en C # (ou code-behind). Gérer le
PasswordBox
est un exemple.Pour cette raison, le traitement du
PasswordBox
code-behind en manipulant lePasswordBox.PasswordChanged
, n'est pas une violation du modèle MVVM.Une violation claire serait de passer un contrôle (le
PasswordBox
) au modèle de vue. De nombreuses solutions le recommandent, par exemple, la baie passant l'instance de l'PasswordBox
asICommand.CommandParameter
au modèle de vue. De toute évidence, une recommandation très mauvaise et inutile.Si vous ne vous souciez pas de l'utilisation de C #, mais que vous souhaitez simplement garder votre fichier code-behind propre ou simplement encapsuler une logique de comportement / d'interface utilisateur, vous pouvez toujours utiliser les propriétés attachées et implémenter un comportement attaché.
Opposé à la tristement célèbre aide à large diffusion qui permet de se lier au mot de passe en texte brut (très mauvais anti-modèle et risque de sécurité), ce comportement utilise un
ICommand
pour envoyer le mot de passe quantSecureString
au modèle d'affichage, chaque fois quePasswordBox
lePasswordBox.PasswordChanged
événement événement.MainWindow.xaml
ViewModel.cs
PasswordBox.cs
la source