2014-05-21 14 views
5

Mam straszne przejść, mimo że zrobiłem coś podobnego w aplikacji systemu Windows. Pracuję nad aplikacją WPF (Prism, Unity, MVVM) i właśnie ukończyłem widok logowania. Po poświadczenia użytkownika zostały zatwierdzone przed tabeli w SQL Server to zrobić:Trwałe poświadczenia użytkownika w WPF w/Unity i MVVM

Thread.CurrentPrincipal = user.GenericPrincipal(); 

Klasa użytkownik jest zdefiniowany jako takie:

public class ApplicationIdentity : GenericIdentity, IApplicationIdentity 
{ 
    public string UserName { get; set; } 
    public bool Authenticated { get; set; } 

    public ICollection<IModuleProperties> Modules { get; set; } 
    public ICollection<IViewProperties> Views { get; set; } 

    public ApplicationIdentity(string userName, IAuthenticatedUser authenticatedUser) 
     : base(userName) 
    { 
     UserName = userName; 
     Authenticated = authenticatedUser.Authenticated; 
     Modules = authenticatedUser.Modules; 
     Views = authenticatedUser.Views; 
    } 

    public IPrincipal GenericPrincipal() 
    { 
     return new GenericPrincipal(this, null); 
    } 
} 

Problem używam do jest to, że niemal natychmiast po ekran logowania zostaje odrzucona robię to wezwanie:

var currentUser = (IApplicationIdentity)Thread.CurrentPrincipal.Identity; 

która zgłasza wyjątek, że tożsamość nie może być rzutowany na typ IApplicationIdentity, i nie jestem pewien, co mi brakuje . Wszystkie artykuły SO/Google, które przeczytałem do tej pory, rozwiązały ten problem, uwierzytelniając użytkownika przed AD, ale to nie działa w moim scenariuszu.

Problem, który próbuję tutaj rozwiązać, polega na utrzymaniu aktualnie zalogowanego użytkownika, do którego modułów i widoków powinien mieć dostęp. Jeśli istnieje lepszy sposób na osiągnięcie tego poza ustawieniem CurrentPrincipal, jestem całkowicie otwarty na inne rozwiązania. Dziękuję za pomoc, którą mógłbyś zapewnić!

EDIT (roztwór):

Chcę tylko, aby zamknąć pętlę na to, co było rozwiązaniem. Poniżej znajduje się część samouczka, więc jest nieco długa, ale powinna być pomocna dla każdego, kto się na nią natknie. Przyjęta odpowiedź sugerowała, że ​​wstrzykiwam pojemnik Unity, rejestruję instancję mojego obiektu i pracuję z nim stamtąd. Zgadzam się, że jest to prawdopodobnie poprawne podejście, ale wymagało to ode mnie trochę "zhakowania" mojego Bootstrappera. Zanim jednak przejdę do logiki Bootstrapper, prawdopodobnie powinienem przejrzeć trochę pracy prep.

W moim oryginalnym podejściem moja Logowanie widok nie został zarejestrowany z moim pojemniku, ponieważ widok Logowanie została instancja przed moim inicjującego systemem (App.xaml otwarty widok logowania.)

Pierwszą rzeczą, którą zrobiłem było uczynić moim zdaniem i ViewModel wstrzykiwania ala:

public Login(ILoginViewModel viewModel) 
    { 
     InitializeComponent(); 
     DataContext = viewModel; 
    } 

    public LoginViewModel(IUnityContainer container) 
    { 
     Container = container; 
    } 

znowu: to powinno być znane każdemu, który pracował z Jedności (lub IoC w ogóle). I wtedy potrzebne do zarejestrowania mój obecny obiekt użytkownika z Jedności gdy użytkownik został uwierzytelniony:

private void Login(object obj) 
    { 
     ... 
     if (user.Authenticated) 
     { 
      Container.RegisterInstance("CurrentUser", user); 
     } 
     ... 
    } 

Każdy, kto śledził każdy z Prism/Unity artykułów/MVVM, które obfitują w internecie jest prawdopodobnie zna następujące metody :

protected override IModuleCatalog CreateModuleCatalog() 
    { 
     var catalog = new ModuleCatalog(); 
     catalog.AddModule(typeof (CoreModule)); 
     catalog.AddModule(typeof (CoreModule2)); 
     catalog.AddModule(typeof (CoreModule3)); 
     return catalog; 
    } 

Ta metoda jest prosta wystarczy, ale w realnym scenariuszu światowego modułów użytkownik ma dostęp do prawdopodobnie będzie dynamiczny niż statyczny CreateModuleCatalog() jest wywoływana w metodzie inicjującego Run() (lub kombinacja obu). przed InitializeShell(). W moim przypadku nadal mam moduł statyczny, do którego mieliby dostęp wszyscy użytkownicy (niezależnie od poziomu autoryzacji), ale (moim zdaniem) czułbym się "anty-wzór", aby utworzyć widok logowania z tej metody (nie wspominając o rejestracji typy z Jednością.) Tak więc mój CreateModuleCatalog() stała:

protected override IModuleCatalog CreateModuleCatalog() 
    { 
     var catalog = new ModuleCatalog(); 
     catalog.AddModule(typeof(CoreModule)); 
     return catalog; 
    } 

ja zdecydowaliśmy się używać override InitializeShell() zarejestrować moje typy logowania, wyświetlenie widoku login, itd. I ostatecznie likwidacji z tym za realizację:

protected override void InitializeShell() 
    { 
     base.InitializeShell(); 
     Container.RegisterType(typeof (Login), "LoginView"); 
     Container.RegisterType<ILoginViewModel, LoginViewModel>(); 

     Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown; 
     ShowLogOn(); 

     Application.Current.MainWindow = (Window)Shell; 
     Application.Current.MainWindow.Show(); 
    } 

zamknie działanie aplikacji, jeśli użytkownik w końcu kliknie przycisk Anuluj w widoku logowania, więc prawdopodobnie mogłem umieścić cały kod od ShowLogOn() przed wywołaniem base.InitializeShell(), aby niepotrzebny kod nie był uruchamiany. ShowLogOn() wygląda dokładnie tak, jak można się było spodziewać, że aby wyglądać:

private void ShowLogOn() 
    { 
     var login = Container.Resolve<Login>(); 
     var dialogResult = login.ShowDialog(); 

     if (!dialogResult.Value) 
     { 
      Application.Current.Shutdown(1); 
     } 
     else 
     { 
      LoadAuthorizedModules(); 
      Application.Current.MainWindow = null; 
      Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose; 
     } 
    } 

Jeśli użytkownik anuluje z mojego loginu zdaniem wynik dialogowe będzie fałszywe, a aplikacja jest wyłączony. Jeśli jednak zostały pomyślnie uwierzytelnione, teraz musimy załadować moduły, które mogą zobaczyć. Jest to również całkiem proste:

private void LoadAuthorizedModules() 
    { 
     var currentUser = Container.Resolve<IApplicationIdentity>("CurrentUser"); 
     foreach (var module in currentUser.Modules) 
     { 
      var moduleAssembly = Assembly.Load(module.AssemblyName); 
      var loadingModule = moduleAssembly.GetType(module.Type); 

      ModuleCatalog.AddModule(new ModuleInfo 
       { 
        ModuleName = loadingModule.Name, 
        ModuleType = loadingModule.AssemblyQualifiedName 
       }); 
     } 
    } 

Ta metoda gwarantuje nieco więcej wyjaśnień. Najpierw spójrz na tę linię, ponieważ może to być mylące: zauważ, że nie zarejestrowałem jawnie typu IApplicationIdentity w dowolnym miejscu w moim kodzie! Został jednak zarejestrowany pośrednio, gdy to zrobiłem: technicznie mogłem napisać poprzednie oświadczenie jako: Container.RegisterInstance<IApplicationIdentity>("CurrentUser", user);, ale to jest dla mnie zbyteczne, więc rób to, z czym czujesz się najbardziej komfortowo.

Właściwość Modules z IApplicationIdentity zawiera kolekcję obiektów, które zawierają informacje o modułach, do których bieżący użytkownik ma dostęp. module.AssemblyName to nazwa fizycznego zespołu, w którym znajduje się mój typ modułu niestandardowego, a module.Type to Typ modułu.

Jak widać: gdy typ został załadowany przez moc odbicia, musimy dodać go do właściwości Unity Bootstrappers ModuleCatalog, która jest prosta. Zakładając, że wszystko poszło poprawnie, wykonanie powinno powrócić do metody InitializeShell, a twój Shell powinien się teraz uruchomić.

To było dość długie, ale mam nadzieję, że ktoś uzna to za przydatne. Ponadto byłbym zainteresowany wszelkimi opiniami, jak uczynić ten kod "lepszym". Dzięki!

+0

Nie będzie można rzutować obiektu tożsamości Framework na niestandardowy typ. Prawdopodobnie powinieneś zmienić swój typ ApplicationIdentity, aby miał konstruktor, który zamiast tego przyjmuje obiekt tożsamości. – Geo242

Odpowiedz

1

Możesz sprawdzić to pytanie, jeśli chcesz je zapisać w wątku - https://stackoverflow.com/a/6699699/1798889. Ale jeśli chcesz mieć tylko dostęp do IApplicationIdentity. Proponuję po prostu zarejestrować tę instancję z jednością.

// IUnityContainer can be injected by unity 
IUnityContainer container; 

// Register this instance each time now you call for IApplicationIdentity this object will be returned 
container.RegisterInstance(typeof (IApplicationIdentity), user.GenericPrincipal()); 

Dzięki temu możesz teraz wstrzykiwać IApplicationIdentity, a jedność będzie je spełniała za każdym razem, gdy jej potrzebujesz. Nie musisz się martwić o nici.

+0

Cześć Charles. Spojrzałem na to łącze, ale Windows Identity nie jest tym, co reprezentuje mój obiekt użytkownika, więc nie zadziała. Jednak zrobiłeś dobry punkt o wstrzyknięciu pojemnika, który ostatecznie doprowadził mnie do mojej odpowiedzi. Dzięki za bycie płytą brzmiącą! – dparsons

Powiązane problemy