私のコントローラーでは、アクティブな(ログインしている)ユーザーが必要なときに、UserDetails
実装を取得するために次のことを行っています。
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
うまくいきますが、このような場合、Springは人生を楽にしてくれると思います。 UserDetails
をコントローラーまたはメソッドに自動接続する方法はありますか?
たとえば、次のようなもの:
public ModelAndView someRequestHandler(Principal principal) { ... }
しかし、UsernamePasswordAuthenticationToken
を取得する代わりに、代わりにUserDetails
を取得しますか?
エレガントなソリューションを探しています。何か案は?
前文:Spring-Security 3.2以降、この回答の最後に説明されているニース注釈@AuthenticationPrincipal
があります。これは、Spring-Security> = 3.2を使用する場合に最適な方法です。
次の場合:
HandlerMethodArgumentResolver
またはWebArgumentResolver
がこれをエレガントな方法でどのように解決できるか、または単に@AuthenticationPrincipal
とAuthenticationPrincipalArgumentResolver
の背景を学習したい(HandlerMethodArgumentResolver
に基づいているため)それから読み続けてください。それ以外の場合は@AuthenticationPrincipal
を使用して、Rob Winch(@AuthenticationPrincipal
の作成者)と Lukas Schmelzeisen (彼の答え)に感謝します。
(BTW:私の答えは少し古いです(2012年1月)、 Lukas Schmelzeisen Spring Security 3.2の@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
これはWebリクエスト(2番目のパラメーター)を取得し、メソッド引数(1番目のパラメーター)に責任があると感じる場合はUser
を返す必要があります。
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
と同じ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 Spring MVC 3.1 HandlerMethodArgumentResolverインターフェースの活用
Spring Security 3.2(Spring 3.2と混同しないでください)には、独自のビルドインソリューションがあります: @AuthenticationPrincipal
(org.springframework.security.web.bind.annotation.AuthenticationPrincipal
)。これは Lukas Schmelzeisenの答え でうまく説明されています
ただ書いています
ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
...
}
これを機能させるには、AuthenticationPrincipalArgumentResolver
(org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver
)を登録する必要があります。@EnableWebMvcSecurity
を「アクティブ化」するか、mvc:argument-resolvers
内でこのBeanを登録します。
@See 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) {
...
}
これを機能させるには、(org.springframework.security.web.method.annotation.
)AuthenticationPrincipalArgumentResolver
を登録する必要があります。これは、@EnableWebMvcSecurity
を「アクティブ化」するか、mvc:argument-resolvers
内でこのBeanを登録することです。
<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リファレンス、第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 Documentation:@AuthenticationPrincipal を参照してください
Spring Securityは、他の非Springフレームワークと連携することを目的としているため、Spring MVCと密接に統合されていません。 Spring SecurityはデフォルトでHttpServletRequest.getUserPrincipal()
メソッドからAuthentication
オブジェクトを返すので、それがプリンシパルとして取得されます。これを使用してUserDetails
オブジェクトを直接取得できます。
UserDetails ud = ((Authentication)principal).getPrincipal()
また、オブジェクトタイプは、使用する認証メカニズムによって異なる場合があり(たとえば、UsernamePasswordAuthenticationToken
を取得できない場合があります)、Authentication
に厳密にUserDetails
を含める必要はありません。文字列またはその他のタイプを指定できます。
SecurityContextHolder
を直接呼び出したくない場合、最もエレガントな方法(これに従う)は、ニーズとユーザーオブジェクトタイプに合わせてカスタマイズされた独自のカスタムセキュリティコンテキストアクセサーインターフェイスを挿入することです。たとえば、関連するメソッドを使用して、インターフェイスを作成します。
interface MySecurityAccessor {
MyUserDetails getCurrentUser();
// Other methods
}
その後、標準実装のSecurityContextHolder
にアクセスして、これを実装し、Spring Securityからコードを完全に切り離します。次に、これをセキュリティ情報または現在のユーザーの情報にアクセスする必要があるコントローラーに挿入します。
もう1つの主な利点は、テスト用の固定データを使用した簡単な実装を簡単に作成できることです。スレッドローカルなどの設定を心配する必要はありません。
HandlerInterceptor
インターフェイスを実装し、次のように、Modelを持つ各リクエストに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から、いくつかの古い回答によって実装されたカスタム機能は、AuthenticationPrincipalArgumentResolver
によってサポートされる@AuthenticationPrincipal
アノテーションの形式ですぐに使用できます。
その使用の簡単な例は次のとおりです。
@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>