在 WPF 中,PasswordBox
的Password
属性不是DependencyProperty
所以我不能直接绑定到它。作为一种解决方法,我正在使用 https://www.wpftutorial.net/PasswordBox.html 中的此PasswordHelper
,它将PasswordHelper.Password
附加到PasswordBox
,以便我可以绑定到它。
为了防止密码以纯文本形式保留在DataContext
中,我想使用一个转换器,该转换器在将密码保存到DataContext
之前生成密码的加盐哈希。由于我需要将盐和盐哈希都保存到DataContext
,因此我正在使用MultiBinding
和IMultiValueConverter
转换器StringToSaltedHashConverter
。
我的问题是,当我填写PasswordBox
时,我的DataContext
的Password
和PasswordSalt
属性没有得到更新。我已经与Snoop进行了检查,PasswordBox
的Password
和PasswordHelper.Password
属性都根据我键入的内容而变化。此外,我的StringToSaltedHashConverter
也被调用,我返回正确的值。
我在这个表格上还有一些其他绑定(用户名,名字,姓氏,性别......(,它们都工作正常。这是唯一未更新的
。¿为什么我的DataContext
没有更新?
XAML:
<PasswordBox x:Name="Password"
Style="{DynamicResource PasswordBoxStyle1}"
local:PasswordHelper.Attach="True">
<local:PasswordHelper.Password>
<MultiBinding Converter="{StaticResource StringToSaltedHashConverter}"
Mode="OneWayToSource">
<Binding Path="Password" />
<Binding Path="PasswordSalt" />
</MultiBinding>
</local:PasswordHelper.Password>
</PasswordBox>
代码隐藏:
public class StringToSaltedHashConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
string str = value as string;
string salt = Hash.CreateSalt();
string hash = Hash.CreateHash(str, salt);
object[] vs = { hash, salt };
return vs;
}
}
这不是要走的路。PasswordHelper
类和他们的喜欢应该被禁止在互联网上。PasswordBox.Password
属性不可绑定,这是有据可查的。访问此属性将创建一个纯文本string
表示形式,可以使用免费工具(例如Microsoft进程资源管理器(轻松检索。
获取 Password 属性值时,将密码公开为内存中的纯文本。若要避免此潜在的安全风险,请使用 SecurePassword 属性将密码作为安全字符串获取。
将此属性设置为 null 会导致基础密码设置为"空"。
您甚至将普通盐值存储在内存中 - 我真的希望这既不是商业应用程序也不是公共应用程序。您绝对无法控制垃圾回收器何时从内存中删除任何值。由于String
是不可变的类型,因此用户密码的多个副本很可能会在内存中保持公共状态。
在 Windows 环境中,建议的身份验证是使用 Windows 用户身份验证 API。
您至少应该清除PasswordBox.Password
属性,方法是将其设置为null
。
不应在视图中执行加密。
请编写负责任的代码!用户数据不是您的数据!
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<PasswordBox x:Name="PasswordBox" />
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.PasswordBox.PasswordChanged += OnPasswordChanged;
}
private void OnPasswordChanged(object sender, RoutedEventArgs e)
{
(this.DataContext as ViewModel).Password = this.PasswordBox.SecurePassword;
}
}
视图模型.cs
pucblic class ViewModel
{
private Model Model { get; }
private SecureString password;
public SecureString Password
{
private get => this.password;
public set
{
this.password = value;
OnPasswordChanged();
}
}
private void OnPasswordChanged()
{
// Hash password in the model e.g. to compare it to a hashed database value
this.Model.TryLogin(this.Password);
}
}
型号.cs
public class Model
{
public bool TryLogin(SecureString password)
{
IntPtr unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(password);
string hashedPassword = HashPassword(Marshal.PtrToStringUni(unmanagedString));
return PasswordIsValid(hashedPassword);
}
private string HashPassword(string unsecurePassword)
{
// Hash algorithm
}
}
在我需要PasswordBox
的密码之前,用户会告诉我他已经通过按下按钮完成了输入。
对我来说最好的选择是将Button
的CommandParameter
与LoginCommand
一起使用:
<StackPanel
Width="300"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Label Content="Username:" />
<TextBox Text="{Binding Username}" />
<Label Content="Password:" />
<PasswordBox PasswordChanged="PasswordChanged" />
<StackPanel
Orientation="Horizontal">
<Button
Name="LoginButton"
Command="{Binding LoginCommand}"
Content="Login" />
</StackPanel>
</StackPanel>
查看代码
private void PasswordChanged( object sender, RoutedEventArgs e )
{
LoginButton.CommandParameter = ( sender as PasswordBox ).SecurePassword;
}
视图模型命令
Login = ReactiveCommand.CreateFromTask( async ( SecureString password, CancellationToken cancellationToken ) =>
{
var loggedIn = await AuthenticationService.LoginAsync( Username, password, cancellationToken );
...
} );
我只想感谢大家的回答和令人叹息的评论。你让我意识到我不应该使用PasswordHelper
,我根本不应该尝试绑定密码。
以防万一有人遇到类似的问题MultiBinding
未正确更新DataContext
,这可以通过将OneWayToSource
模式添加到MultiBinding
中的每个绑定来修复。
<MultiBinding Converter="{StaticResource StringToSaltedHashConverter}"
Mode="OneWayToSource">
<Binding Path="Password"
Mode="OneWayToSource" />
<Binding Path="PasswordSalt"
Mode="OneWayToSource" />
</MultiBinding>