web-dev-qa-db-ja.com

アクティブなユーザーのUserDetailsを取得する方法

私のコントローラーでは、アクティブな(ログインしている)ユーザーが必要なときに、UserDetails実装を取得するために次のことを行っています。

User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());

うまくいきますが、このような場合、Springは人生を楽にしてくれると思います。 UserDetailsをコントローラーまたはメソッドに自動接続する方法はありますか?

たとえば、次のようなもの:

public ModelAndView someRequestHandler(Principal principal) { ... }

しかし、UsernamePasswordAuthenticationTokenを取得する代わりに、代わりにUserDetailsを取得しますか?

エレガントなソリューションを探しています。何か案は?

159
The Awnry Bear

前文:Spring-Security 3.2以降、この回答の最後に説明されているニース注釈@AuthenticationPrincipalがあります。これは、Spring-Security> = 3.2を使用する場合に最適な方法です。

次の場合:

  • 古いバージョンのSpring-Securityを使用し、
  • プリンシパルに保存されているいくつかの情報(ログインやIDなど)によってデータベースからカスタムユーザーオブジェクトを読み込む必要があります。
  • HandlerMethodArgumentResolverまたはWebArgumentResolverがこれをエレガントな方法でどのように解決できるか、または単に@AuthenticationPrincipalAuthenticationPrincipalArgumentResolverの背景を学習したい(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のコメントを参照


Spring 3.1+の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 Solution

Spring Security 3.2(Spring 3.2と混同しないでください)には、独自のビルドインソリューションがあります: @AuthenticationPrincipalorg.springframework.security.web.bind.annotation.AuthenticationPrincipal)。これは Lukas Schmelzeisenの答え でうまく説明されています

ただ書いています

ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
    ...
 }

これを機能させるには、AuthenticationPrincipalArgumentResolverorg.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver)を登録する必要があります。@EnableWebMvcSecurityを「アクティブ化」するか、mvc:argument-resolvers内でこのBeanを登録します。

@See Spring Security 3.2リファレンス、第11.2章@AuthenticationPrincipal


Spring-Security 4.0ソリューション

Spring 3.2ソリューションと同様に機能しますが、Spring 4.0では@AuthenticationPrincipalおよび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

209
Ralph

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 を参照してください

62

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つの主な利点は、テスト用の固定データを使用した簡単な実装を簡単に作成できることです。スレッドローカルなどの設定を心配する必要はありません。

25
Shaun the Sheep

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());
        }
}
9
atrain

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は次のとおりです。

8
geoand
@Controller
public abstract class AbstractController {
    @ModelAttribute("loggedUser")
    public User getLoggedUser() {
        return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }
}
5
Comrade

また、テンプレート(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>
0
GKislin