我遇到了绑定到密码盒的问题。这似乎是一个安全风险,但我正在使用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中的密码。


当前回答

也许我遗漏了一些东西,但似乎大多数解决方案都把事情复杂化了,并且忽略了安全实践。

此方法不违反MVVM模式,并保持完全的安全性。是的,从技术上讲,它是后面的代码,但它只不过是一个“特殊情况”绑定。ViewModel仍然没有视图实现的知识,在我看来,如果你试图将PasswordBox传递到ViewModel,它就会知道。

Code Behind !=自动违反MVVM。这完全取决于你怎么处理它。在这种情况下,我们只是手动编码绑定,所以它都被认为是UI实现的一部分,因此是ok的。

在ViewModel中,只是一个简单的属性。我把它设置为“只写”,因为无论出于什么原因,都不需要从ViewModel外部检索它,但它并不一定要这样。注意,它是一个SecureString,而不仅仅是一个字符串。

public SecureString SecurePassword { private get; set; }

在xaml中,您设置了PasswordChanged事件处理程序。

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

在后面的代码中:

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((dynamic)this.DataContext).SecurePassword = ((PasswordBox)sender).SecurePassword; }
}

使用这种方法,您的密码始终保持在SecureString中,因此提供了最大的安全性。如果你真的不关心安全性,或者你需要为需要它的下游方法提供明文密码(注意:大多数需要密码的。net方法也支持SecureString选项,所以你可能真的不需要明文密码,即使你认为你需要),你可以只使用password属性。是这样的:

(ViewModel财产)

public string Password { private get; set; }

(代码后面)

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((dynamic)this.DataContext).Password = ((PasswordBox)sender).Password; }
}

如果你想保持事物的强类型,你可以用ViewModel的接口代替(动态的)强制转换。但实际上,“正常的”数据绑定也不是强类型的,所以这不是什么大问题。

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
    if (this.DataContext != null)
    { ((IMyViewModel)this.DataContext).Password = ((PasswordBox)sender).Password; }
}

所以最好的是-你的密码是安全的,你的ViewModel就像任何其他属性一样有一个属性,你的View是自包含的,不需要外部引用。

其他回答

对不起,你做错了。

人们应该在眼皮内侧纹以下安全指南: 永远不要在记忆中保存纯文本密码。

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,只会得到梦寐以求的密码。

这只是我的看法。

——贾斯汀

这很简单。为password创建另一个属性,并将其与TextBox绑定

但所有输入操作都使用实际的密码属性执行

private string _Password;

    public string PasswordChar
    {
        get
        {
            string szChar = "";

            foreach(char szCahr in _Password)
            {
                szChar = szChar + "*";
            }

            return szChar;
        }

        set
        {
            _PasswordChar = value; NotifyPropertyChanged();
        }
    }

public string密码 { 得到 { 返回_Password; }

        set
        {
            _Password = value; NotifyPropertyChanged();
            PasswordChar = _Password;
        }
    }

不幸的是,我没有足够的代表来评论另一个答案:( 只是想继续“史蒂夫在指挥官”的回答。 更新他的“PasswordChanged”事件如下,如果你在一个视图上有多个PasswordBox,通过使用sender的Name属性,可以为所有的PasswordBox使用相同的PasswordChanged事件。可能有更好的方法来做到这一点,如果有任何理由不这样做,请告诉我:)

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
        if (sender is PasswordBox) { 
        if (this.DataContext != null)

        { ((dynamic)this.DataContext).GetType().GetProperty(((PasswordBox)sender).Name).SetValue((dynamic)this.DataContext, ((PasswordBox)sender).Password, null); }
    }
    }

为了在不破坏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}}" />

在windows通用应用程序

你可以将此代码与属性“Password”一起使用,并与modelView绑定

<密码框 x:uid=“密码框” 密码=“{绑定服务员.密码,模式=双向}” 名称=“txt密码” 水平对齐=“拉伸” 边距=“50,200,50,0” 垂直对齐=“顶部”/>