我遇到了绑定到密码盒的问题。这似乎是一个安全风险,但我正在使用MVVM模式,所以我希望绕过这个。我在这里发现了一些有趣的代码(有人使用过这个或类似的代码吗?)
http://www.wpftutorial.net/PasswordBox.html
从技术上讲,它看起来很棒,但我不确定如何检索密码。
我基本上有属性在我的LoginViewModel用户名和密码。用户名是好的,正在工作,因为它是一个文本框。
我使用上面的代码,并输入这个
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
当我有PasswordBox作为一个文本框和绑定路径=密码,然后在我的LoginViewModel属性更新。
我的代码非常简单,基本上我有一个命令为我的按钮。当我按下它CanLogin被调用,如果它返回真,它调用Login。
你可以看到,我检查了我的用户名属性,这很好。
在登录我发送到我的服务的用户名和密码,用户名包含数据从我的视图,但密码是空|空
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 { }
}
这就是我正在做的
<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
MinWidth="180" />
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
我有我的文本框,这是没有问题的,但在我的ViewModel密码是空的。
是我做错了什么还是漏了一步?
我放了一个断点,果然代码进入静态助手类,但它从不更新我的ViewModel中的密码。
以下是我的看法:
Using an attached property to bind the password defeats the purpose of securing the password. The Password property of a password box is not bindable for a reason.
Passing the password box as command parameter will make the ViewModel aware of the control. This will not work if you plan to make your ViewModel reusable cross platform. Don't make your VM aware of your View or any other controls.
I don't think introducing a new property, an interface, subscribing to password changed events or any other complicated things is necessary for a simple task of providing the password.
XAML
<PasswordBox x:Name="pbPassword" />
<Button Content="Login" Command="{Binding LoginCommand}" x:Name="btnLogin"/>
代码背后——使用代码背后并不一定违反MVVM。只要你不把任何业务逻辑放在里面。
btnLogin.CommandParameter = new Func<string>(()=>pbPassword.Password);
ViewModel
LoginCommand = new RelayCommand<Func<string>>(getpwd=> { service.Login(username, getpwd()); });
为了在不破坏MVVM的情况下解决OP问题,我将使用自定义值转换器和必须从密码框中检索的值(密码)的包装器。
public interface IWrappedParameter<T>
{
T Value { get; }
}
public class PasswordBoxWrapper : IWrappedParameter<string>
{
private readonly PasswordBox _source;
public string Value
{
get { return _source != null ? _source.Password : string.Empty; }
}
public PasswordBoxWrapper(PasswordBox source)
{
_source = source;
}
}
public class PasswordBoxConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Implement type and value check here...
return new PasswordBoxWrapper((PasswordBox)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException("No conversion.");
}
}
在视图模型中:
public string Username { get; set; }
public ICommand LoginCommand
{
get
{
return new RelayCommand<IWrappedParameter<string>>(password => { Login(Username, password); });
}
}
private void Login(string username, string password)
{
// Perform login here...
}
因为视图模型使用IWrappedParameter<T>,所以它不需要任何关于PasswordBoxWrapper或PasswordBoxConverter的知识。这样您就可以将PasswordBox对象与视图模型隔离开来,并且不会破坏MVVM模式。
在视图中:
<Window.Resources>
<h:PasswordBoxConverter x:Key="PwdConverter" />
</Window.Resources>
...
<PasswordBox Name="PwdBox" />
<Button Content="Login" Command="{Binding LoginCommand}"
CommandParameter="{Binding ElementName=PwdBox, Converter={StaticResource PwdConverter}}" />
对不起,你做错了。
人们应该在眼皮内侧纹以下安全指南:
永远不要在记忆中保存纯文本密码。
WPF/Silverlight PasswordBox不为Password属性公开DP的原因与安全有关。
如果WPF/Silverlight要为密码保留一个DP,那么就需要框架在内存中保持密码本身不加密。这被认为是一个相当麻烦的安全攻击载体。
PasswordBox使用加密内存(某种程度上),访问密码的唯一方法是通过CLR属性。
我建议访问密码框时。密码CLR属性,您将避免将它放在任何变量或作为任何属性的值。
在客户端机器RAM中以明文形式保存密码是一个安全禁忌。
所以去掉公共字符串Password {get;设置;你已经站在那里了。
访问PasswordBox时。密码,把它拿出来,尽快发到服务器上。
不要保留密码的值,也不要像对待任何其他客户端机器文本一样对待它。不要在记忆中保存清晰的文本密码。
我知道这打破了MVVM模式,但您不应该绑定到PasswordBox。密码附加DP,存储您的密码在ViewModel或任何其他类似的诡计。
如果你正在寻找一个过度架构的解决方案,这里有一个:
1. 使用一个方法创建IHavePassword接口,该方法返回密码明文。
2. 让你的UserControl实现一个IHavePassword接口。
3.注册UserControl实例到你的IoC实现IHavePassword接口。
4. 当服务器请求您的密码发生时,调用IoC实现IHavePassword,只会得到梦寐以求的密码。
这只是我的看法。
——贾斯汀