내 컨트롤러에서 활성 (로그인 된) 사용자가 필요할 때 UserDetails
구현 을 위해 다음을 수행하고 있습니다 .
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
잘 작동하지만 스프링이 이런 경우 인생을 더 쉽게 만들 수 있다고 생각합니다. (가)이 할 수있는 방법이 있나요 UserDetails
컨트롤러 나 방법 중 하나에를 autowire는?
예를 들면 다음과 같습니다.
public ModelAndView someRequestHandler(Principal principal) { ... }
그러나 대신을 얻는 UsernamePasswordAuthenticationToken
, 내가 얻을 UserDetails
대신을?
우아한 솔루션을 찾고 있습니다. 어떤 아이디어?
답변
서문 : Spring-Security 3.2 이후 @AuthenticationPrincipal
로이 답변의 끝에 멋진 주석 이 있습니다. 이것은 Spring-Security> = 3.2를 사용할 때 가장 좋은 방법입니다.
때를:
- 이전 버전의 Spring-Security를 사용하십시오.
- 주체에 저장된 일부 정보 (예 : 로그인 또는 ID)로 데이터베이스에서 사용자 정의 사용자 개체를로드해야합니다.
- 방법을 배우고 싶어
HandlerMethodArgumentResolver
하거나WebArgumentResolver
우아한 방법으로이 문제를 해결하거나 뒤에 배경을 배울 수있는 단지 원하는 수@AuthenticationPrincipal
와AuthenticationPrincipalArgumentResolver
(그것이 기반으로하기 때문에HandlerMethodArgumentResolver
)
그런 다음 계속 읽으십시오. 그렇지 않으면 @AuthenticationPrincipal
Rob Winch (저자 @AuthenticationPrincipal
)와 Lukas Schmelzeisen (자신의 답변) 에게 감사의 말씀을 전 합니다.
(BTW : 제 답변은 조금 더 오래되었으므로 (2012 년 1 월) Spring Security 3.2에 주석 솔루션 기반을 가진 최초의 사람으로 등장한 것은 Lukas Schmelzeisen 이었습니다 @AuthenticationPrincipal
.)
그런 다음 컨트롤러에서 사용할 수 있습니다
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
메소드 인수 (첫 번째 매개 변수)에 대한 책임이 있다고 생각되면 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;
}
}
}
사용자 정의 어노테이션을 정의해야합니다. 모든 사용자 인스턴스가 항상 보안 컨텍스트에서 가져와야하지만 명령 오브젝트가 아닌 경우이를 무시할 수 있습니다.
@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>
@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;
}
}
}
구성에서 이것을 추가해야합니다
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="CurrentUserHandlerMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
@Spring MVC 3.1 HandlerMethodArgumentResolver 인터페이스 활용
스프링 보안 3.2 솔루션
Spring Security 3.2 (Spring 3.2와 혼동하지 마십시오)에는 자체 솔루션이 내장되어 있습니다 : @AuthenticationPrincipal
( org.springframework.security.web.bind.annotation.AuthenticationPrincipal
). 이것은 Lukas Schmelzeisen의 답변 에 잘 설명되어 있습니다.
그냥 쓰고 있어요
ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
...
}
이 작업을 수행하려면 “활성화” 하거나이 Bean을 등록하여 위의 Spring 3.1 솔루션에서 설명한 것과 같은 방식으로 AuthenticationPrincipalArgumentResolver
( org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver
) 를 등록해야합니다 .@EnableWebMvcSecurity
mvc:argument-resolvers
@Spring Security 3.2 Reference, 11.2 장을 참조하십시오. Auth
스프링 시큐리티 4.0 솔루션
그것은 봄 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) {
...
}
이 작업을 수행하려면 “활성화” 하거나이 Bean을 등록하여 위의 Spring 3.1 솔루션에서 설명한 것과 같은 방식으로 ( org.springframework.security.web.method.annotation.
) 를 등록해야합니다 .AuthenticationPrincipalArgumentResolver
@EnableWebMvcSecurity
mvc:argument-resolvers
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
@Spring Security 5.0 참조 서, 39.3 장 @AuthenticationPrincipal 참조
답변
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...
}
답변
Spring Security는 Spring 이외의 다른 프레임 워크와 작동하도록 설계되었으므로 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";
}
}
CustomUser는 다음에서 할당 가능해야합니다. authentication.getPrincipal()
다음은 대응하는 AuthenticationPrincipal 및 AuthenticationPrincipalArgumentResolver의 Javadoc입니다.
답변
@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>