web-dev-qa-db-ja.com

OAuth 2トークンをリソースサーバーのUserDetailsオブジェクトにマップする方法を教えてください。

2つの個別のSpring Bootアプリケーションがあり、1つはOAuth 2認証サーバーとして機能し、もう1つはリソースサーバーとして機能します。リソースサーバーでSpringのRemoteTokenServicesを使用して、承認サーバーからのトークンをチェックしています。現在、リソースサーバーアプリケーションで保護されたコントローラーコードを定義しようとしていますが、UserDetailsクラスをOAuth 2メカニズムを通じて提供される認証プリンシパルにマップする方法がわかりません。

カスタムTokenEnhancerを使用して認証サーバーを設定しました。これにより、/oauth/check_token?token=<token>がカスタムフィールドを返し、リソースサーバーコントローラーにマップするように、トークンに詳細が追加されます。

承認サーバーがリソースサーバーでもあるよりモノリシックなセットアップでは、認証されたプリンシパルを次のように利用するコントローラーメソッドを定義できます。

//User implements UserDetails
public Map<String, Object> getResource(@AuthenticationPrincipal User user) {
    //code that uses the user object
}

ただし、これはより分散されたアプローチでは簡単には機能しないようです。マッピングは失敗し、userパラメータは最終的にnullオブジェクトになります。私は次のアプローチを試してみました:

public Map<String, Object> getResource(Authentication authentication) {
    //code that uses the authentication object
}

上記のコードは認証の詳細を正常にマップしますが、前述のTokenEnhancerを使用して設定したカスタムフィールドに直接アクセスする方法はありません。これに関するSpringドキュメントから何も見つけられないようです。

11
Psycho Punch

この問題を解決するために、まずアーキテクチャの背景について少し説明します。 _@AuthenticationPrincipal_によって自動的にマップされるUserDetailsオブジェクトは、アクティブなprincipalオブジェクトのAuthenticationフィールドから取得されます。リソースサーバーコントローラーは、メソッドパラメーターの一部として宣言するだけで、Spring OAuth2セキュリティフレームワークのAuthenticationの特殊なインスタンスである_OAuth2Authencation_オブジェクトにアクセスできます。

_public void controllerMethod(OAuth2Authentication authentication) {
    //controller definition
}
_

これを知って、問題はAuthenticationオブジェクトのgetPrincipal()メソッドがカスタムUserDetailsクラスのインスタンスであることを確認する方法に移ります。リソースサーバーアプリケーションで使用するRemoteTokenServicesは、AccessTokenConverterのインスタンスを使用して、承認サーバーから送信されたトークンの詳細を解釈します。デフォルトでは、DefaultAccessTokenConverterを使用します。これは、認証プリンシパルをユーザー名として設定するだけで、これはStringです。このコンバーターは、UserAuthenticationConverterを使用して、許可サーバーからのデータをAuthenticationのインスタンスに変換します。これは私がカスタマイズする必要があったものです:

_DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {

    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        Authentication authentication = super.extractAuthentication(map);
        // User is my custom UserDetails class
        User user = new User();
        user.setSpecialKey(map.get("specialKey").toString());
        return new UsernamePasswordAuthenticationToken(user,
                authentication.getCredentials(), authentication.getAuthorities());
    }

});
tokenServices.setAccessTokenConverter(tokenConverter);
_

これらすべての設定が完了すると、_@AuthenticationPrincipal_メカニズムが期待どおりに機能するようになります。

20
Psycho Punch

次のxmlのように、AuthenticationPrincipalArgumentResolverを有効にしましたか?

<mvc:annotation-driven>
        <mvc:argument-resolvers>
                <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
        </mvc:argument-resolvers>
</mvc:annotation-driven>

また、UserDetailsを実装して独自のCustomerUser objectを返す必要がある場合は、アノテーションを使用してプリンシパルを直接取得できます。

0
chaoluo