web-dev-qa-db-ja.com

2-legged(クライアント資格情報)OAuth2サーバーのSpringセキュリティコンテキストのセットアップ

REST 1つのクライアントのサーバーを保護したい場合、spring-security OAuth2の最小セットアップは何ですか?不要なセットアップを使用したり、不要なBeanを実装したりしません。たぶん "簡単な」チュートリアル/スプリングセキュリティ+ OAuth2のサンプルはすでにあるか(私はそれについてあまり期待しすぎないようにしていますが)

私の現在の作業設定(sparklrコンテキストからのcopy + past + wtfでの作業)は、次のように感じすぎます。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
       xmlns:sec="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2
                           http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
                           http://www.springframework.org/schema/security
                           http://www.springframework.org/schema/security/spring-security-3.1.xsd
                           http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
        <oauth:client-credentials />
    </oauth:authorization-server>

    <sec:authentication-manager alias="clientAuthenticationManager">
        <sec:authentication-provider user-service-ref="clientDetailsUserService" />
    </sec:authentication-manager>

    <http pattern="/oauth/token" create-session="stateless"
            authentication-manager-ref="clientAuthenticationManager"
            xmlns="http://www.springframework.org/schema/security">
        <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
        <anonymous enabled="false" />
        <http-basic entry-point-ref="clientAuthenticationEntryPoint" />

        <!-- include this only if you need to authenticate clients via request parameters -->
        <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
        <access-denied-handler ref="oauthAccessDeniedHandler" />
    </http>

    <oauth:resource-server id="resourceServerFilter"
            resource-id="rest_server" token-services-ref="tokenServices" />

    <oauth:client-details-service id="clientDetails">
        <oauth:client client-id="the_client" authorized-grant-types="client_credentials" 
                authorities="ROLE_RESTREAD" secret="1234567890" />
    </oauth:client-details-service>


    <http pattern="/**" create-session="never"
            entry-point-ref="oauthAuthenticationEntryPoint"
            access-decision-manager-ref="accessDecisionManager"
            xmlns="http://www.springframework.org/schema/security">
        <anonymous enabled="false" />

        <intercept-url pattern="/rest/**" access="ROLE_RESTREAD" method="GET" />
        <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
        <access-denied-handler ref="oauthAccessDeniedHandler" />
    </http>

    <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />

    <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <property name="tokenStore" ref="tokenStore" />
        <property name="supportRefreshToken" value="false" />
        <property name="clientDetailsService" ref="clientDetails" />
        <property name="accessTokenValiditySeconds" value="400000" />
        <property name="refreshTokenValiditySeconds" value="0" />
    </bean>

    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"
            xmlns="http://www.springframework.org/schema/beans">
        <constructor-arg>
            <list>
                <bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
                <bean class="org.springframework.security.access.vote.RoleVoter" />
                <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
            </list>
        </constructor-arg>
    </bean>


    <bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
        <property name="realmName" value="theRealm" />
    </bean>

    <bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
        <property name="realmName" value="theRealm/client" />
        <property name="typeName" value="Basic" />
    </bean>

    <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <property name="authenticationManager" ref="clientAuthenticationManager" />
    </bean>


    <bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <constructor-arg ref="clientDetails" />
    </bean>

    <bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />


    <sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
        <sec:expression-handler ref="oauthExpressionHandler" />
    </sec:global-method-security>

    <oauth:expression-handler id="oauthExpressionHandler" />

    <oauth:web-expression-handler id="oauthWebExpressionHandler" />
</beans>   

基本的なSpring-Securityの実装の一環としてauthenticationManager(UserDetailsS​​ervice)をすでに実装しているため、アカウントとロールがデータベースに対して永続化されます。


私が実際に手に入らない豆は次のとおりです。

serApprovalHandler:client_credentialsフロー/付与でユーザー承認が必要なのはなぜですか?どうやら、sparklrはデフォルトのTokenServicesUserApprovalHandlerをオーバーライドして、1つのクライアントを自動承認します。信頼されたクライアントとサーバー間の通信のためにもそれを行う必要がありますか?

oauthAuthenticationEntryPoint:これに関するすべてのsparklrは次のとおりです。

<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <property name="realmName" value="sparklr2" />
</bean>

それは何をすることになっていますか?

clientCredentialsTokenEndpointFilterこれは、リクエストパラメータを介して認証する場合にのみこれを含める必要があることを示しています。つまり、私が念頭に置いているのは、GET(?)リクエストをシークレットとトークンを取得し、そのトークンでリソースにアクセスしますか?だから私は考えています、トークンのリクエストにはリクエストパラメータとしてシークレットを含める必要があります..?

resourceServerFilterこれは別のリソースサーバーを示しているようです。リソースが認証プロバイダーと同じサーバー上にある場合、どのように適用されますか?

accessDecisionManagerカスタムのスプリングセキュリティ実装をセットアップするときにこれを使用する必要があったことを覚えていませんが、なぜ今すぐ使用したいのですか?

お読みいただきありがとうございます。誰かが私の質問のいくつかに答えてくれることを願っています。

更新

設定を現在の動作状態に更新しました。これで、クライアント資格情報を使用してアクセストークンをリクエストできます。

$ curl -X -v -d 'client_id=the_client&client_secret=secret&grant_type=client_credentials' -X POST "http://localhost:9090/our-server/oauth/token"

そのトークンを使用して、保護されたリソースにアクセスします。

$ curl -H "Authorization: Bearer fdashuds-5432fsd5-sdt5s5d-sd5" "http://localhost:9090/our-server/rest/social/content/posts"

それでも多くの設定のように感じられ、私の質問は残っています。また、信頼できるクライアントとRESTサーバー全般)の間の通信をセキュリティで保護するための正しい方法であるかどうかも疑問に思っています。

また、https経由で行われる場合を除いて、トークンの最初のリクエストは安全ではないように感じますが、それで十分でしょうか?

また、トークン自体についてはどうですか?トークンに長い有効期間を与えて、クライアントに永続化する必要がありますか?いずれにしても、トークンの有効期限の例外をキャッチして、新しい例外を要求することを意味します。または、すべてのリクエストに対してハンドシェイクを行う必要がありますか?トークンの更新についてはどうですか?更新トークンがクライアント資格情報付与タイプに対して安全でないことをどこかで読んだと思います。トークンをHTTPヘッダーとして送信する必要がありますか、それとも変更できますか?クライアントにSpring-Securityクライアントスタックを使用したくありません。クライアントレガシーセットアップ(jboss 5)があり、これまでに行ったことは、統合REST要求パラメーターを使用した通信機能です。 ..

また、すべてのスプリングセキュリティセットアップについて詳しく知ることも役立ちますが、ドキュメントは非常に薄いです。

編集

Springのセキュリティ設定を現在の状態に更新しました。また、これがweb.xmlです。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://Java.Sun.com/xml/ns/javaee" xmlns:web="http://Java.Sun.com/xml/ns/javaee/web-app_2_5.xsd"
        xsi:schemaLocation="http://Java.Sun.com/xml/ns/javaee http://Java.Sun.com/xml/ns/javaee/web-app_2_5.xsd"
        id="WebApp_ID" version="2.5">

    <display-name>the-display-name</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-context.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>jersey-serlvet</servlet-name>     
        <servlet-class>
            com.Sun.jersey.spi.spring.container.servlet.SpringServlet
        </servlet-class>        
        <init-param>
            <param-name>com.Sun.jersey.config.property.packages</param-name>
            <param-value>base.package.rest</param-value>
        </init-param>               
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>jersey-serlvet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
            /WEB-INF/servlet-context.xml            
            </param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.appServlet</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

注:上記のspring-security-context.xmlは、servlet-contextによって初期化されます。 spring-context.xml自体は、Beanのみを初期化します。 (また、私たちのサーバーにはいくつかのビューもあるので、残りのリソースはすべて/ restの下で実行されるため、URLパターンになります。ただし、サーブレットとSpringのコンテキストは別にする必要があります。)

31
Pete

serApprovalHandler:システムにクライアントが1つしかない場合、ユーザーがデータにアクセスすることを承認する必要がないことに同意します。

oauthAuthenticationEntryPoint:通常、認証が失敗した場合、応答タイプはJSONです。ドキュメントは、「認証が失敗し、発信者が特定のコンテンツタイプの応答を要求した場合、このエントリポイントは、標準の401ステータスとともに応答を送信できます」と述べています。

clientCredentialsTokenEndpointFilter:アクセストークンの発行は2段階のプロセスです。まず、ユーザーを認証のためにリソースサーバーに送信します。このリダイレクトは、理想的にはHTTPヘッダー(キー+シークレット)を使用してクライアントによって認証されます。代わりに、クライアントはトークンと交換できるコードを取得します。ユーザーからの承認が含まれていないため、キーとシークレットを直接トークンと交換することはありません。

resourceServerFilter:Ithinkこの目的は、さまざまなリソースがある場合に、どのクライアントがどのリソースにアクセスできるかを示すことです。

accessDecisionManager:OAuth2の場合はScopeVoterが必要なので、デフォルトのマネージャーでは不十分です。

一般的に:ユーザーに代わってリソースにアクセスするクライアントが1つだけの場合は、OAuth2ではなくDigestの使用を検討してください。 (ユーザーではなく)クライアントのみを認証したい場合、OAuth2は過剰です。 OAuth2のクライアント認証は、https経由の基本認証と実際に同じです。

9
sosandstrom

RESTクライアントとスプリングセキュリティouath 2.0を使用するユーザーのoauth2の良い例: http://www.e-zest.net/blog/rest-authentication-using-oauth -2-0-resource-owner-password-flow-protocol /

4
K. Pavlov