Как получить UserDetails активного пользователя
в моих контроллерах, когда мне нужен активный (зарегистрированный) пользователь, я делаю следующее, чтобы получить мой UserDetails
реализация:
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
он отлично работает, но я думаю, что весна может облегчить жизнь в таком случае. Есть ли способ иметь UserDetails
autowired в контроллер или метод?
например, что-то вроде:
public ModelAndView someRequestHandler(Principal principal) { ... }
но вместо того, чтобы получать UsernamePasswordAuthenticationToken
, Я UserDetails
вместо?
Я смотрю для элегантного решения. Есть идеи?
7 ответов:
преамбула: С весны-безопасность 3.2 есть хорошая аннотация
@AuthenticationPrincipal
описано в конце этого ответа. Это лучший способ пойти, когда вы используете Spring-Security >= 3.2.если вы:
- используйте более старую версию Spring-Security,
- необходимо загрузить пользовательский объект пользователя из базы данных по некоторой информации (например, логин или id), хранящейся в основном или
- хотите узнать, как
HandlerMethodArgumentResolver
илиWebArgumentResolver
может решить эту проблему элегантным способом, или просто хотите узнать фон позади@AuthenticationPrincipal
иAuthenticationPrincipalArgumentResolver
(потому что он основан наHandlerMethodArgumentResolver
)тогда продолжайте читать-иначе просто используйте
@AuthenticationPrincipal
и спасибо Робу Винчу (автор@AuthenticationPrincipal
) и Lukas Schmelzeisen (для его ответа).(кстати: мой ответ немного старше (январь 2012), так что это было Lukas Schmelzeisen которые приходят как первый с
@AuthenticationPrincipal
основание решения аннотации на безопасности весны 3.2.)
тогда вы можете использовать в вашем контроллере
public ModelAndView someRequestHandler(Principal principal) { User activeUser = (User) ((Authentication) principal).getPrincipal(); ... }
это нормально, если вам это нужно один раз. Но если вам это нужно несколько раз, это уродливо, потому что он загрязняет ваш контроллер деталями инфраструктуры, которые обычно должны быть скрыты фреймворком.
так что вы действительно можете хотеть иметь контроллер, как это:
public ModelAndView someRequestHandler(@ActiveUser User activeUser) { ... }
поэтому вам нужно только реализовать
WebArgumentResolver
. Он имеет методObject resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception
который получает веб-запрос (второй параметр) и должен вернуть
User
если он чувствует себя ответственным за аргумент метода (первый параметр).с весны 3.1 появилась новая концепция под названием
HandlerMethodArgumentResolver
. Если вы используете Spring 3.1+, то вы должны использовать его. (Это описано в следующем разделе этого ответа))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; } } }
вам нужно определить Пользовательская Аннотация -- вы можете пропустить ее, если каждый экземпляр пользователя всегда должен быть взят из контекста безопасности, но никогда не является объектом команды.
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ActiveUser {}
в конфигурации вам нужно только добавить это:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" id="applicationConversionService"> <property name="customArgumentResolver"> <bean class="CurrentUserWebArgumentResolver"/> </property> </bean>
@см.: научитесь настраивать аргументы метода Spring MVC @Controller
следует отметить, что если вы используете Spring 3.1, они рекомендуют HandlerMethodArgumentResolver над WebArgumentResolver. - см. комментарий Джей
то же самое с
HandlerMethodArgumentResolver
для весны 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; } } }
в конфигурации, вам нужно добавить этот
<mvc:annotation-driven> <mvc:argument-resolvers> <bean class="CurrentUserHandlerMethodArgumentResolver"/> </mvc:argument-resolvers> </mvc:annotation-driven>
@See использование пружины MVC 3.1 HandlerMethodArgumentResolver интерфейс
Spring-Security 3.2 Solution
Spring Security 3.2 (не путайте с Spring 3.2) имеет собственное встроенное решение:
@AuthenticationPrincipal
(org.springframework.security.web.bind.annotation.AuthenticationPrincipal
). Это хорошо описано в Лукас Schmelzeisen этоэто просто писать
ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) { ... }
чтобы получить эту работу, вам нужно зарегистрировать
AuthenticationPrincipalArgumentResolver
(org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver
) : либо "активация"@EnableWebMvcSecurity
или зарегистрировав этот боб внутриmvc:argument-resolvers
- так же, как я описал его с решением may Spring 3.1 выше.@See Пружинная Защита 3.2 Ссылка, Глава 11.2. @AuthenticationPrincipal
Spring-Security 4.0 Solution
он работает как решение Spring 3.2, но весной 4.0
@AuthenticationPrincipal
иAuthenticationPrincipalArgumentResolver
был "перемещен" в другой пакет:
org.springframework.security.core.annotation.AuthenticationPrincipal
org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver
(но старые классы в своих старых упаковочных комплектах все еще существует, поэтому не смешивайте их!)
это просто пишу
import org.springframework.security.core.annotation.AuthenticationPrincipal; ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) { ... }
чтобы получить эту работу вам нужно зарегистрировать (
org.springframework.security.web.method.annotation.
)AuthenticationPrincipalArgumentResolver
: либо "активация"@EnableWebMvcSecurity
или зарегистрировав этот боб внутриmvc:argument-resolvers
- так же, как я описал его с решением may Spring 3.1 выше.<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, Глава 39.3 @AuthenticationPrincipal
пока Ральф Отвечает обеспечивает элегантное решение, с Spring Security 3.2 вам больше не нужно реализовывать свой собственный
ArgumentResolver
.если у вас
UserDetails
реализацияCustomUser
, вы можете просто сделать это:@RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) { // .. find messages for this User and return them... }
посмотреть Spring Security Documentation: @AuthenticationPrincipal
Spring Security предназначена для работы с другими не пружинными фреймворками, поэтому она не тесно интегрирована с Spring MVC. Spring Security возвращает
Authentication
объектHttpServletRequest.getUserPrincipal()
метод по умолчанию, так что это то, что вы получаете в качестве основного. Вы можете получить вашUserDetails
объект непосредственно с помощьюUserDetails ud = ((Authentication)principal).getPrincipal()
обратите внимание также, что типы объектов могут отличаться в зависимости от используемого механизма аутентификации (вы не можете получить
UsernamePasswordAuthenticationToken
, например) иAuthentication
строго не должен содержатьUserDetails
. Это может быть строка или любой другой тип.если вы не хотите называть
SecurityContextHolder
непосредственно, самый элегантный подход (который я бы последовал) заключается в том, чтобы внедрить свой собственный пользовательский интерфейс доступа к контексту безопасности, который настроен в соответствии с вашими потребностями и типами объектов пользователя. Создайте интерфейс, используя соответствующие методы, например:interface MySecurityAccessor { MyUserDetails getCurrentUser(); // Other methods }
затем вы можете реализовать это путем доступа
SecurityContextHolder
в свой стандарт реализация, таким образом полностью отделяя ваш код от Spring Security. Затем введите это в контроллеры, которым нужен доступ к информации о безопасности или информации о текущем пользователе.другим главным преимуществом является то, что легко сделать простые реализации с фиксированными данными для тестирования, не беспокоясь о заполнении локальных потоков и так далее.
реализовать
HandlerInterceptor
интерфейс, а затем ввестиUserDetails
в каждый запрос, который имеет модель, следующим образом:@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()); } }
начиная с версии Spring Security 3.2, пользовательская функциональность, которая была реализована некоторыми из старых ответов, существует из коробки в виде
@AuthenticationPrincipal
аннотация, которая поддерживаетсяAuthenticationPrincipalArgumentResolver
.простой пример его использования:
@Controller public class MyController { @RequestMapping("/user/current/show") public String show(@AuthenticationPrincipal CustomUser customUser) { // do something with CustomUser return "view"; } }
пользователь должен быть назначен из
authentication.getPrincipal()
здесь представлены соответствующие javadocs из AuthenticationPrincipal и AuthenticationPrincipalArgumentResolver
@Controller public abstract class AbstractController { @ModelAttribute("loggedUser") public User getLoggedUser() { return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); } }
и если вам нужен авторизованный пользователь в шаблонах (например, JSP) используйте
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <sec:authentication property="principal.yourCustomField"/>
вместе с
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring-security.version}</version> </dependency>