Jak uzyskać aktywny UserDetails
W moich kontrolerach, gdy potrzebuję aktywnego (zalogowanego) użytkownika, wykonuję następujące czynności, aby uzyskać implementację UserDetails
:
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
Działa dobrze, ale myślę, że wiosna może ułatwić życie w takim przypadku. Czy istnieje sposób na automatyczne podłączenie UserDetails
do kontrolera lub metody?
Na przykład coś w stylu:
public ModelAndView someRequestHandler(Principal principal) { ... }
Ale zamiast dostać UsernamePasswordAuthenticationToken
, dostaję UserDetails
zamiast?
8 answers
Preambuła: od Spring-Security 3.2 jest ładna adnotacja @AuthenticationPrincipal
opisana na końcu tej odpowiedzi. Jest to najlepszy sposób, gdy używasz Spring-Security > = 3.2.
Kiedy:
- użyj starszej wersji Spring-Security,
- trzeba załadować obiekt użytkownika z bazy danych za pomocą niektórych informacji (takich jak login lub id) przechowywanych w głównym lub
- chcesz dowiedzieć się, jak
HandlerMethodArgumentResolver
lubWebArgumentResolver
może rozwiązać ten problem w elegancki sposób, lub po prostu chcę poznać tło za@AuthenticationPrincipal
iAuthenticationPrincipalArgumentResolver
(ponieważ opiera się naHandlerMethodArgumentResolver
)
To czytaj dalej-w przeciwnym razie użyj @AuthenticationPrincipal
i podziękuj Robowi Winchowi (Autor @AuthenticationPrincipal
) i Lukasowi Schmelzeisenowi (za jego odpowiedź).
(BTW: moja odpowiedź jest nieco starsza (styczeń 2012), więc to Lukas Schmelzeisen pojawił się jako pierwszy z bazą @AuthenticationPrincipal
adnotation solution na Spring Security 3.2.)
Wtedy możesz użyj w kontrolerze
public ModelAndView someRequestHandler(Principal principal) {
User activeUser = (User) ((Authentication) principal).getPrincipal();
...
}
To jest w porządku, jeśli potrzebujesz go raz. Ale jeśli potrzebujesz go kilka razy jego brzydki, ponieważ zanieczyszcza kontroler ze szczegółami infrastruktury, które normalnie powinny być ukryte przez framework.
Więc to, czego naprawdę chcesz, to mieć taki kontroler:]}public ModelAndView someRequestHandler(@ActiveUser User activeUser) {
...
}
Dlatego wystarczy zaimplementować WebArgumentResolver
. Posiada metodę
Object resolveArgument(MethodParameter methodParameter,
NativeWebRequest webRequest)
throws Exception
, który pobiera web request (drugi parametr) i musi zwrócić User
jeśli its czuje się odpowiedzialny za argument metody (pierwszy parametr).
od wiosny 3.1 pojawiła się nowa koncepcja o nazwie HandlerMethodArgumentResolver
. Jeśli używasz Springa 3.1+, powinieneś go użyć. (Opisane jest ono w następnym rozdziale niniejszej odpowiedzi))
public class CurrentUserWebArgumentResolver implements WebArgumentResolver{
Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
if(methodParameter is for type User && methodParameter is annotated with @ActiveUser) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
Musisz zdefiniować niestandardową adnotację -- możesz ją pominąć, jeśli każda instancja użytkownika powinna być zawsze wzięta z kontekstu bezpieczeństwa, ale nigdy nie jest obiektem polecenia.
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ActiveUser {}
W konfiguracja wystarczy tylko dodać:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
id="applicationConversionService">
<property name="customArgumentResolver">
<bean class="CurrentUserWebArgumentResolver"/>
</property>
</bean>
@Zobacz: Naucz się dostosowywać argumenty metody Spring MVC @ Controller
należy zauważyć, że jeśli używasz Spring 3.1, polecają HandlerMethodArgumentResolver nad WebArgumentResolver. - zobacz komentarz by Jay
To samo z HandlerMethodArgumentResolver
na wiosnę 3.1+
public class CurrentUserHandlerMethodArgumentResolver
implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return
methodParameter.getParameterAnnotation(ActiveUser.class) != null
&& methodParameter.getParameterType().equals(User.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
if (this.supportsParameter(methodParameter)) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
W konfiguracji, musisz dodać to
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="CurrentUserHandlerMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
@See 3.1 interfejs HandlerMethodArgumentResolver
Spring-Security 3.2 Solution
Spring Security 3.2 (nie mylić z Spring 3.2) ma własne rozwiązanie wbudowane: @AuthenticationPrincipal
(org.springframework.security.web.bind.annotation.AuthenticationPrincipal
) . To jest ładnie opisane w odpowiedź Lukasa Schmelzeisena
To tylko pisanie
ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
...
}
Aby to działało musisz zarejestrować AuthenticationPrincipalArgumentResolver
(org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver
) : albo poprzez "aktywację" @EnableWebMvcSecurity
albo poprzez zarejestrowanie tej fasoli w mvc:argument-resolvers
- tak samo jak opisałem to z rozwiązaniem may Spring 3.1 powyżej.
@See Spring Security 3.2 Reference, Rozdział 11.2. @AuthenticationPrincipal
Spring-Security 4.0 Solution
Działa jak rozwiązanie Spring 3.2, ale wiosną 4.0 @AuthenticationPrincipal
i AuthenticationPrincipalArgumentResolver
zostały "przeniesione" do innego pakietu:
org.springframework.security.core.annotation.AuthenticationPrincipal
org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver
(ale stare klasy w swoich starych paczkach nadal istnieją, więc nie mieszaj ich!)
To tylko pisanie
import org.springframework.security.core.annotation.AuthenticationPrincipal;
ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
...
}
Aby to działało musisz zarejestrować (org.springframework.security.web.method.annotation.
) AuthenticationPrincipalArgumentResolver
: albo "aktywując" @EnableWebMvcSecurity
, albo rejestrując tę fasolę w mvc:argument-resolvers
- tak samo jak opisałem to z rozwiązaniem may Spring 3.1 powyżej.
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
@See Spring Security 5.0 Reference, Chapter 39.3 @AuthenticationPrincipal
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-23 20:34:28
Podczas Gdy Ralphs Answer zapewnia eleganckie rozwiązanie, dzięki Spring Security 3.2 nie musisz już implementować własnego ArgumentResolver
.
Jeśli masz UserDetails
implementację CustomUser
, możesz to zrobić:
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {
// .. find messages for this User and return them...
}
Zobacz Wiosenna Dokumentacja Bezpieczeństwa: @ AuthenticationPrincipal
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-02 21:14:01
Spring Security jest przeznaczony do pracy z innymi frameworkami nie-Spring, dlatego nie jest ściśle zintegrowany z Spring MVC. Spring Security domyślnie zwraca obiekt {[2] } z metody HttpServletRequest.getUserPrincipal()
, więc to otrzymujesz jako główny. Możesz uzyskać swój UserDetails
obiekt bezpośrednio z tego za pomocą
UserDetails ud = ((Authentication)principal).getPrincipal()
Zauważ również, że typy obiektów mogą się różnić w zależności od zastosowanego mechanizmu uwierzytelniania (na przykład możesz nie otrzymać UsernamePasswordAuthenticationToken
), a Authentication
nie musi ściśle zawiera UserDetails
. Może to być łańcuch lub dowolny inny typ.
Jeśli nie chcesz wywoływać bezpośrednio SecurityContextHolder
, najbardziej eleganckim podejściem (które bym zastosował) jest wstrzyknięcie własnego interfejsu security context accessor, który jest dostosowany do Twoich potrzeb i typów obiektów użytkownika. Tworzenie interfejsu z odpowiednimi metodami, na przykład:
interface MySecurityAccessor {
MyUserDetails getCurrentUser();
// Other methods
}
Możesz zaimplementować to poprzez dostęp do SecurityContextHolder
w standardowej implementacji, oddzielając w ten sposób Kod od zabezpieczeń Spring całkowicie. Następnie wstrzyknij to do kontrolerów, które potrzebują dostępu do informacji o zabezpieczeniach lub informacji o bieżącym użytkowniku.
Inną główną korzyścią jest to, że łatwo jest tworzyć proste implementacje ze stałymi danymi do testowania, bez martwienia się o zaludnienie miejsc wątków i tak dalej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-16 19:28:45
Zaimplementuj interfejs HandlerInterceptor
, a następnie wprowadź UserDetails
do każdego żądania, które ma Model, w następujący sposób:
@Component
public class UserInterceptor implements HandlerInterceptor {
....other methods not shown....
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if(modelAndView != null){
modelAndView.addObject("user", (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal());
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-09 15:08:45
Począwszy od Spring Security w wersji 3.2, niestandardowa funkcjonalność, która została zaimplementowana przez niektóre starsze odpowiedzi, istnieje po wyjęciu z pudełka w formie adnotacji @AuthenticationPrincipal
, która jest wspierana przez AuthenticationPrincipalArgumentResolver
.
Prosty przykład jego użycia to:
@Controller
public class MyController {
@RequestMapping("/user/current/show")
public String show(@AuthenticationPrincipal CustomUser customUser) {
// do something with CustomUser
return "view";
}
}
CustomUser musi być przypisany z authentication.getPrincipal()
Oto odpowiednie Javadocs z AuthenticationPrincipal i AuthenticationPrincipalArgumentResolver
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 08:35:43
@Controller
public abstract class AbstractController {
@ModelAttribute("loggedUser")
public User getLoggedUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-19 13:52:24
I jeśli potrzebujesz autoryzowanego użytkownika w szablonach (np. JSP) użyj
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authentication property="principal.yourCustomField"/>
Razem z
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security.version}</version>
</dependency>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-05 16:22:40
Możesz spróbować tego: Przy pomocy obiektu uwierzytelniania z Spring możemy uzyskać z niego dane użytkownika w metodzie kontrolera . Poniżej znajduje się przykład przekazania obiektu uwierzytelniania w metodzie kontrolera wraz z argumentem.Po uwierzytelnieniu użytkownika dane są uzupełniane w obiekcie uwierzytelniania.
@GetMapping(value = "/mappingEndPoint") <ReturnType> methodName(Authentication auth) {
String userName = auth.getName();
return <ReturnType>;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-27 16:18:53