spring OAuth快速入門

如何閱讀

前幾節簡單介紹了OAuth,以及spring secuirty OAuth的一些基本構成。文中有一些關聯知識點的引用,若是要深刻研究,能夠點進去詳細瞭解一下,這樣能夠加深當即。若是你只想快速瀏覽OAuth server的搭建能夠跳過前面幾節,直接看下面的代碼實例。html

什麼是OAuth2

OAuth(開放受權)是一個開放標準,容許用戶受權第三方移動應用訪問他們存儲在另外的服務提供者上的信息,而不須要將用戶名和密碼提供給第三方移動應用或分享他們數據的全部內容。

Oauth2 受權方式java

  • authorization_code(受權碼類型,使用三方qq登陸使用該方式)
  • implicit(隱式受權類型)
  • password(資源全部者即用戶密碼類型,典型應用是微服務裏使用uaa服務器進行登陸認證)
  • client_credentials(客戶端憑據【客戶端ID以及Key】類型,典型應用是微服務內部系統與系統之間調用客服端受權)
  • refresh_token(經過以上受權得到的刷新令牌來獲取新的令牌)。

關於OAuth不一樣受權的具體流程能夠參考[理解OAuth 2.0
-阮一峯](http://www.ruanyifeng.com/blo...算法

spring Security OAuth2

spring security OAuth 是對OAuth2協議的一個實現。是在spring security的基礎上發展而來,以前是spring security的一個子項目,如今已經獨立出來。詳細見官網.
spring OAuth使用相似spring secuirty的機制來實現OAuth2 .根據OAuth協議規定,一個完整的受權服務器應當包含:受權服務器和資源服務器。受權服務器負責認證和受權,資源服務器負責根據用戶提供的受權憑證(好比給以token)。這裏有一篇官方文檔介紹了spring OAuth的使用.[OAuth 2 Developers Guide
](http://projects.spring.io/spr...spring

受權服務

一個受權服務大體幾個模塊:client管理、受權接口、用戶認證、令牌管理。
  1. client管理主要用來管理和區分不一樣的client,咱們能夠經過配置認證client連接是否合法,能爲該client提供哪些受權服務,個性化定製client容許的行爲。在spring secruity OAuth2中,能夠對client進行以下屬性配置:
  • clientId:(必須的)用來標識客戶的Id。
  • secret:(須要值得信任的客戶端)客戶端安全碼,若是有的話。
  • scope:用來限制客戶端的訪問範圍,若是爲空(默認)的話,那麼客戶端擁有所有的訪問範圍。
  • authorizedGrantTypes:此客戶端可使用的受權類型,默認爲空。
  • authorities:此客戶端可使用的權限(基於Spring Security authorities)。
  1. 受權接口是受權服務對外提供的http入口。在spring中受權端口以下:

spring security OAuth授端口數據庫

  • /oauth/authorize:受權端點。對應AuthorizationEndpoint類
  • /oauth/token:令牌端點。對應TokenEndpoint類
  • /oauth/confirm_access:用戶確認受權提交端點。對應WhitelabelApprovalEndpoint類
  • /oauth/error:受權服務錯誤信息端點。對應WhitelabelApprovalEndpoint類
  • /oauth/check_token:用於資源服務訪問的令牌解析端點。對應CheckTokenEndpoint類
  • /oauth/token_key:提供公有密匙的端點,若是你使用JWT令牌的話。對應TokenKeyEndpoint類

受權是使用 AuthorizationEndpoint 這個端點來進行控制的,你可以使用 AuthorizationServerEndpointsConfigurer 這個對象的實例來進行配置 ,若是你不進行設置的話,默認是除了資源全部者密碼(password)受權類型之外,支持其他全部標準受權類型的(RFC6749),咱們來看一下這個配置對象有哪些屬性能夠設置吧,以下列表:後端

  • authenticationManager:認證管理器,當你選擇了資源全部者密碼(password)受權類型的時候,請設置這個屬性注入一個 AuthenticationManager 對象。
  • userDetailsService:若是啊,你設置了這個屬性的話,那說明你有一個本身的 UserDetailsService 接口的實現,或者你能夠把這個東西設置到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 這個配置對象),當你設置了這個以後,那麼 "refresh_token" 即刷新令牌受權類型模式的流程中就會包含一個檢查,用來確保這個帳號是否仍然有效,假如說你禁用了這個帳戶的話。
  • authorizationCodeServices:這個屬性是用來設置受權碼服務的(即 AuthorizationCodeServices 的實例對象),主要用於 "authorization_code" 受權碼類型模式。
  • implicitGrantService:這個屬性用於設置隱式受權模式,用來管理隱式受權模式的狀態。
  • tokenGranter:這個屬性就很牛B了,當你設置了這個東西(即 TokenGranter 接口實現),那麼受權將會交由你來徹底掌控,而且會忽略掉上面的這幾個屬性,這個屬性通常是用做拓展用途的,即標準的四種受權模式已經知足不了你的需求的時候,纔會考慮使用這個
  1. 用戶認證,例如在使用password受權模式時,須要在獲取令牌以前先校驗用戶提供的憑證是否合法,合法的憑證是用戶獲取受權令牌的前提,spring secuiurty OAuth使用了spring security的認證服務,在令牌獲取端口AuthenticationManager進行受權,這個會在後面的受權端口中提到。
  2. 令牌管理服務負責令牌是生成、校驗等操做。

令牌主要有兩種解決方案:使用隨機算法生成惟一標示與用戶受權關聯,而後保存起來供校驗時查詢,爲了方便資源服務方便驗證令牌,這種方案經常是受權服務和資源服務共存的,若是不共存,那麼資源服務的tokenStore服務於受權服務tokenStore要作到數據互通,spring 的解決方案是提供/oauth/check_token接口來完成。第二種是受權服務器使用某種算法生成字符串,資源服務器使用約定好的算法對令牌進行解析校驗,以驗證 他的合法性。在spring security OAuth2中提供了InMemoryTokenStore、JdbcTokenStore、JwtTokenStore。前面二者須要將令牌存在起來,最後一個JwtTokenStore是jwt令牌TokenStore的實現,他不存儲令牌,只根據必定的規則和祕鑰驗證令牌的合法性。jwt令牌分爲三段:頭部信息、playload、簽名。每段使用base64編碼,每段之間使用"."鏈接。api

資源服務

一個資源服務(能夠和受權服務在同一個應用中,固然也能夠分離開成爲兩個不一樣的應用程序)提供一些受token令牌保護的資源,Spring OAuth提供者是經過Spring Security authentication filter來驗證過濾器來實現的保護(OAuth2AuthenticationProcessingFilter),你能夠經過 @EnableResourceServer 註解到一個 @Configuration 配置類上來標記應用是一個資源服務器,你能夠經過配置 ResourceServerConfigurer 配置對象來進行資源服務器的一些自定義配置(能夠選擇繼承自 ResourceServerConfigurerAdapter 而後覆寫其中的方法,參數就是這個對象的實例),下面是一些能夠配置的屬性:緩存

  • tokenServices:ResourceServerTokenServices 類的實例,用來實現令牌服務。
  • resourceId:這個資源服務的ID,這個屬性是可選的,可是推薦設置並在受權服務中進行驗證。
  • 其餘的拓展屬性例如 tokenExtractor 令牌提取器用來提取請求中的令牌,也就說,你能夠自定義提如何在請求中提取令牌。
  • 請求匹配器,用來設置須要進行保護的資源路徑,默認的狀況下是受保護資源服務的所有路徑。
  • 受保護資源的訪問規則,默認的規則是簡單的身份驗證(plain authenticated)。
  • 其餘的自定義權限保護規則經過 HttpSecurity 來進行配置。

@EnableResourceServer 註解自動增長了一個類型爲 OAuth2AuthenticationProcessingFilter 的過濾器鏈。
ResourceServerTokenServices 是組成受權服務的另外一半,若是你的受權服務和資源服務在同一個應用程序上的話,你可使用 DefaultTokenServices ,這樣的話,你就不用考慮關於實現全部必要的接口的一致性問題,這一般是很困難的。若是你的資源服務器是分離開的,那麼你就必需要確保可以有匹配受權服務提供的 ResourceServerTokenServices,它知道如何對令牌進行解碼。
在受權服務器上,你一般可使用 DefaultTokenServices 而且選擇一些主要的表達式經過 TokenStore(後端存儲或者本地編碼)。
RemoteTokenServices 能夠做爲一個替代,它將容許資源服務器經過HTTP請求來解碼令牌(也就是受權服務的 /oauth/check_token 端點)。若是你的資源服務沒有太大的訪問量的話,那麼使用RemoteTokenServices 將會很方便(全部受保護的資源請求都將請求一次受權服務用以檢驗token值),或者你能夠經過緩存來保存每個token驗證的結果。安全

spring Security OAuth 配置示例

關於spring OAuth的更多配置細節能夠參考官方文檔: OAuth2 Autoconfig.spring Secuirty OAuth2配置比較簡單,關鍵仍是要理解OAuth2協議,以及他的使用場景。

添加依賴

<dependencies>
  <!-- ... other dependency elements ... -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.1.1.RELEASE</version>
  </dependency>
</dependencies>

受權服務配置

受權服務配置主要須要完成如下幾個配置:bash

  • ClientDetailsServiceConfigurer:用來配置客戶端詳情服務(ClientDetailsService),客戶端詳情信息在這裏進行初始化,你可以把客戶端詳情信息寫死在這裏或者是經過數據庫來存儲調取詳情信息。
  • AuthorizationServerSecurityConfigurer:用來配置令牌端點(Token Endpoint)的安全約束.
  • AuthorizationServerEndpointsConfigurer:用來配置受權(authorization)以及令牌(token)的訪問端點和令牌服務(token services)

咱們能夠繼承AuthorizationServerConfigurerAdapter而且覆寫其中的三個configure方法來進行配置。下面是簡單的
配置demo,配置來源於官方文檔。

@Component
public class CustomAuthorizationServerConfigurer extends
    AuthorizationServerConfigurerAdapter {

    AuthenticationManager authenticationManager;

    public CustomAuthorizationServerConfigurer(
        AuthenticationConfiguration authenticationConfiguration
    ) throws Exception {
        this.authenticationManager =
            authenticationConfiguration.getAuthenticationManager();
    }

    @Override
    public void configure(
        ClientDetailsServiceConfigurer clients
    ) throws Exception {
        //下面配置了一個簡單的客服端
        clients.inMemory()// 使用內存保存客服端信息,建議編寫本身的clientService來配置客服端
            .withClient("client")//客服端的clientid
                .authorizedGrantTypes("password","client_credentials","refresh_token")//支持的受權方式
                .secret("secret")//該客服端訪問時的密碼
                .scopes("all");//scope範圍
    }

    @Override
    public void configure(
        AuthorizationServerEndpointsConfigurer endpoints
    ) throws Exception {
        endpoints.authenticationManager(authenticationManager);
        endpoints.tokenStore(tokenStore());
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }
    
}

以上完成了一個簡單的受權服務配置,咱們的配置重點放在了client的配置上,因爲咱們爲配置資源擁有者(用戶),咱們沒法使用password受權,應爲用戶信不存在,咱們不管輸入用戶名和密碼都不正確。所以我暫且只能使用,client_credentials,這個僅僅是client受權,不牽扯到用戶信息。使用下面命令

curl client:secret@localhost:8080/oauth/token -d grant_type=client_credentials

一切順利的話咱們能夠獲得token相關信息。若是要使用其餘好比password等用戶受權,那麼須要在受權服務器配置用戶相關信息,並把相關的userDetailService配置到AuthorizationServerEndpointsConfigurer對象上,上面有提到endponint配置相關規則。

資源服務配置

資源服務的做用前面已經介紹過了,資源服務主要關注兩個事情,對token進行鑑權,根據具體權限包含資源。因爲token多是其餘服務器生成,所以要作到ResourceServerTokenServices與受權服務的tokenService相匹配。下面是簡單的配置.

@EnableResourceServer
   public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

       // 注意這個TokenStore須要和以前配置的一致
       private final TokenStore tokenStore;


       public ResourceServerConfiguration(TokenStore tokenStore) {
           this.tokenStore = tokenStore;
       }

       // 資源服務器安全規則配置
       @Override
       public void configure(HttpSecurity http) throws Exception {
           http
                   .exceptionHandling()
                   .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
                   .and()
                   .csrf()
                   .disable()
                   .headers()
                   .frameOptions()
                   .disable()
                   .and()
                   .sessionManagement()
                   .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                   .and()
                   .authorizeRequests()
                   .antMatchers("/api/register").permitAll()
                   .antMatchers("/api/activate").permitAll()
                   .antMatchers("/api/authenticate").permitAll()
                   .antMatchers("/api/account/reset-password/init").permitAll()
                   .antMatchers("/api/account/reset-password/finish").permitAll()
                   .antMatchers("/api/**").authenticated()
                   // 配置scope權限訪問
//                    .antMatchers("/api/**").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))");
       }

       @Override
       public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
           resources.resourceId("jhipster-uaa")
                   .tokenStore(tokenStore);
       }
   }

上面是資源服務和受權服務在一臺機器上時的配置。因爲共享tokenStore,所以資源服務在驗證token的時候不會出任何問題。

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends ResourceServerConfigurerAdapter {
   private final OAuth2Properties oAuth2Properties;

   public SecurityConfiguration(OAuth2Properties oAuth2Properties) {
       this.oAuth2Properties = oAuth2Properties;
   }

   // 主要配置資源的保護規則
   @Override
   public void configure(HttpSecurity http) throws Exception {
       http
           .csrf()
           .disable()
           .headers()
           .frameOptions()
           .disable()
       .and()
           .sessionManagement()
           .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
       .and()
           .authorizeRequests()
           .antMatchers("/api/**").authenticated() 
           .antMatchers("/management/health").permitAll()
           .antMatchers("/management/info").permitAll()
           .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN);
   }

   // 配置tokenstore,資源服務器在進行token驗證的時候須要,
   @Bean
   public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
       return new JwtTokenStore(jwtAccessTokenConverter);
   }

   @Bean
   public JwtAccessTokenConverter jwtAccessTokenConverter(OAuth2SignatureVerifierClient signatureVerifierClient) {
       return new OAuth2JwtAccessTokenConverter(oAuth2Properties, signatureVerifierClient);
   }

}

上面是採用jwt,資源/受權服務分離的配置,以上配置沒法和上面的受權服務配置配合使用。若要配置使用,須要修改受權服務的TokenStore爲JwtTokenStore,而且在OAuth2JwtAccessTokenConverter中需訪問受權服務器/token/access_key獲取RSA公鑰,該公鑰會在token驗證時用到。

相關文章
相關標籤/搜索