Mam aplikację internetową Spring MVC, która korzysta z Spring Security. Chcę poznać nazwę użytkownika aktualnie zalogowanego. Używam fragmentu kodu podanego poniżej. Czy to jest akceptowany sposób?
Nie podoba mi się wywołanie metody statycznej w tym kontrolerze - co przeczy całemu celowi Springa, IMHO. Czy istnieje sposób, aby skonfigurować aplikację tak, aby zamiast niej podawać bieżący kontekst zabezpieczeń lub bieżące uwierzytelnianie?
@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(final HttpServletRequest request...) {
final String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
...
}
java
spring
spring-mvc
spring-security
Scott Bale
źródło
źródło
Odpowiedzi:
Jeśli używasz Spring 3 , najprostszym sposobem jest:
źródło
W świecie wiosennym wiele się zmieniło od czasu odpowiedzi na to pytanie. Wiosna uprościła wprowadzanie bieżącego użytkownika do kontrolera. W przypadku innych ziaren Spring przyjął sugestie autora i uprościł zastrzyk „SecurityContextHolder”. Więcej szczegółów znajduje się w komentarzach.
To jest rozwiązanie, z którym skończyłem. Zamiast używać
SecurityContextHolder
w moim kontrolerze, chcę wstrzyknąć coś, co używaSecurityContextHolder
pod maską, ale abstrahuje od mojego kodu klasę podobną do singletona. Nie znalazłem innego sposobu niż zrobienie tego z własnym interfejsem:Teraz mój kontroler (lub cokolwiek POJO) wyglądałby tak:
A ponieważ interfejs jest punktem odsprzęgania, testowanie jednostkowe jest proste. W tym przykładzie używam Mockito:
Domyślna implementacja interfejsu wygląda następująco:
I wreszcie produkcyjna konfiguracja wiosenna wygląda następująco:
Wydaje się bardziej niż trochę głupie, że Spring, pojemnik do wstrzykiwania zależności wszystkich rzeczy, nie dostarczył sposobu na wstrzyknięcie czegoś podobnego. Rozumiem, że
SecurityContextHolder
został odziedziczony z acegi, ale nadal. Chodzi o to, że są tak blisko - gdyby tylkoSecurityContextHolder
mieli getter, aby uzyskać bazowąSecurityContextHolderStrategy
instancję (która jest interfejsem), moglibyście to wstrzyknąć. W rzeczywistości nawet otworzyłem w tym celu kwestię Jiry .I ostatnia rzecz - zasadniczo zmieniłem odpowiedź, którą tu wcześniej miałem. Sprawdź historię, jeśli jesteś ciekawy, ale, jak wskazał mi współpracownik, moja poprzednia odpowiedź nie zadziałałaby w środowisku wielowątkowym. Bazą
SecurityContextHolderStrategy
używaną przezSecurityContextHolder
jest domyślnie instancjaThreadLocalSecurityContextHolderStrategy
, która przechowujeSecurityContext
s w plikuThreadLocal
. Dlatego niekoniecznie dobrym pomysłem jest wstrzyknięcieSecurityContext
bezpośrednio do komponentu bean w czasie inicjalizacji - może zaThreadLocal
każdym razem być konieczne pobranie go w środowisku wielowątkowym, aby pobrać właściwy.źródło
Zgadzam się, że zapytanie o SecurityContext dla obecnego użytkownika śmierdzi, wydaje się to bardzo nie-wiosenny sposób poradzenia sobie z tym problemem.
Napisałem statyczną klasę „pomocnika”, aby poradzić sobie z tym problemem; jest brudny, ponieważ jest to metoda globalna i statyczna, ale pomyślałem w ten sposób, jeśli zmienimy coś związanego z bezpieczeństwem, przynajmniej muszę zmienić szczegóły tylko w jednym miejscu:
źródło
Aby po prostu pojawił się na stronach JSP, możesz użyć Spring Security Tag Lib:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html
Aby użyć dowolnego ze znaczników, musisz zadeklarować taglib bezpieczeństwa na swojej stronie JSP:
Następnie na stronie jsp wykonaj coś takiego:
UWAGA: Jak wspomniano w komentarzach @ SBerg413, musisz dodać
do znacznika „http” w konfiguracji security.xml, aby to działało.
źródło
Jeśli używasz Spring Security ver> = 3.2, możesz użyć
@AuthenticationPrincipal
adnotacji:Tutaj
CustomUser
jest niestandardowy obiekt, który implementuje,UserDetails
który jest zwracany przez niestandardowyUserDetailsService
.Więcej informacji można znaleźć w rozdziale @AuthenticationPrincipal dokumentów referencyjnych Spring Security.
źródło
Otrzymuję uwierzytelnionego użytkownika przez HttpServletRequest.getUserPrincipal ();
Przykład:
źródło
null
jeśli użytkownik zostanie uwierzytelniony anonimowo (http
>anonymous
elementy w Spring Security XML).SecurityContextHolder
lubSecurityContextHolderStrategy
jest właściwy sposób.Wiosną 3+ masz następujące opcje.
Opcja 1 :
Opcja 2 :
Opcja 3:
Opcja 4: Fancy one: Sprawdź to, aby uzyskać więcej informacji
źródło
@CurrentUser
która działa jak niestandardowy@ActiveUser
link.@AuthenticationPrincipal
z niestandardową@CurrentUser
adnotacją. Od wersji 3.2 nie musimy implementować niestandardowego narzędzia do rozwiązywania argumentów, jak w połączonej odpowiedzi. Ta inna odpowiedź ma więcej szczegółów.Tak, statyka jest ogólnie zła - ogólnie, ale w tym przypadku statyczność jest najbezpieczniejszym kodem, jaki możesz napisać. Ponieważ kontekst bezpieczeństwa wiąże Principal z aktualnie działającym wątkiem, najbezpieczniejszy kod uzyskałby dostęp do statycznego z wątku tak bezpośrednio, jak to możliwe. Ukrywanie dostępu za wstrzykiwaną klasą opakowania zapewnia atakującemu więcej punktów do ataku. Nie potrzebowaliby dostępu do kodu (który byłby trudny do zmiany, gdyby jar był podpisany), po prostu potrzebują sposobu na zastąpienie konfiguracji, co można zrobić w czasie wykonywania lub wstawić trochę XML na ścieżkę klasy. Nawet użycie wstrzyknięcia adnotacji w podpisanym kodzie byłoby możliwe do zastąpienia za pomocą zewnętrznego XML. Taki XML mógłby wstrzyknąć działającemu systemowi nieuczciwą zasadę.
źródło
Po prostu zrobiłbym to:
źródło
SecurityContextHolderAwareRequestFilter
zapytanie, które otacza żądanie i realizuje to wywołanie, uzyskując dostęp doSecurityContextHolder
.W ostatniej aplikacji Spring MVC, którą napisałem, nie wstrzyknąłem uchwytu SecurityContext, ale miałem podstawowy kontroler, który miałem dwie metody narzędziowe związane z tym ... isAuthenticated () i getUsername (). Wewnętrznie wykonują opisane przez ciebie wywołanie metody statycznej.
Przynajmniej wtedy jest tylko w jednym miejscu, jeśli chcesz później refaktoryzować.
źródło
Możesz użyć metody Spring AOP. Na przykład, jeśli masz jakąś usługę, musi znać aktualny zleceniodawca. Możesz wprowadzić niestandardową adnotację, tj. @Principal, która wskazuje, że ta usługa powinna być zależna od użytkownika.
Następnie w swojej radzie, którą moim zdaniem należy rozszerzyć MethodBeforeAdvice, sprawdź, czy dana usługa ma adnotację @Principal i wstrzyknij nazwę główną, lub ustaw ją zamiast tego na „ANONIMOWY”.
źródło
Jedynym problemem jest to, że nawet po uwierzytelnieniu w Spring Security, użytkownik / główny komponent bean nie istnieje w kontenerze, więc wstrzyknięcie zależności będzie trudne. Zanim skorzystaliśmy z Spring Security, stworzyliśmy fasolę o zasięgu sesji, która miała obecnego Zleceniodawcę, wstrzyknęliśmy ją do „AuthService”, a następnie wstrzyknęliśmy tę Usługę do większości innych usług w Aplikacji. Więc te usługi po prostu wywołałyby authService.getCurrentUser (), aby uzyskać obiekt. Jeśli masz miejsce w kodzie, w którym znajduje się odwołanie do tego samego Zleceniodawcy w sesji, możesz po prostu ustawić go jako właściwość dla komponentu bean o zasięgu sesji.
źródło
Spróbuj tego
źródło
Najlepszym rozwiązaniem, jeśli używasz Spring 3 i potrzebujesz uwierzytelnionego podmiotu głównego w kontrolerze, to zrobić coś takiego:
źródło
@AuthenticationPrincipal
Adnotacji używam zarówno w@Controller
klasach, jak i w@ControllerAdvicer
adnotacjach. Dawny.:Gdzie
UserActive
jest klasa, z której korzystam dla usług zalogowanych użytkowników i od której się rozszerzaorg.springframework.security.core.userdetails.User
. Coś jak:Naprawdę proste.
źródło
Zdefiniuj
Principal
jako zależność w metodzie kontrolera, a sprężyna wstrzykuje bieżącego uwierzytelnionego użytkownika do metody podczas wywołania.źródło
Lubię udostępniać sposób wspierania danych użytkownika na stronie Freemarker. Wszystko jest bardzo proste i działa idealnie!
Musisz tylko złożyć żądanie uwierzytelnienia
default-target-url
(strona po formularzu logowania) To jest moja metoda Kontrolera dla tej strony:A to jest mój kod ftl:
I to wszystko, nazwa użytkownika pojawi się na każdej stronie po autoryzacji.
źródło