The*_*ear 165 java spring spring-mvc spring-security
在我的控制器中,当我需要活动(登录)用户时,我正在执行以下操作来获取我的UserDetails
实现:
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
Run Code Online (Sandbox Code Playgroud)
它工作正常,但我认为Spring可以在这样的情况下让生活更轻松.有没有办法将UserDetails
自动装配到控制器或方法?
例如,类似于:
public ModelAndView someRequestHandler(Principal principal) { ... }
Run Code Online (Sandbox Code Playgroud)
但是,而不是得到UsernamePasswordAuthenticationToken
,我得到了一个UserDetails
?
我正在寻找一个优雅的解决方案.有任何想法吗?
Ral*_*lph 219
序言:从Spring-Security 3.2开始,@AuthenticationPrincipal
在这个答案的最后描述了一个很好的注释.当您使用Spring-Security> = 3.2时,这是最好的方法.
当你:
HandlerMethodArgumentResolver
或WebArgumentResolver
在一个优雅的方式解决这个问题,或者只是想一个学习后面的背景@AuthenticationPrincipal
和AuthenticationPrincipalArgumentResolver
(因为它是基于一个HandlerMethodArgumentResolver
)然后继续阅读 - 其他只是使用@AuthenticationPrincipal
并感谢Rob Winch(作者@AuthenticationPrincipal
)和Lukas Schmelzeisen(他的回答).
(顺便说一句:我的答案有点老了(2012年1月),所以Lukas Schmelzeisen是第一个带有@AuthenticationPrincipal
Spring Security 3.2注释解决方案的人.)
然后你可以在你的控制器中使用
public ModelAndView someRequestHandler(Principal principal) {
User activeUser = (User) ((Authentication) principal).getPrincipal();
...
}
Run Code Online (Sandbox Code Playgroud)
如果你需要它一次就可以了.但是,如果你需要它几次丑陋,因为它会污染你的控制器的基础设施细节,通常应该被框架隐藏.
所以你真正想要的是拥有一个像这样的控制器:
public ModelAndView someRequestHandler(@ActiveUser User activeUser) {
...
}
Run Code Online (Sandbox Code Playgroud)
因此,您只需要实现一个WebArgumentResolver
.它有一种方法
Object resolveArgument(MethodParameter methodParameter,
NativeWebRequest webRequest)
throws Exception
Run Code Online (Sandbox Code Playgroud)
获取Web请求(第二个参数)并且必须返回User
if if感觉对方法参数(第一个参数)负责.
从Spring 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
您需要定义自定义注释 - 如果应始终从安全上下文中获取User的每个实例,但是从不是命令对象,则可以跳过它.
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ActiveUser {}
Run Code Online (Sandbox Code Playgroud)
在配置中,您只需添加以下内容:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
id="applicationConversionService">
<property name="customArgumentResolver">
<bean class="CurrentUserWebArgumentResolver"/>
</property>
</bean>
Run Code Online (Sandbox Code Playgroud)
@See:学习自定义Spring MVC @Controller方法参数
应该注意的是,如果您使用的是Spring 3.1,他们会在WebArgumentResolver上推荐HandlerMethodArgumentResolver. - 见Jay的评论
HandlerMethodArgumentResolver
Spring 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在配置中,您需要添加它
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="CurrentUserHandlerMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
Run Code Online (Sandbox Code Playgroud)
@See 利用Spring MVC 3.1 HandlerMethodArgumentResolver接口
春季安全3.2(不使用Spring 3.2混淆)有自己打造的解决方案:@AuthenticationPrincipal
(org.springframework.security.web.bind.annotation.AuthenticationPrincipal
).这在Lukas Schmelzeisen的回答中得到了很好的描述
这只是写作
ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
...
}
Run Code Online (Sandbox Code Playgroud)
要使这个工作,您需要注册AuthenticationPrincipalArgumentResolver
(org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver
):通过"激活" @EnableWebMvcSecurity
或通过注册这个bean mvc:argument-resolvers
- 就像我在上面讨论的Spring Spring 3.1解决方案一样.
@参见Spring Security 3.2参考,第11.2章.@AuthenticationPrincipal
它的工作方式与Spring 3.2解决方案类似,但在Spring 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) {
...
}
Run Code Online (Sandbox Code Playgroud)
要使这个工作,您需要注册(org.springframework.security.web.method.annotation.
)AuthenticationPrincipalArgumentResolver
:通过"激活" @EnableWebMvcSecurity
或通过注册这个bean mvc:argument-resolvers
- 就像我在上面讨论的Spring Spring 3.1解决方案一样.
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
Run Code Online (Sandbox Code Playgroud)
@参见Spring Security 5.0参考,第39.3章@AuthenticationPrincipal
Luk*_*sen 62
虽然Ralphs Answer提供了一个优雅的解决方案,但使用Spring Security 3.2,您不再需要实现自己的解决方案ArgumentResolver
.
如果您有UserDetails
实现CustomUser
,您可以这样做:
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {
// .. find messages for this User and return them...
}
Run Code Online (Sandbox Code Playgroud)
请参阅Spring Security Documentation:@AuthenticationPrincipal
Sha*_*eep 27
Spring Security旨在与其他非Spring框架一起使用,因此它没有与Spring MVC紧密集成.默认情况下,Spring Security会Authentication
从HttpServletRequest.getUserPrincipal()
方法返回对象,因此您将获得作为主体的内容.您可以UserDetails
使用以下方法直接从中获取对象
UserDetails ud = ((Authentication)principal).getPrincipal()
Run Code Online (Sandbox Code Playgroud)
另请注意,对象类型可能会有所不同,具体取决于所使用的身份验证机制(UsernamePasswordAuthenticationToken
例如,您可能不会获得),并且Authentication
严格来说不必包含a UserDetails
.它可以是字符串或任何其他类型.
如果您不想SecurityContextHolder
直接调用,最优雅的方法(我将遵循)是注入您自己的自定义安全上下文访问器接口,该接口是自定义的,以匹配您的需求和用户对象类型.使用相关方法创建接口,例如:
interface MySecurityAccessor {
MyUserDetails getCurrentUser();
// Other methods
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过访问SecurityContextHolder
标准实现来实现此功能,从而完全将您的代码与Spring Security分离.然后将其注入需要访问当前用户的安全信息或信息的控制器.
另一个主要好处是很容易使用固定数据进行简单的实现测试,而不必担心填充线程本地等等.
实现HandlerInterceptor
接口,然后将注入UserDetails
到具有Model的每个请求中,如下所示:
@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());
}
}
Run Code Online (Sandbox Code Playgroud)
从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";
}
}
Run Code Online (Sandbox Code Playgroud)
CustomUser需要从中分配 authentication.getPrincipal()
以下是AuthenticationPrincipal和AuthenticationPrincipalArgumentResolver的相应Javadoc
@Controller
public abstract class AbstractController {
@ModelAttribute("loggedUser")
public User getLoggedUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
119173 次 |
最近记录: |