1. 程式人生 > >Spring Security 與 Oauth2 整合 步驟

Spring Security 與 Oauth2 整合 步驟

為什麼要寫

1. spring-security-oauth2的demo 不容易讓開發者理解, 配置的內容很多, 沒有分解的步驟; 我曾經試著按照文件(https://github.com/spring-projects/spring-security-oauth/blob/master/docs/oauth2.md) 配置了幾次, 結果全失敗, 無一成功(說實話, 這是第二次在實際專案中使用spring & oauth,但還是花了不少時間才完全弄清楚);甚至有時候找錯誤的原因都不好找.

2.Oauth應該屬於security的一部分, 但demo並沒有將二者分開, 混在一起

3.總結現在, 方便未來

相比於demo,作了哪些改進

1. 將Spring MVC配置與Oauth的配置分開, 互不影響

2.將使用者資訊存放資料庫

3.將ClientDetails資料存放於資料庫,並能對資料進行管理

4.擴充套件ClientDetails基本屬性, 新增trusted屬性,用於判斷Client是否是可信任的

5.取消掉demo中一些不必要的配置

6.針對不同的資源配置不同的許可權

7.token存入資料庫而不是記憶體

開始

>>前提:  使用Maven來管理專案; spring-security-oauth的版本號為 1.0.5.RELEASE

1. 新增Maven dependencies;

以下只列出了主要的

        <!--spring security-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-acl</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-crypto</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>1.0.5.RELEASE</version>
        </dependency>


2. web.xml配置; 這一步與只使用Spring Security的配置一樣.

</pre><pre code_snippet_id="73897" snippet_file_name="blog_20131119_2_2257675" name="code" class="html">    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

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

    <!--contextConfigLocation -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/*.xml</param-value>
    </context-param>

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

    <!--hy mvc-->
    <servlet>
        <servlet-name>hy</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>hy</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

對於Spring MVC, 需要配置檔案hy-servlet.xml, 該檔案不是這兒關注的(忽略); 

在classpath建立spring目錄, 在該目錄裡建立 security.xml 檔案, 這是所有步驟配置的重點.

3.security.xml的配置; 重點開始.

3.1 起用註解; TokenEndpoint與AuthorizationEndpoint需要

    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>

3.2  TokenServices 配置

   1). TokenStore, 使用JdbcTokenStore, 將token資訊存放資料庫, 需要提供一個dataSource物件; 也可使用InMemoryTokenStore存於記憶體中

    <!--<beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore"/>-->
    <beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">
        <beans:constructor-arg index="0" ref="dataSource"/>
    </beans:bean>

: 可以在spring-security-oauth2中找到對應的SQL指令碼, 地址為https://github.com/spring-projects/spring-security-oauth/tree/master/spring-security-oauth2/src/test/resources, 目錄中的schema.sql 即是. (以下不再說明SQL指令碼的問題)

  2).TokenServices; 需要注入TokenStore

    <beans:bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <beans:property name="tokenStore" ref="tokenStore"/>
        <beans:property name="supportRefreshToken" value="true"/>
    </beans:bean>

      如果允許重新整理token 請將supportRefreshToken 的值設定為true, 預設為不允許

3.3 ClientDetailsService 配置, 使用JdbcClientDetailsService, 也需要提供dataSource, 替換demo中直接配置在配置檔案中

    <beans:bean id="clientDetailsService" class="org.springframework.security.oauth2.provider.JdbcClientDetailsService">
        <beans:constructor-arg index="0" ref="dataSource"/>
    </beans:bean>

3.4 ClientDetailsUserDetailsService配置, 該類實現了Spring security中 UserDetailsService 介面

    <beans:bean id="oauth2ClientDetailsUserService"
                class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <beans:constructor-arg ref="clientDetailsService"/>
    </beans:bean>

3.5 OAuth2AuthenticationEntryPoint配置
    <beans:bean id="oauth2AuthenticationEntryPoint"
                class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>

3.6 oauth2 AuthenticationManager配置; 在整個配置中,有兩個AuthenticationManager需要配置
    <authentication-manager id="oauth2AuthenticationManager">
        <authentication-provider user-service-ref="oauth2ClientDetailsUserService"/>
    </authentication-manager>
第二個AuthenticationManager用於向獲取UserDetails資訊, 
    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="userService">
            <password-encoder hash="md5"/>
        </authentication-provider>
    </authentication-manager>

userService是一個實現UserDetailsService的Bean

3.7 OAuth2AccessDeniedHandler配置, 實現AccessDeniedHandler介面

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

3.8 UserApprovalHandler配置, 這兒使用DefaultUserApprovalHandler, 這裡是實現client是否可信任的關鍵點,你可以擴充套件該介面來自定義approval行為
    <beans:bean id="oauthUserApprovalHandler" class="org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler">
    </beans:bean>

3.9 authorization-server配置, 核心
    <oauth2:authorization-server client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"
                                 user-approval-handler-ref="oauthUserApprovalHandler">
        <oauth2:authorization-code/>
        <oauth2:implicit/>
        <oauth2:refresh-token/>
        <oauth2:client-credentials/>
        <oauth2:password/>
    </oauth2:authorization-server>

該元素裡面的每個標籤可設定每一種authorized-grant-type的行為. 如disable refresh-token的配置為
<oauth2:refresh-token disabled="true"/>
3.10 Oauth2 AccessDecisionManager配置, 這兒在預設的Spring Security AccessDecisionManager的基礎上添加了ScopeVoter
    <beans:bean id="oauth2AccessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
        <beans:constructor-arg>
            <beans:list>
                <beans:bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter"/>
                <beans:bean class="org.springframework.security.access.vote.RoleVoter"/>
                <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
            </beans:list>
        </beans:constructor-arg>
    </beans:bean>

3.11 resource-server配置, 這兒定義兩咱不同的resource
    <!--unity resource server filter-->
    <oauth2:resource-server id="unityResourceServer" resource-id="unity-resource" token-services-ref="tokenServices"/>

    <!--mobile resource server filter-->
    <oauth2:resource-server id="mobileResourceServer" resource-id="mobile-resource" token-services-ref="tokenServices"/>

注意: 每個resource-id的值必須在對應的ClientDetails中resourceIds值中存在

3.12 ClientCredentialsTokenEndpointFilter配置, 該Filter將作用於Spring Security的chain 鏈條中
    <beans:bean id="clientCredentialsTokenEndpointFilter"
                class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <beans:property name="authenticationManager" ref="oauth2AuthenticationManager"/>
    </beans:bean>

3.13 /oauth/token 的http 配置, 用於監聽該URL的請求, 核心
    <http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="oauth2AuthenticationManager"
          entry-point-ref="oauth2AuthenticationEntryPoint">
        <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/>
        <anonymous enabled="false"/>
        <http-basic entry-point-ref="oauth2AuthenticationEntryPoint"/>

        <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
        <access-denied-handler ref="oauth2AccessDeniedHandler"/>
    </http>

3.14 針對不同resource的http配置, 由於上面配置了兩個resource, 這兒也配置兩個
    <!--unity http configuration-->
    <http pattern="/unity/**" create-session="never" entry-point-ref="oauth2AuthenticationEntryPoint"
          access-decision-manager-ref="oauth2AccessDecisionManager">
        <anonymous enabled="false"/>

        <intercept-url pattern="/unity/**" access="ROLE_UNITY,SCOPE_READ"/>

        <custom-filter ref="unityResourceServer" before="PRE_AUTH_FILTER"/>
        <access-denied-handler ref="oauth2AccessDeniedHandler"/>
    </http>

    <!--mobile http configuration-->
    <http pattern="/m/**" create-session="never" entry-point-ref="oauth2AuthenticationEntryPoint"
          access-decision-manager-ref="oauth2AccessDecisionManager">
        <anonymous enabled="false"/>

        <intercept-url pattern="/m/**" access="ROLE_MOBILE,SCOPE_READ"/>

        <custom-filter ref="mobileResourceServer" before="PRE_AUTH_FILTER"/>
        <access-denied-handler ref="oauth2AccessDeniedHandler"/>
    </http>
注意每一個http對應不同的resourceServer. access-decison-manager-ref對應Oauth的AccessDecisionManager

3.15 預設的http配置,給/oauth/** 設定許可權

    <http access-denied-page="/login.jsp?authorization_error=2" disable-url-rewriting="true"
          authentication-manager-ref="authenticationManager">
        <intercept-url pattern="/oauth/**" access="ROLE_USER,ROLE_UNITY,ROLE_MOBILE"/>
        <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

        <form-login authentication-failure-url="/login.jsp?authentication_error=1" default-target-url="/index.jsp"
                    login-page="/login.jsp" login-processing-url="/login.do"/>
        <logout logout-success-url="/index.jsp" logout-url="/logout.do"/>
        <anonymous/>
    </http>



到此, securiy.xml 配置完畢.

當然,還有些額外的工作你需要做, 如配置dataSource, 建立資料庫, 新增使用者使用者資訊, 管理ClientDetails等等.

Oauth相關的資料都是存放在資料庫, 我們就可以根據表結果建立domain來實現管理.