【JavaEE】SSH+Spring Security+Spring oauth2整合及example


現在加最后一樣,就是oauth2,現在很多網站都有對應的移動版本,那么移動端訪問服務端的服務怎么控制權限,我知道的主要是兩種方法,第一是模擬瀏覽器,訪問服務的時候會生成session,之后在移動端緩存cookie,每次網絡請求都把cookie加上,還有一種就是通過oauth2,登錄之后生成一個憑證,每次請求時攜帶憑證,當然oauth2更多的是為第三方應用提供訪問自己服務的權限。

oauth2的配置,可以純配置文件打造,相比較前面的那些,可以說是最簡單也是最復雜的,簡單是因為引入jar包配置一個xml就可以,復雜是說這個僅有的xml需要寫的東西很多理解起來也要費勁。

關於Oauth2的整合,很大程度上參考了這位仁兄的文章:http://blog.csdn.net/monkeyking1987/article/details/16828059

1. pom.xml

首先導入oauth2的包:

<properties>
    ……
    <spring-security-oauth2.version>2.0.2.RELEASE</spring-security-oauth2.version>
</properties>

<dependencies>
    ……
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
        <version>${spring-security-oauth2.version}</version>
    </dependency>
    ……
</dependencies>

2. applicationContext-security.xml

oauth2是security的一部分,配置也有關聯,就不再單建文件,首先要在最前面加一些schema:

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth2="http://www.springframework.org/schema/security/oauth2"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                        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.2.xsd">

其實就是加了一個oauth2的命名。

在這個文件的開始,也就是之前配置的攔截鏈的http標簽前面並列一個http標簽:

<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>

這個標簽處理/oauth/token的網絡請求,這是oauth2的登錄驗證請求,那么登錄需要什么,首先,和Spring Security一樣,需要一個認證管理器,Spring Oauth2需要兩個認證管理器,第一個就是之前Spring中配置的那一個,用來驗證用戶名密碼的,還有一個是用來區分客戶端用戶的,給它起個名字叫oauth2AuthenticationManager:

<oauth2:client-details-service id="clientDetailsService">
    <oauth2:client client-id="mobile_1" authorized-grant-types="password,authorization_code,refresh_token,implicit"
                secret="secret_1"scope="read,write,trust" />
</oauth2:client-details-service>
<beans:bean id="oauth2ClientDetailsUserService"
                class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
    <beans:constructor-arg ref="clientDetailsService"/>
</beans:bean>
<authentication-manager id="oauth2AuthenticationManager">
    <authentication-provider user-service-ref="oauth2ClientDetailsUserService"/>
</authentication-manager>

這兒設置了一種客戶端,id叫做mobile_1,secret叫做secret_1,針對read、write和trust幾個域有效。這幾個域會在訪問控制中被用到。

當登錄成功之后會得到一個token,再次訪問的時候需要攜帶這個token,spring-oauth2根據這個token來做認證,那么spring-oauth2必須先存一份token和用戶關系的對應,因為不用session了,這就相當於session,那么這個token在服務器中怎么存,有兩種主要的存儲方式,一是創建數據表,把token存到數據庫里,我現在追求簡單可用,采用第二種方式,直接存到內存里。下面配置一個管理token的service:

<beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore"/>
<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>

下面配置4個基本的bean:分別處理訪問成功、訪問拒絕、認證點和訪問控制:

<beans:bean id="oauth2AuthenticationEntryPoint"
                class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>
    
<beans:bean id="oauth2AccessDeniedHandler"
                class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
<beans:bean id="oauthUserApprovalHandler" 
                class="org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler"/>
    
<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>

下一步,配置這個oauth2的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>

比如說,如果配置本服務器不支持刷新token,那么就:

<oauth2:refresh-token disabled="true" />

我們的請求里,要把驗證類型、用戶名密碼都作為表單參數提交,這就需要配置下面的filter:

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

下面定義一種資源,指定spring要保護的資源,如果沒有這個,訪問控制的時候會說沒有Authentication object:

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

好了,到此為止基本配置就都有了,下面就看訪問控制的配置:在前面的攔截鏈上,已經為登錄驗證配了一個/auth/token,在這個標簽下面添加對/json和/admin這兩個路徑的控制(這里沒有再寫這兩個訪問的controller,依舊用前面幾篇文章中寫好的):

<http pattern="/json**" create-session="never"
        entry-point-ref="oauth2AuthenticationEntryPoint"
        access-decision-manager-ref="oauth2AccessDecisionManager">
    <anonymous enabled="false" />
    <intercept-url pattern="/json**" access="ROLE_USER" />
    <custom-filter ref="mobileResourceServer" before="PRE_AUTH_FILTER" />
    <access-denied-handler ref="oauth2AccessDeniedHandler" />
</http>
<http pattern="/admin**" create-session="never"
        entry-point-ref="oauth2AuthenticationEntryPoint"
        access-decision-manager-ref="oauth2AccessDecisionManager">
    <anonymous enabled="false" />
    <intercept-url pattern="/admin**" access="ROLE_ADMIN,SCOPE_READ" />
    <custom-filter ref="mobileResourceServer" before="PRE_AUTH_FILTER" />
    <access-denied-handler ref="oauth2AccessDeniedHandler" />
</http>

我們用oauth2AccessDecisionManager來做決策,這個地方需要注意,spring-security里面配置access="ROLE_USER,ROLE_ADMIN"是說user和admin都可以訪問,是一個“或”的關系,但是這里是“與”的關系,比如第二個,需要ROLE_ADMIN並且當前的scope包含read才可以,否則就沒有權限。認證失敗會返回一段xml,這個可以自定義handler來修改,暫且按下不表。

源碼下載

ps:使用方法:用戶名密碼前面spring-security中已經創建了用戶,驗證身份時訪問:

http://localhost:8080/demo4ssh-security-oauth2/oauth/token?client_id=mobile_1&client_secret=secret_1&grant_type=password&username=zhangsan&password=123456

這時候會返回一個access_token:

{"access_token":"4219a91f-45d5-4a07-9e8e-3acbadd0c23e","token_type":"bearer","refresh_token":"d41df9fd-3d36-4a20-b0b7-1a1883c7439d","expires_in":43199,"scope":"read write trust"}

這之后再拿着這個access_token去訪問資源:

http://localhost:8080/demo4ssh-security-oauth2/admin?access_token=4219a91f-45d5-4a07-9e8e-3acbadd0c23e

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM