MVVM'de bir PasswordBox'a bağlanma


251

P'ye bağlanma ile ilgili bir sorunla karşılaştım asswordBox. Bu bir güvenlik riski gibi görünüyor ama ben MVVM kalıbı kullanıyorum, bu yüzden bunu atlamak istiyorum. Burada bazı ilginç kod buldum (kimse bu ya da benzer bir şey kullandı mı?)

http://www.wpftutorial.net/PasswordBox.html

Teknik olarak harika görünüyor, ancak şifreyi nasıl alacağımdan emin değilim.

Temelde benim LoginViewModeliçin özellikleri var Usernameve Password. Usernamegayet iyi ve olduğu gibi çalışıyor TextBox.

Yukarıdaki kodu belirtildiği gibi kullandım ve bunu girdim

<PasswordBox ff:PasswordHelper.Attach="True"
    ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>

Ben vardı PasswordBoxbir TextBoxve Binding Path=Passwordsonra benim özelliği LoginViewModelgüncellendi.

Kodum temelde ben bir var, çok basit Commandsıramı Button. Ben bastığımda CanLoginçağrılır ve eğer doğru dönerse çağırır Login. Burada
benim için Usernameharika çalışıyor benim özelliği kontrol görebilirsiniz .

Gelen Loginbenim hizmet a kadar birlikte göndermek Usernameve Password, Usernamealınan verileri içeren benim Viewama PasswordolduğuNull|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 { }
}

Ben bunu yapıyorum

<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
         MinWidth="180" />

<PasswordBox ff:PasswordHelper.Attach="True" 
             ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>

Ben benim var TextBox, bu sorun yok, ama benim de boştur.ViewModelPassword

Yanlış bir şey mi yapıyorum veya bir adımı kaçırıyor muyum?

Bir kesme noktası koymak ve emin yeterli kod statik yardımcı sınıf girmek ama asla güncellemeler benim Passwordbölümümde ViewModel.


3
Peki kod işe yaramadı çıkıyor ama burada alternatif bir kod denedim ve mükemmel çalışıyor. blog.functionalfun.net/2008/06/…
mark smith

5
Tüm şifre kutusu kontrolünden geçmek, görünümü viewmodel'den ayırmaya karşı gelmiyor mu?

Yanıtlar:


165

Üzgünüm, ama yanlış yapıyorsun.

İnsanlar göz kapaklarının iç kısımlarında aşağıdaki güvenlik yönergelerine sahip olmalıdır:
Asla düz metin şifrelerini hafızada tutmayın.

WPF / Silverlight'ın PasswordBoxbir DP'yi Passwordmülk için göstermemesinin nedeni güvenlikle ilgilidir.
WPF / Silverlight bir DP tutuyor Passwordolsaydı, parolanın kendisini şifrelenmemiş olarak saklaması için bir çerçeve gerektirirdi. Bu oldukça zahmetli bir güvenlik saldırısı vektörü olarak kabul edilir. PasswordBoxTürlü () kullandığı şifreli bellek ve şifreyi erişmenin tek yolu CLR özelliği geçer.

PasswordBox.PasswordCLR özelliğine erişirken, onu herhangi bir değişkene veya herhangi bir özellik için bir değer olarak yerleştirmekten kaçınmanızı öneririm .
İstemci makine RAM'inde şifrenizi düz metin olarak tutmak bir güvenlik no-no'dur.
Yani public string Password { get; set; }oradan kalktığınızdan kurtulun .

Erişirken PasswordBox.Password, sadece çıkarın ve ASAP sunucusuna gönderin. Parolanın değerini saklamayın ve diğer herhangi bir istemci makine metninde yaptığınız gibi kullanmayın. Net metin şifrelerini bellekte tutmayın.

Bunun MVVM modelini bozduğunu biliyorum, ancak PasswordBox.PasswordEkli DP'ye bağlanmamalı , parolanızı ViewModel'de veya başka benzer maskaralıklarda saklamamalısınız.

Aşırı mimarili bir çözüm arıyorsanız, işte bir tane:
1. IHavePasswordParola açık metnini döndüren bir yöntemle arayüzü oluşturun .
2. UserControlUygulamanıza bir IHavePasswordarayüz sağlayın .
3. UserControlÖrneği, IHavePasswordarayüzü uygularken IoC'nize kaydedin .
4. Parolanızı gerektiren bir sunucu isteği gerçekleştiğinde, IHavePassworduygulama için IoC'nizi arayın ve yalnızca çok beğenilen parolayı alın.

Sadece benim almam.

- Justin


19
WPF için VM'deki SecureString'i bu sorunu çözmek için kullanamaz mısınız? Silverlight için bir şey yok gibi görünmüyor.
Bryant

35
Niyetinize ve ilettiğiniz mesaja katılıyorum, ancak bu yaklaşımı uygularsanız cevabınız parola dizesinin asla bellekte olmadığını ima eder. Parolanın değeri, kullanıcı yazdığı andan itibaren bellekte olacaktır. Parolanızı tutan mülkün ortadan kaldırılması iyi bir fikirdir ve çöp toplayıcının hasat etmesi için bırakılan veya belki de programınızın bir parçası olarak çalışan diğer yönetilen ve yönetilmeyen kod tarafından bulunabilecek parolanızın kopyalarını sınırlar, ancak tamamen saklamayın.
IanNorton

182
Çoğu durumda, bu güvenlik düzeyine ihtiyacınız yoktur . Şifreleri çalmanın başka birçok yolu olduğunda bunu bir şeyi zorlaştırmanın anlamı nedir? @Bryant'ın belirttiği gibi en az WPF SecureString kullanımına izin vermeliydi.
chakrit

336
Kötü adamların makinenizin RAM'ine erişimi varsa, şifrenizi çalmaktan daha büyük sorunlarınız vardır.
Cameron MacFarland

13
Yıllardır, tıpkı PasswordBox gibi davranan ancak sadece metin değerini SecureString olarak döndüren özel bir kullanıcı kontrolü kullanıyorum. Evet, bu Snoop'un parolayı düz metin olarak görüntülemesini önler. Ancak, SecureString'in düz metin değeri yine de kolayca çıkarılabilir ve yalnızca acemi saldırıları algılar. Sisteminiz Snoop gibi anahtar kaydedicileri ve koklayıcıları gizlice kullanma riski altındaysa, sistem güvenliğinizde yeniden değerlendirmeniz gerekir.
Mike Christian

199

2 sentim:

WPF ve MVVM kullanarak bir kez tipik bir giriş iletişim kutusu (kullanıcı ve şifre kutuları artı "Tamam" düğmesi) geliştirdim. Ben sadece "Tamam" düğmesine ekli komut için bir parametre olarak PasswordBox denetimini geçirerek şifre bağlama sorunu çözüldü. Yani görüşüme göre:

<PasswordBox Name="txtPassword" VerticalAlignment="Top" Width="120" />
<Button Content="Ok" Command="{Binding Path=OkCommand}"
   CommandParameter="{Binding ElementName=txtPassword}"/>

ViewModel'de, Executeeklenen komutun yöntemi şöyleydi:

void Execute(object parameter)
{
    var passwordBox = parameter as PasswordBox;
    var password = passwordBox.Password;
    //Now go ahead and check the user name and password
}

ViewModel, View'un nasıl uygulandığı hakkında bir şeyler bildiğinden, MVVM modelini biraz ihlal ediyor, ancak bu belirli projede bunu karşılayabiliyordum. Umarım birisi için de faydalıdır.


Merhaba Konamiman, Execute yöntemi çağrıldığında.modelimde bir sınıf kullanıcısı (login, pass) ve authenticate komutum var.Bu bağlamda Execute'u nasıl kullanabilirim?

3
çok yararlı, teşekkürler. fyi, birisi _loginCommand = new RelayCommand (param => Login (KullanıcıAdı, (PasswordBox) param), param => CanLogIn) gibi bir şeyi görmek için kullanılabilir;
Chuck Rostance

5
Bu iyi bir çözüm ama şifre + şifre onay combo gibi bir şey için başarısız
Julien

Merhaba Konamiman, çözümünüzü kullanıyorum ancak Windows 8.1 Store uygulamasında çalışmıyor. Bu soruyu sordum: stackoverflow.com/questions/26221594/…
VansFannel

2
Bunun için teşekkürler! Bu UI iş parçacığı ana program iş parçacığı veri taşıma ile büyük bir sorunu çözdü. SecureString yaklaşımını uyguladığınızdan emin olun ve ~ mümkün olan en kısa zamanda şifreden kurtulun ~. Boşalt. Atın. Temizle. Yapman gerekeni yap. Ayrıca, IDisposable'ı uyguladığınızdan emin olun.
Steven C. Britton

184

Belki bir şeyleri kaçırıyorum, ancak bu çözümlerin çoğu işleri karmaşıklaştırıyor ve güvenli uygulamalarla ortadan kalkıyor gibi görünüyor.

Bu yöntem MVVM modelini ihlal etmez ve tam güvenliği sağlar. Evet, teknik olarak kodun arkasındadır, ancak "özel durum" bağlayıcısından başka bir şey değildir. ViewModel hala ViewModel için PasswordBox geçirmeye çalışıyorsanız bence yapar View uygulaması hakkında hiçbir bilgiye sahip değildir.

Code Behind! = Otomatik MVVM ihlali. Her şey onunla ne yaptığınıza bağlı. Bu durumda, sadece bir bağlayıcıyı manuel olarak kodluyoruz, bu yüzden hepsi UI uygulamasının bir parçası olarak kabul edildi ve bu nedenle tamam.

ViewModel'de sadece basit bir özellik. Herhangi bir nedenle ViewModel dışından almak için bir ihtiyaç olmamalı çünkü "sadece yazma" yaptım, ama olmak zorunda değil. Bunun bir String değil, bir SecureString olduğunu unutmayın.

public SecureString SecurePassword { private get; set; }

Xaml'de bir PasswordChanged olay işleyicisi ayarladınız.

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/>

Arkasındaki kodda:

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

Bu yöntemle, şifreniz her zaman bir SecureString içinde kalır ve bu nedenle maksimum güvenlik sağlar. Güvenliği gerçekten önemsemiyorsanız veya bunu gerektiren bir aşağı akış yöntemi için açık metin parolasına ihtiyacınız varsa (not: parola gerektiren çoğu .NET yöntemi de SecureString seçeneğini destekler, bu nedenle gerçekten açık bir metin parolasına ihtiyacınız olmayabilir. Düşünseniz bile), bunun yerine Parola özelliğini kullanabilirsiniz. Bunun gibi:

(ViewModel özelliği)

public string Password { private get; set; }

(Arkasındaki kod)

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

İşleri güçlü bir şekilde yazmak istiyorsanız, (dinamik) kadroyu ViewModel arayüzünüzle değiştirebilirsiniz. Ama gerçekten, "normal" veri bağlamaları da güçlü bir şekilde yazılmamıştır, bu yüzden büyük bir anlaşma değildir.

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

Tüm dünyaların en iyisi - şifreniz güvenlidir, ViewModel'inizde başka herhangi bir mülk gibi bir özellik vardır ve Görünümünüz harici referans gerektirmeden kendi kendine bulunur.


1
Bu bana iyi geliyor! Güvenlik tarafında süper sıkı olmak istedim, bu kesecek emin değilim, ama bana onun mükemmel bir orta zemin. Teşekkürler!
jrich523

3
MVVM ve paranoya hakkında katı dogmaya karşı pratiklik için teşekkürler. Harika çalışıyor, teşekkürler.
Bruce Pierson

2
SecureString örneği, bu uzantı blogs.msdn.com/b/fpintos/archive/2009/06/12/…
Ayman

1
Gerçekten güzel. Keşke MS bu kontrole SecureString türünde bir Şifre DP eklemiş olsaydı.
Keith Hill

1
Güvenlik ve MVVM'yi koruduğu için bu mükemmel bir cevaptır.
LoRdPMN

20

Bu XAML'yi kullanabilirsiniz:

<PasswordBox Name="PasswordBox">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="PasswordChanged">
            <i:InvokeCommandAction Command="{Binding PasswordChangedCommand}" CommandParameter="{Binding ElementName=PasswordBox}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</PasswordBox>

Ve bu komut yürütme yöntemi:

private void ExecutePasswordChangedCommand(PasswordBox obj)
{ 
   if (obj != null)
     Password = obj.Password;
}

3
FYIxmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
XAMlMAX

PasswordBox'a Ad Verme gerekmeksizin: CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}"(not: değil RelativeSource Self ).
wondra

Bu çözüm MVVM modelini ihlal ediyor.
BionicCode

13

Bu benim için iyi çalışıyor.

<Button Command="{Binding Connect}" 
        CommandParameter="{Binding ElementName=MyPasswordBox}"/>

3
CommandParameter = "{Binding ElementName = MyPasswordBox, Path = SecurePassword"} ne olacak?
LukeN

2
LukeN, bu işe yaramıyor (en azından benim için). Muhtemelen aynı nedenden dolayı - SecurePassword bağımlılık özelliği değildir.
vkrzv

ICommandGörünüm modelinde uygulandığı varsayılarak , bu çözüm MVVM modelini ihlal edecektir.
BionicCode

9

MVVM modelini ihlal etmeden basit bir çözüm, ViewModel'de şifreyi toplayan bir olay (veya temsilci) eklemektir.

In ViewModel :

public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;

bu EventArgs ile:

class HarvestPasswordEventArgs : EventArgs
{
    public string Password;
}

içinde Görünüm , şifre değerindeki ViewModel ve dolguyu oluşturma konusunda olaya abone.

_viewModel.HarvestPassword += (sender, args) => 
    args.Password = passwordBox1.Password;

In ViewModel şifre gerektiğinde, sen olayı ve oradan şifreyi hasat edebilirsiniz:

if (HarvestPassword == null)
  //bah 
  return;

var pwargs = new HarvestPasswordEventArgs();
HarvestPassword(this, pwargs);

LoginHelpers.Login(Username, pwargs.Password);

Eksik olan bir şey, bir görünümü bir görünüm modeli olayına abone olurken, WeakEventManager<TEventSource, TEventArgs>bellek sızıntılarını önlemek için a kullanmanız gerektiğidir . Çoğu zaman görünüm, görünüm modeliyle aynı ömre sahip olmaz. WeakEventManager<IViewModel, EventArgs>.AddHandler(iViewModelInstance, nameof(IViewModel.Event), eventHandlerMethod);
Todd A. Stedel

Bu çözümü tercih ediyorum, çünkü basit, MVVM'yi ihlal etmiyor, arkasında minimum kod var, şifre kutusunun doğru kullanımına izin veriyor (bunun yerine ´SecurePassword´ kullanıyorsanız). Ayrıca şimdi diğer HarvestPassword yöntemlerini uygulamak artık çok basit (SmartCard gibi ....)
Matt

8

Çeşitli çözümlere bakmak için çok zaman harcadım. Dekoratörler fikrini beğenmedim, davranışlar doğrulama arayüzünü bozuyor, arkasındaki kod ... gerçekten mi?

En iyisi, özel bir ekli mülke bağlı kalmak ve SecureStringgörünüm modelinizde mülkünüze bağlamaktır . Mümkün olduğunca orada saklayın. Düz parolaya hızlı erişmeniz gerektiğinde, aşağıdaki kodu kullanarak geçici olarak güvenli olmayan bir dizeye dönüştürün:

namespace Namespace.Extensions
{
    using System;
    using System.Runtime.InteropServices;
    using System.Security;

    /// <summary>
    /// Provides unsafe temporary operations on secured strings.
    /// </summary>
    [SuppressUnmanagedCodeSecurity]
    public static class SecureStringExtensions
    {
        /// <summary>
        /// Converts a secured string to an unsecured string.
        /// </summary>
        public static string ToUnsecuredString(this SecureString secureString)
        {
            // copy&paste from the internal System.Net.UnsafeNclNativeMethods
            IntPtr bstrPtr = IntPtr.Zero;
            if (secureString != null)
            {
                if (secureString.Length != 0)
                {
                    try
                    {
                        bstrPtr = Marshal.SecureStringToBSTR(secureString);
                        return Marshal.PtrToStringBSTR(bstrPtr);
                    }
                    finally
                    {
                        if (bstrPtr != IntPtr.Zero)
                            Marshal.ZeroFreeBSTR(bstrPtr);
                    }
                }
            }
            return string.Empty;
        }

        /// <summary>
        /// Copies the existing instance of a secure string into the destination, clearing the destination beforehand.
        /// </summary>
        public static void CopyInto(this SecureString source, SecureString destination)
        {
            destination.Clear();
            foreach (var chr in source.ToUnsecuredString())
            {
                destination.AppendChar(chr);
            }
        }

        /// <summary>
        /// Converts an unsecured string to a secured string.
        /// </summary>
        public static SecureString ToSecuredString(this string plainString)
        {
            if (string.IsNullOrEmpty(plainString))
            {
                return new SecureString();
            }

            SecureString secure = new SecureString();
            foreach (char c in plainString)
            {
                secure.AppendChar(c);
            }
            return secure;
        }
    }
}

GC'nin UI öğenizi toplamasına izin verdiğinizden emin olun, bu nedenle PasswordChangedüzerindeki etkinlik için statik bir olay işleyicisi kullanma isteğine karşı koyun PasswordBox. Ayrıca, denetimin SecurePasswordayar için özelliği kullanırken UI'yi güncellemediği bir anormallik buldum, Passwordbunun yerine parolayı neden kopyaladığım .

namespace Namespace.Controls
{
    using System.Security;
    using System.Windows;
    using System.Windows.Controls;
    using Namespace.Extensions;

    /// <summary>
    /// Creates a bindable attached property for the <see cref="PasswordBox.SecurePassword"/> property.
    /// </summary>
    public static class PasswordBoxHelper
    {
        // an attached behavior won't work due to view model validation not picking up the right control to adorn
        public static readonly DependencyProperty SecurePasswordBindingProperty = DependencyProperty.RegisterAttached(
            "SecurePassword",
            typeof(SecureString),
            typeof(PasswordBoxHelper),
            new FrameworkPropertyMetadata(new SecureString(),FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AttachedPropertyValueChanged)
        );

        private static readonly DependencyProperty _passwordBindingMarshallerProperty = DependencyProperty.RegisterAttached(
            "PasswordBindingMarshaller",
            typeof(PasswordBindingMarshaller),
            typeof(PasswordBoxHelper),
            new PropertyMetadata()
        );

        public static void SetSecurePassword(PasswordBox element, SecureString secureString)
        {
            element.SetValue(SecurePasswordBindingProperty, secureString);
        }

        public static SecureString GetSecurePassword(PasswordBox element)
        {
            return element.GetValue(SecurePasswordBindingProperty) as SecureString;
        }

        private static void AttachedPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // we'll need to hook up to one of the element's events
            // in order to allow the GC to collect the control, we'll wrap the event handler inside an object living in an attached property
            // don't be tempted to use the Unloaded event as that will be fired  even when the control is still alive and well (e.g. switching tabs in a tab control) 
            var passwordBox = (PasswordBox)d;
            var bindingMarshaller = passwordBox.GetValue(_passwordBindingMarshallerProperty) as PasswordBindingMarshaller;
            if (bindingMarshaller == null)
            {
                bindingMarshaller = new PasswordBindingMarshaller(passwordBox);
                passwordBox.SetValue(_passwordBindingMarshallerProperty, bindingMarshaller);
            }

            bindingMarshaller.UpdatePasswordBox(e.NewValue as SecureString);
        }

        /// <summary>
        /// Encapsulated event logic
        /// </summary>
        private class PasswordBindingMarshaller
        {
            private readonly PasswordBox _passwordBox;
            private bool _isMarshalling;

            public PasswordBindingMarshaller(PasswordBox passwordBox)
            {
                _passwordBox = passwordBox;
                _passwordBox.PasswordChanged += this.PasswordBoxPasswordChanged;
            }

            public void UpdatePasswordBox(SecureString newPassword)
            {
                if (_isMarshalling)
                {
                    return;
                }

                _isMarshalling = true;
                try
                {
                    // setting up the SecuredPassword won't trigger a visual update so we'll have to use the Password property
                    _passwordBox.Password = newPassword.ToUnsecuredString();

                    // you may try the statement below, however the benefits are minimal security wise (you still have to extract the unsecured password for copying)
                    //newPassword.CopyInto(_passwordBox.SecurePassword);
                }
                finally
                {
                    _isMarshalling = false;
                }
            }

            private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e)
            {
                // copy the password into the attached property
                if (_isMarshalling)
                {
                    return;
                }

                _isMarshalling = true;
                try
                {
                    SetSecurePassword(_passwordBox, _passwordBox.SecurePassword.Copy());
                }
                finally
                {
                    _isMarshalling = false;
                }
            }
        }
    }
}

Ve XAML kullanımı:

<PasswordBox controls:PasswordBoxHelper.SecurePassword="{Binding LogonPassword, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">

Görünüm modelindeki mülküm şöyle görünüyordu:

[RequiredSecureString]
public SecureString LogonPassword
{
   get
   {
       return _logonPassword;
   }
   set
   {
       _logonPassword = value;
       NotifyPropertyChanged(nameof(LogonPassword));
   }
}

RequiredSecureStringAşağıdaki mantığı var sadece basit özel doğrulayıcı geçerli:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]    
public class RequiredSecureStringAttribute:ValidationAttribute
{
    public RequiredSecureStringAttribute()
        :base("Field is required")
    {            
    }

    public override bool IsValid(object value)
    {
        return (value as SecureString)?.Length > 0;
    }
}

Bu artık senin. Eksiksiz ve test edilmiş saf bir MVVM çözümü.


7

Burada bir şifre kutusu olan bir GIST yayınladım .

using System.Windows;
using System.Windows.Controls;

namespace CustomControl
{
    public class BindablePasswordBox : Decorator
    {
        /// <summary>
        /// The password dependency property.
        /// </summary>
        public static readonly DependencyProperty PasswordProperty;

        private bool isPreventCallback;
        private RoutedEventHandler savedCallback;

        /// <summary>
        /// Static constructor to initialize the dependency properties.
        /// </summary>
        static BindablePasswordBox()
        {
            PasswordProperty = DependencyProperty.Register(
                "Password",
                typeof(string),
                typeof(BindablePasswordBox),
                new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnPasswordPropertyChanged))
            );
        }

        /// <summary>
        /// Saves the password changed callback and sets the child element to the password box.
        /// </summary>
        public BindablePasswordBox()
        {
            savedCallback = HandlePasswordChanged;

            PasswordBox passwordBox = new PasswordBox();
            passwordBox.PasswordChanged += savedCallback;
            Child = passwordBox;
        }

        /// <summary>
        /// The password dependency property.
        /// </summary>
        public string Password
        {
            get { return GetValue(PasswordProperty) as string; }
            set { SetValue(PasswordProperty, value); }
        }

        /// <summary>
        /// Handles changes to the password dependency property.
        /// </summary>
        /// <param name="d">the dependency object</param>
        /// <param name="eventArgs">the event args</param>
        private static void OnPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs eventArgs)
        {
            BindablePasswordBox bindablePasswordBox = (BindablePasswordBox) d;
            PasswordBox passwordBox = (PasswordBox) bindablePasswordBox.Child;

            if (bindablePasswordBox.isPreventCallback)
            {
                return;
            }

            passwordBox.PasswordChanged -= bindablePasswordBox.savedCallback;
            passwordBox.Password = (eventArgs.NewValue != null) ? eventArgs.NewValue.ToString() : "";
            passwordBox.PasswordChanged += bindablePasswordBox.savedCallback;
        }

        /// <summary>
        /// Handles the password changed event.
        /// </summary>
        /// <param name="sender">the sender</param>
        /// <param name="eventArgs">the event args</param>
        private void HandlePasswordChanged(object sender, RoutedEventArgs eventArgs)
        {
            PasswordBox passwordBox = (PasswordBox) sender;

            isPreventCallback = true;
            Password = passwordBox.Password;
            isPreventCallback = false;
        }
    }
}

1
bu kötü olmasa da, dolgu ve tabindex gibi basit özellikleri ayarlama yeteneğini kaybedersiniz
Julien

1
Taylor, cevapta mevcut olması için özü işaretledim. (Aksi takdirde sadece bağlantıya bir cevap gibi görünüyordu .. sadece bunun gibi silinmesini önlemek için çalışıyor.) Satır içi içeriği ile karışıklık çekinmeyin.
Lynn Crumbling

@Julien ama bunu stillerle düzeltebilirsiniz. Ben benzer bir şekilde bu sorunu çözmek ama ben ContentControlsadece sonra bir PasswordBox kullanabilirsiniz içerik ve stil olarak XAML uygun olarak kullanabilirsiniz. Bunun amacı ContentControl, yalnızca PasswordChangedetkinliğe abone olmak ve iki yönlü bir ikili özelliği göstermektir. Sonuçta, bu 65 kod satırı ve bu dekorasyon sınıfının yaptığı hemen hemen. Aşağıdaki gist için özüm
John Leidegren

6

Bu uygulama biraz farklı. ViewModel'de bir özelliğin bağlanması yoluyla Görünüm'e bir parola kutusu iletirsiniz, herhangi bir komut parametresi kullanmaz. ViewModel, Görünümden Haberdar Olmaz. SkyDrive'dan indirilebilen bir VB vs 2010 Projem var. Wpf MvvM PassWordBox Örnek.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511

Bir Wpf MvvM Uygulaması PasswordBox kullanıyorum yolu oldukça basit ve benim için iyi çalışıyor. Bu, doğru veya en iyi yol olduğunu düşündüğüm anlamına gelmez. Sadece PasswordBox ve MvvM Kalıbı Kullanmanın bir uygulamasıdır.

Temel olarak, görünümün bir PasswordBox olarak bağlanabileceği bir genel salt okunur özellik oluşturursunuz (Gerçek kontrol) Örnek:

Private _thePassWordBox As PasswordBox
Public ReadOnly Property ThePassWordBox As PasswordBox
    Get
        If IsNothing(_thePassWordBox) Then _thePassWordBox = New PasswordBox
        Return _thePassWordBox
    End Get
End Property

Ben sadece kendi kendini Başlatma yapmak için bir destek alanı kullanın.

Daha sonra Xaml'den bir ContentControl veya Kontrol Kapsayıcısının İçeriğini bağlarsınız Örnek:

 <ContentControl Grid.Column="1" Grid.Row="1" Height="23" Width="120" Content="{Binding Path=ThePassWordBox}" HorizontalAlignment="Center" VerticalAlignment="Center" />

Oradan da şifre kutusu üzerinde tam kontrol var Ben de bir PasswordAccessor (Sadece bir Dize Fonksiyonu) giriş yaparken veya başka ne için Şifre yaparken Şifre Değeri döndürmek için kullanın. Örnekte Genel Kullanıcı Nesnesi Modelinde bir public özelliği var. Misal:

Public Property PasswordAccessor() As Func(Of String)

Kullanıcı Nesnesi'nde password string özelliği herhangi bir destek deposu olmadan salt okunurdur ve sadece PasswordBox'tan Parolayı döndürür. Misal:

Public ReadOnly Property PassWord As String
    Get
        Return If((PasswordAccessor Is Nothing), String.Empty, PasswordAccessor.Invoke())
    End Get
End Property

Sonra ViewModel Accessor oluşturulan ve PasswordBox.Password özelliğine ayarlanır emin olun 'Örnek:

Public Sub New()
    'Sets the Accessor for the Password Property
    SetPasswordAccessor(Function() ThePassWordBox.Password)
End Sub

Friend Sub SetPasswordAccessor(ByVal accessor As Func(Of String))
    If Not IsNothing(VMUser) Then VMUser.PasswordAccessor = accessor
End Sub

Parola dizesine giriş için ihtiyacım olduğunda, gerçekten parolayı alıp geri döndürmek için işlevi çağıran Kullanıcı Nesneleri Parola özelliğini alıyorum, sonra gerçek parola Kullanıcı Nesnesi tarafından saklanmaz. Örnek: ViewModel'de olurdu

Private Function LogIn() as Boolean
    'Make call to your Authentication methods and or functions. I usally place that code in the Model
    Return AuthenticationManager.Login(New UserIdentity(User.UserName, User.Password)
End Function

Bunu yapmalı. ViewModel, Görünüm Kontrolleri hakkında herhangi bir bilgiye ihtiyaç duymaz. View Just, bir Görüntüye veya Diğer Kaynağa Bağlanan Görünümden farklı olmayan ViewModel'deki özelliğe bağlanır. Bu durumda, kaynak (Özellik) sadece bir kullanıcı kontrolü olur. ViewModel, Mülkü oluşturup sahibi olarak ve Mülk, Görünümden bağımsız olduğu için test yapılmasına olanak tanır. Güvenlik gelince, bu uygulamanın ne kadar iyi olduğunu bilmiyorum. Ancak bir İşlev kullanılarak Değer, Özellik tarafından yeni erişilen Mülkün kendisinde depolanmaz.


6

MVVM kırmadan OP sorunu çözmek için, özel değer dönüştürücü ve parola kutusundan alınması gereken değer (parola) için bir sarıcı kullanırdım.

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.");
    }
}

Görünüm modelinde:

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

Görünüm modeli kullandığından IWrappedParameter<T>, hakkında herhangi bir bilgi sahibi olmak gerekmez.PasswordBoxWrapper ne ne dePasswordBoxConverter . Bu şekilde PasswordBoxnesneyi görünüm modelinden ayırabilir ve MVVM desenini bozamazsınız.

Görünümde:

<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}}" />

çok zarif bir çözüm imo. Benimkini buna dayandım. tek fark: i String Password yerine login işlevine SecureString SecurePassword iletir. böylece şifresiz bellek etrafında uçan şifrelenmemiş dizeleri yoktur.
bana havuç

Bir süredir oldu, ancak RelayCommand'ım nedeniyle bunu çalıştıramadım. seninkini ekler misin?
Ecnerwal

5

Şifreyi herhangi bir yerde saklamaktan kaçınmanın önemli olduğunu kabul etsem de, görünüm modelini bir görünüm olmadan somutlaştırma ve testlerimi ona karşı yürütme yeteneğine ihtiyacım var.

Benim için çalışan çözüm, PasswordBox.Password işlevini görünüm modeline kaydetmek ve giriş modelini çalıştırırken görünüm modelinin çağırmasını sağlamaktı.

Bu does görünümün codebehind içinde kod satırını anlamına gelir.

Yani, Login.xaml'imde

<PasswordBox x:Name="PasswordBox"/>

ve Login.xaml.cs içinde var

LoginViewModel.PasswordHandler = () => PasswordBox.Password;

sonra LoginViewModel.cs içinde PasswordHandler tanımlanmış var

public Func<string> PasswordHandler { get; set; }

ve giriş yapılması gerektiğinde, kod görünümünden parolayı almak için işleyiciyi çağırır ...

bool loginResult = Login(Username, PasswordHandler());

Bu şekilde, viewmodel'i test etmek istediğimde, PasswordHandler'ı testte kullanmak istediğim şifreyi sunmama izin veren anonim bir yönteme ayarlayabilirim.


4

Çözümümü karışıma atacağımı düşündüm, çünkü bu çok yaygın bir konudur ... ve birçok seçeneğe sahip olmak her zaman iyi bir şeydir.

Ben sadece bir PasswordBoxa sarılmış UserControlve DependencyPropertybağlamak için bir uyguladı . Bellekte herhangi bir net metin saklamaktan kaçınmak için elimden gelen her şeyi yapıyorum, bu yüzden her şey bir SecureStringve PasswordBox.Passwordözellik aracılığıyla yapılır . foreachDöngü sırasında , her karakter ortaya çıkar, ancak çok kısa. Dürüst olmak gerekirse, WPF uygulamanızın bu kısa pozlamadan ödün verilmesinden endişe ediyorsanız, ele alınması gereken daha büyük güvenlik sorunlarınız var.

Bunun güzelliği, herhangi bir MVVM kuralını, hatta "saf" kuralları bile ihlal etmemenizdir, çünkü bu bir UserControlkoddur, bu yüzden kod arkasına sahip olmasına izin verilir. Kullanırken , parolanın herhangi bir kısmının veya kaynağının farkında olmadan Viewve ViewModelolmadan saf iletişim kurabilirsiniz . Sadece bağlandığınızdan emin olun .VideModelViewSecureStringViewModel .

BindablePasswordBox.xaml

<UserControl x:Class="BK.WPF.CustomControls.BindanblePasswordBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" d:DesignHeight="22" d:DesignWidth="150">
    <PasswordBox x:Name="PswdBox"/>
</UserControl>

BindablePasswordBox.xaml.cs (Sürüm 1 - İki yönlü ciltleme desteği yok.)

using System.ComponentModel;
using System.Security;
using System.Windows;
using System.Windows.Controls;

namespace BK.WPF.CustomControls
{
    public partial class BindanblePasswordBox : UserControl
    {
        public static readonly DependencyProperty PasswordProperty =
            DependencyProperty.Register("Password", typeof(SecureString), typeof(BindanblePasswordBox));

        public SecureString Password
        {
            get { return (SecureString)GetValue(PasswordProperty); }
            set { SetValue(PasswordProperty, value); }
        }

        public BindanblePasswordBox()
        {
            InitializeComponent();
            PswdBox.PasswordChanged += PswdBox_PasswordChanged;
        }

        private void PswdBox_PasswordChanged(object sender, RoutedEventArgs e)
        {
            var secure = new SecureString();
            foreach (var c in PswdBox.Password)
            {
                secure.AppendChar(c);
            }
            Password = secure;
        }
    }
}

Sürüm 1'in Kullanımı:

<local:BindanblePasswordBox Width="150" HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Password="{Binding Password, Mode=OneWayToSource}"/>

BindablePasswordBox.xaml.cs (Sürüm 2 - İki yönlü ciltleme desteği vardır.)

public partial class BindablePasswordBox : UserControl
{
    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(SecureString), typeof(BindablePasswordBox),
        new PropertyMetadata(PasswordChanged));

    public SecureString Password
    {
        get { return (SecureString)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    public BindablePasswordBox()
    {
        InitializeComponent();
        PswdBox.PasswordChanged += PswdBox_PasswordChanged;
    }

    private void PswdBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
        var secure = new SecureString();
        foreach (var c in PswdBox.Password)
        {
            secure.AppendChar(c);
        }
        if (Password != secure)
        {
            Password = secure;
        }
    }

    private static void PasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var pswdBox = d as BindablePasswordBox;
        if (pswdBox != null && e.NewValue != e.OldValue)
        {
            var newValue = e.NewValue as SecureString;
            if (newValue == null)
            {
                return;
            }

            var unmanagedString = IntPtr.Zero;
            string newString;
            try
            {
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(newValue);
                newString = Marshal.PtrToStringUni(unmanagedString);
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
            }

            var currentValue = pswdBox.PswdBox.Password;
            if (currentValue != newString)
            {
                pswdBox.PswdBox.Password = newString;
            }
        }
    }
}

Sürüm 2 Kullanımı:

<local:BindanblePasswordBox Width="150" HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Password="{Binding Password, Mode=TwoWay}"/>

Bunu uygulamayı denedim, ancak kullanıcı arayüzünde parolayı güncellediğinizde sonsuz bir döngü elde edersiniz; çünkü if (Password != secure)SecureString eşittir geçersiz kılmadığı için her zaman yanlış olacaktır. Düşüncesi olan var mı?
simonalexander2005


2

Bu yöntemi kullandım ve şifre kutusunu geçtim, ancak bu MVVM'yi ihlal etse de, benim için önemliydi çünkü karmaşık bir kabuk çevresi olan kabuğumdaki girişim için veri şablonuyla bir içerik kontrolü kullanıyordum. Yani kabuğun arkasındaki koda erişmek bok olurdu.

Parola kutusundan geçmek, bildiğim kadarıyla arkasındaki koddan kontrole erişmekle aynı olduğunu düşünürdüm. Şifreleri kabul ediyorum, hafızasında kalmıyorum vb. Bu uygulamada görünüm modelinde şifre için özelliğim yok.

Düğme Komutu

Command="{Binding Path=DataContext.LoginCommand, ElementName=MyShell}" CommandParameter="{Binding ElementName=PasswordBox}"

ViewModel

private void Login(object parameter)
{
    System.Windows.Controls.PasswordBox p = (System.Windows.Controls.PasswordBox)parameter;
    MessageBox.Show(p.Password);
}

Bu MVVM modelinin açık bir ihlalidir. Kalıp, görünüm modelindeki denetimlerin işlenmesine izin vermez.
BionicCode

2

Bana göre, her ikisi de yanlış geliyor:

  • Açık metin parola özelliklerini uygulama
  • Gönderme PasswordBoxViewModel için bir komut parametresi olarak

Steve tarafından CO'da açıklandığı gibi SecurePassword'ün (SecureString örneği) aktarılması kabul edilebilir görünmektedir. tercih ederimBehaviorsArkasında kod yazmayı ve ayrıca şifreyi viewmodel'den sıfırlayabilme ek gereksinimim vardı.

Xaml ( PasswordViewModel özelliğidir):

<PasswordBox>
    <i:Interaction.Behaviors>
        <behaviors:PasswordBinding BoundPassword="{Binding Password, Mode=TwoWay}" />
    </i:Interaction.Behaviors>
</PasswordBox>

Davranış:

using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;

namespace Evidence.OutlookIntegration.AddinLogic.Behaviors
{
    /// <summary>
    /// Intermediate class that handles password box binding (which is not possible directly).
    /// </summary>
    public class PasswordBoxBindingBehavior : Behavior<PasswordBox>
    {
        // BoundPassword
        public SecureString BoundPassword { get { return (SecureString)GetValue(BoundPasswordProperty); } set { SetValue(BoundPasswordProperty, value); } }
        public static readonly DependencyProperty BoundPasswordProperty = DependencyProperty.Register("BoundPassword", typeof(SecureString), typeof(PasswordBoxBindingBehavior), new FrameworkPropertyMetadata(OnBoundPasswordChanged));

        protected override void OnAttached()
        {
            this.AssociatedObject.PasswordChanged += AssociatedObjectOnPasswordChanged;
            base.OnAttached();
        }

        /// <summary>
        /// Link up the intermediate SecureString (BoundPassword) to the UI instance
        /// </summary>
        private void AssociatedObjectOnPasswordChanged(object s, RoutedEventArgs e)
        {
            this.BoundPassword = this.AssociatedObject.SecurePassword;
        }

        /// <summary>
        /// Reacts to password reset on viewmodel (ViewModel.Password = new SecureString())
        /// </summary>
        private static void OnBoundPasswordChanged(object s, DependencyPropertyChangedEventArgs e)
        {
            var box = ((PasswordBoxBindingBehavior)s).AssociatedObject;
            if (box != null)
            {
                if (((SecureString)e.NewValue).Length == 0)
                    box.Password = string.Empty;
            }
        }

    }
}

2

Benim gibi tam yeni başlayanlar için, Konamimanyukarıda önerilenlerin tam bir çalışma örneği . Teşekkürler Konamiman.

XAML

    <PasswordBox x:Name="textBoxPassword"/>
    <Button x:Name="buttonLogin" Content="Login"
            Command="{Binding PasswordCommand}"
            CommandParameter="{Binding ElementName=textBoxPassword}"/> 

ViewModel

public class YourViewModel : ViewModelBase
{
    private ICommand _passwordCommand;
    public ICommand PasswordCommand
    {
        get {
            if (_passwordCommand == null) {
                _passwordCommand = new RelayCommand<object>(PasswordClick);
            }
            return _passwordCommand;
        }
    }

    public YourViewModel()
    {
    }

    private void PasswordClick(object p)
    {
        var password = p as PasswordBox;
        Console.WriteLine("Password is: {0}", password.Password);
    }
}

Bu MVVM modelinin açık bir ihlalidir. Kalıp, görünüm modelindeki denetimlerin işlenmesine izin vermez.
BionicCode

1

Gördüğünüz gibi Şifre bağlıyorum, ama belki statik sınıfa bağlar ..

Bu bir olduğunu ekli özelliği . Bu tür mülkler, DependencyObjectsadece beyan edildiği türe değil, her tür uygulamaya uygulanabilir . Bu nedenle, PasswordHelperstatik sınıfta bildirilmesine rağmen , onu kullandığınız yere uygulanır PasswordBox.

Bu ekli özelliği kullanmak için, onu PasswordViewModel'inizdeki özelliğe bağlamanız yeterlidir :

<PasswordBox w:PasswordHelper.Attach="True" 
         w:PasswordHelper.Password="{Binding Password}"/>

1

Gibi yaptım:

XAML:

<PasswordBox x:Name="NewPassword" PasswordChanged="NewPassword_PasswordChanged"/>
<!--change tablenameViewSource: yours!-->
<Grid DataContext="{StaticResource tablenameViewSource}" Visibility="Hidden">
        <TextBox x:Name="Password" Text="{Binding password, Mode=TwoWay}"/>
</Grid>

C #:

private void NewPassword_PasswordChanged(object sender, RoutedEventArgs e)
    {
        try
        {
           //change tablenameDataTable: yours! and tablenameViewSource: yours!
           tablenameDataTable.Rows[tablenameViewSource.View.CurrentPosition]["password"] = NewPassword.Password;
        }
        catch
        {
            this.Password.Text = this.NewPassword.Password;
        }
    }

Benim için çalışıyor!


Bana güzel bir fikir verdin. :)
Andre Mendonca

1

Daha önce de belirtildiği gibi, VM Görünüm'den habersiz olmalı ancak tüm PasswordBox'u geçmek en basit yaklaşıma benziyor. Yani belki PasswordBox geçirilen parametre döküm yerine ondan Şifre özelliği ayıklamak için Yansıma kullanın. Bu durumda VM, Özellik Parolası ile bir tür Parola Kapsayıcı bekler (MVMM Light-Toolkit'ten RelayCommands kullanıyorum):

public RelayCommand<object> SignIn
{
    get
    {
        if (this.signIn == null)
        {
            this.signIn = new RelayCommand<object>((passwordContainer) => 
                {
                    var password = passwordContainer.GetType().GetProperty("Password").GetValue(passwordContainer) as string;
                    this.authenticationService.Authenticate(this.Login, password);
                });
        }

        return this.signIn;
    }
}

Anonim bir sınıfla kolayca test edilebilir:

var passwordContainer = new
    {
        Password = "password"
    };

Yorumlar uzun tartışmalar için değildir; bu sohbet sohbete taşındı .
Samuel Liew

1

Windows evrensel uygulamasında

bu kodu "Parola" özelliğiyle ve modelView ile bağlayarak kullanabilirsiniz

 <PasswordBox x:Uid="PasswordBox" Password="{Binding Waiter.Password, Mode=TwoWay}" Name="txtPassword" HorizontalAlignment="Stretch" Margin="50,200,50,0" VerticalAlignment="Top"/>


1

Bu uygulamanın getirdiği risklerin farkında olan herkes için, parolanızı ViewModel'inizle eşitlemek için Mode = OneWayToSource öğesini eklemeniz yeterlidir .

XAML

<PasswordBox
    ff:PasswordHelper.Attach="True"
    ff:PasswordHelper.Password="{Binding Path=Password, Mode=OneWayToSource}" />

Neden sadece OneWayToSource?
BK

@BK Cevabımı düzenledi. Teşekkürler.
Kevin

1
Mod bağlayıcı parantez içinde olmamalı mı?
Mat

@Mat Yap. Teşekkürler.
Kevin

1

İşte benim almam:

  1. Parolayı bağlamak için ekli bir özellik kullanmak parolayı güven altına alma amacını bozar. Parola kutusunun Parola özelliği bir nedenden dolayı ikiye bölünemez.

  2. Parola kutusunun komut parametresi olarak iletilmesi, ViewModel'i denetimden haberdar eder. ViewModel cihazınızı yeniden kullanılabilir çapraz platform haline getirmeyi planlıyorsanız bu çalışmaz. VM'nizi Görünümünüzden veya diğer denetimlerden haberdar etmeyin.

  3. Yeni bir özellik, bir arayüz, şifre değiştirilmiş olaylara veya diğer karmaşık şeylere abone olmanın, şifreyi sağlamanın basit bir görevi için gerekli olduğunu düşünmüyorum.

XAML

<PasswordBox x:Name="pbPassword" />
<Button Content="Login" Command="{Binding LoginCommand}" x:Name="btnLogin"/>

Arkadaki kod - arkadaki kodu kullanmak mutlaka MVVM'yi ihlal etmez. İçine herhangi bir iş mantığı koymadığınız sürece.

btnLogin.CommandParameter = new Func<string>(()=>pbPassword.Password); 

ViewModel

LoginCommand = new RelayCommand<Func<string>>(getpwd=> { service.Login(username, getpwd()); });

0

PasswordBox için bir çözüm, WPF Uygulama Çerçevesi (WAF) projesinin ViewModel örnek uygulamasında bulabilirsiniz .

Ancak, Justin haklı. View ve ViewModel arasında şifreyi düz metin olarak geçirmeyin. Bunun yerine SecureString kullanın (Bkz. MSDN PasswordBox).


2
WAF'ın Pop3SettingsView uygulamasında kullanılan yöntem komiktir. PasswordBox passwordBox = (PasswordBox) gönderen; if (ViewModel! = null) {ViewModel.Pop3Password = passwordBox.Password; } Pop3Password of ViewModel, string özelliğidir. yani, onun da güvenli değil .. ekli özelliği kullanmak daha iyi
Michael Sync

0

Veri sınıfına parolayı yazmak için bir arabulucu sınıfı tarafından çağrılan bir alt kimlik tarafından görüntülenen bir kimlik denetimi (bu da bir kimlik doğrulama denetimi uygular) kullandım.

Mükemmel bir çözüm değil; ancak şifremi hareket ettirememe sorunumu çözdü.


0

Henüz bahsedilmeyen özlü MVVM dostu çözüm kullanıyorum. İlk olarak, XAML PasswordBox adı:

<PasswordBox x:Name="Password" />

Sonra görünüm yapıcısına tek bir yöntem çağrısı ekleyin:

public LoginWindow()
{
    InitializeComponent();
    ExposeControl<LoginViewModel>.Expose(this, view => view.Password,
        (model, box) => model.SetPasswordBox(box));
}

Ve bu kadar. Görünüm modeli, DataContext aracılığıyla bir görünüme eklendiğinde bildirim alır ve ayrıldığında başka bir bildirim alır. Bu bildirimin içeriği lambdalar aracılığıyla yapılandırılabilir, ancak genellikle sorunlu kontrolü parametre olarak geçirerek görünüm modelinde sadece bir ayarlayıcı veya yöntem çağrısıdır.

Alt kontroller yerine görünümün ortaya çıkma arayüzüne sahip olması sayesinde MVVM-dostu hale getirilebilir.

Yukarıdaki kod, blogumda yayınlanan yardımcı sınıflara dayanıyor .


0

Yaşlarımı bu işi yapmaya çalışarak geçirdim. Sonunda vazgeçtim ve sadece DevExpress'ten PasswordBoxEdit'i kullandım.

Korkunç hileler çekmeden bağlanmaya izin verdiği için şimdiye kadarki en basit çözümdür.

DevExpress web sitesinde çözüm

Kayıt için DevExpress ile hiçbir şekilde bağlantılı değilim.


0

<UserControl x:Class="Elections.Server.Handler.Views.LoginView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
             xmlns:cal="http://www.caliburnproject.org"
             mc:Ignorable="d" 
             Height="531" Width="1096">
    <ContentControl>
        <ContentControl.Background>
            <ImageBrush/>
        </ContentControl.Background>
        <Grid >
            <Border BorderBrush="#FFABADB3" BorderThickness="1" HorizontalAlignment="Left" Height="23" Margin="900,100,0,0" VerticalAlignment="Top" Width="160">
                <TextBox TextWrapping="Wrap"/>
            </Border>
            <Border BorderBrush="#FFABADB3" BorderThickness="1" HorizontalAlignment="Left" Height="23" Margin="900,150,0,0" VerticalAlignment="Top" Width="160">
                <PasswordBox x:Name="PasswordBox"/>
            </Border>
            <Button Content="Login" HorizontalAlignment="Left" Margin="985,200,0,0" VerticalAlignment="Top" Width="75">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="Click">
                        <cal:ActionMessage MethodName="Login">
                            <cal:Parameter Value="{Binding ElementName=PasswordBox}" />
                        </cal:ActionMessage>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </Button>

        </Grid>
    </ContentControl>
</UserControl>

using System;
using System.Windows;
using System.Windows.Controls;
using Caliburn.Micro;

namespace Elections.Server.Handler.ViewModels
{
    public class LoginViewModel : PropertyChangedBase
    {
        MainViewModel _mainViewModel;
        public void SetMain(MainViewModel mainViewModel)
        {
            _mainViewModel = mainViewModel;
        }

        public void Login(Object password)
        {
            var pass = (PasswordBox) password;
            MessageBox.Show(pass.Password);

            //_mainViewModel.ScreenView = _mainViewModel.ControlPanelView;
            //_mainViewModel.TitleWindow = "Panel de Control";
            //HandlerBootstrapper.Title(_mainViewModel.TitleWindow);
        }
    }
}

;) kolay!


0

Çok basit . Şifre için başka bir özellik oluşturun ve bunu TextBox ile bağlayın

Ancak tüm giriş işlemleri gerçek parola özelliğiyle çalışır

özel dize _Parola;

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

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

            return szChar;
        }

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

public string Şifre {get {return _Password; }

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


Parola kutusunun çift taraflı olmamasının nedeni, parolayı açık bir dizede saklamak istemememizdir. Dize değişmez ve bellekte ne kadar kalacağından emin değiliz.
Lance

0

Peki benim cevap sadece MVVM desen için daha basit

sınıfta görünüm modeli

public string password;

PasswordChangedCommand = new DelegateCommand<RoutedEventArgs>(PasswordChanged);

Private void PasswordChanged(RoutedEventArgs obj)

{

    var e = (WatermarkPasswordBox)obj.OriginalSource;

    //or depending or what are you using

    var e = (PasswordBox)obj.OriginalSource;

    password =e.Password;

}

WinBox'ın sağladığı PasswordBox veya XCeedtoolkit'in sağladığı WatermarkPasswordBox öğelerinin bir RoutedEventArgs oluşturur, böylece onu bağlayabilirsiniz.

şimdi xmal görünümünde

<Xceed:WatermarkPasswordBox Watermark="Input your Password" Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="7" PasswordChar="*" >

        <i:Interaction.Triggers>

            <i:EventTrigger EventName="PasswordChanged">

                <prism:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path= DataContext.PasswordChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path= Password}"/>

            </i:EventTrigger>

        </i:Interaction.Triggers>

    </Xceed:WatermarkPasswordBox>

veya

<PasswordBox Grid.Column="1" Grid.ColumnSpan="3" Grid.Row="7" PasswordChar="*" >

        <i:Interaction.Triggers>

            <i:EventTrigger EventName="PasswordChanged">

                <prism:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path= DataContext.PasswordChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path= Password}"/>

            </i:EventTrigger>

        </i:Interaction.Triggers>

    </PasswordBox>

0

Gönder SecureStringEkli Davranış kullanarak görünüm modeline gönderin veICommand

MVVM'yi uygularken kodun arkasında yanlış bir şey yok. MVVM, görünümü model / iş mantığından ayırmayı amaçlayan mimari bir modeldir. MVVM bu hedefe tekrarlanabilir bir şekilde nasıl ulaşılacağını açıklar (kalıp). Görünümü nasıl yapılandırdığınız veya uyguladığınız gibi uygulama ayrıntılarıyla ilgilenmez. Sadece sınırları çizer ve bu modelin terminolojisi açısından görünümün, görünüm modelinin ve modelin ne olduğunu tanımlar.

MVVM, dili (XAML veya C #) veya derleyiciyi ( partialsınıflar) umursamıyor . Dilden bağımsız olmak, bir tasarım modelinin zorunlu bir özelliğidir - dil açısından tarafsız olmalıdır.

Bununla birlikte, arkadaki kod, XAML ve C # arasında çılgınca dağıtıldığında UI mantığınızı anlamayı zorlaştırmak gibi bazı geri dönüşlere sahiptir. Ancak en önemli uygulama UI mantığı veya C #'da şablonlar, stiller, tetikleyiciler, animasyonlar vb. Gibi nesneler XAML kullanmaktan çok karmaşık ve çirkin / daha az okunabilir. XAML, nesne hiyerarşisini görselleştirmek için etiketleri ve iç içe yerleştirmeyi kullanan bir biçimlendirme dilidir. XAML kullanarak kullanıcı arayüzü oluşturmak çok uygundur. Her ne kadar C # (veya arkada kod) UI mantık uygulamak için iyi durumları vardır. KullanımıPasswordBox bir örnek.

Bu nedenle, PasswordBoxarkasındaki koddaPasswordBox.PasswordChanged , ile , MVVM modelinin ihlali anlamına gelmez.

Açık bir ihlal PasswordBox, görünüm modeline bir kontrol ( ) uygulamaktır. Birçok çözüm bunu tavsiye eder, örn PasswordBox.ICommand.CommandParameter görünüm modeline geçiriyor. Açıkçası çok kötü ve gereksiz bir öneri.

C # kullanmayı umursamıyorsanız, ancak sadece arka plan kodunuzu temiz tutmak veya sadece bir davranış / UI mantığı kapsüllemek istiyorsanız, her zaman ekli özellikleri kullanabilir ve ekli bir davranış uygulayabilirsiniz.

Düz metin parolaya (gerçekten kötü anti-desen ve güvenlik riski) bağlanmayı sağlayan rezil geniş yayılmış yardımcıdan farklı olarak, bu davranış olayı her yükselttiğinde görünüm modeline göre bir ICommandparola göndermek için kullanır .SecureStringPasswordBoxPasswordBox.PasswordChanged

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <PasswordBox PasswordBox.Command="{Binding VerifyPasswordCommand}" />
</Window>

ViewModel.cs

public class ViewModel : INotifyPropertyChanged
{
  public ICommand VerifyPasswordCommand => new RelayCommand(VerifyPassword);

  public void VerifyPassword(object commadParameter)
  {
    if (commandParameter is SecureString secureString)
    {
      IntPtr valuePtr = IntPtr.Zero;
      try
      {
        valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
        string plainTextPassword = Marshal.PtrToStringUni(valuePtr);

        // Handle plain text password. 
        // It's recommended to convert the SecureString to plain text in the model, when really needed.
      } 
      finally 
      {
        Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
      }
    }
  }
}

PasswordBox.cs

// Attached behavior
class PasswordBox : DependencyObject
{
  #region Command attached property

  public static readonly DependencyProperty CommandProperty =
    DependencyProperty.RegisterAttached(
      "Command",
      typeof(ICommand),
      typeof(PasswordBox),
      new PropertyMetadata(default(ICommand), PasswordBox.OnSendPasswordCommandChanged));

  public static void SetCommand(DependencyObject attachingElement, ICommand value) =>
    attachingElement.SetValue(PasswordBox.CommandProperty, value);

  public static ICommand GetCommand(DependencyObject attachingElement) =>
    (ICommand) attachingElement.GetValue(PasswordBox.CommandProperty);

  #endregion

  private static void OnSendPasswordCommandChanged(
    DependencyObject attachingElement,
    DependencyPropertyChangedEventArgs e)
  {
    if (!(attachingElement is System.Windows.Controls.PasswordBox passwordBox))
    {
      throw new ArgumentException("Attaching element must be of type 'PasswordBox'");
    }

    if (e.OldValue != null)
    {
      return;
    }

    WeakEventManager<object, RoutedEventArgs>.AddHandler(
      passwordBox,
      nameof(System.Windows.Controls.PasswordBox.PasswordChanged),
      SendPassword_OnPasswordChanged);
  }

  private static void SendPassword_OnPasswordChanged(object sender, RoutedEventArgs e)
  {
    var attachedElement = sender as System.Windows.Controls.PasswordBox;
    SecureString commandParameter = attachedElement?.SecurePassword;
    if (commandParameter == null || commandParameter.Length < 1)
    {
      return;
    }

    ICommand sendCommand = GetCommand(attachedElement);
    sendCommand?.Execute(commandParameter);
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.