spring security實戰 3-使用受權碼模式( Authorization Code grant)保護資源

寫在開篇:

在一邊翻譯,一邊學習的過程當中,感受仍是將原文改造爲更爲精小的課程會更爲合適,緣由在於:html

  1. 這本書原本就是實踐課程,本書的目的也是但願你們經過應用,來加深對Spring Security的理解。
  2. 原文講述的比較細緻,甚至過於細緻,例如maven的按照過程,這樣的,去除這部分,能更加突出重點。

在改造課程的過程當中,我會作到:java

  1. 翻譯尊重原文,適當意譯。
  2. 原文提到的知識點我會所有保留
  3. 全部用例我會作一遍,並對一些部分進行適當補充,或改動。

再次聲明,本改編課程源於《OAuth 2.0 Cookbook_Protect Your Web Applications using Spring Security-Packt Publishing(2017)》git

課程從第二章開始,在Chaptor2,咱們將學習如下內容:github

  1. 使用受權碼模式(Authorization Code grant)保護資源web

  2. 支持隱式受權模式(Implicit grant)
  3. 使用密碼模式(Resource Owner Password Credentials grant type )
  4. 配置客戶端證書受權模式(Client Credentials grant)
  5. 支持refresh tokens
  6. 使用一個關係數據庫來保存tokens和客戶信息
  7. 使用redis保存token
  8. 實現客戶端註冊過程
  9. 中途破壞Oauth 2.0 Provider
  10. 使用Gatling,經過共享數據庫測試token的驗證過程

介紹

咱們有一些場景,要求應用在和大量服務進行交互的同時,還要做爲分佈式系統在網絡中提供API。在這樣的場景下,用戶若是要使用咱們的應用程序受權給第三方應用程序,OAuth2.0是一個好的選擇。redis

在這一章,你將學習如何使用OAuth2.0規範所描述的全部授予類型,來建立、配置和分發OAuth2.0 Provider,以覆蓋不一樣場景,同時還將學到如何經過關係數據庫和Redis來使用不一樣的訪問令牌管理策略。本章中使用的全部用例都將經過Spring Ontecurity OAuth2.0來實現,在編寫這本書的時候,它的版本是2.2.0.RELEASE(在http://projects.spring.io/spr... 中能夠查看Spring Ontecurity OAuth2的官方文檔)。學習如何配置本身的OAuth2.0 Provider 很重要,由於在當今的應用程序中集成了大量的應用程序。此外,經過閱讀本章,您將可以經過使用Spring Security OAuth2.0,實踐全部OAuth2細節。spring

記住,請在在生產中使用TLS/SSL來始終保護客戶端和OAuth2.0 Provider之間的全部傳輸數據,而且貫穿本書的整個實踐過程。數據庫

Getting ready(配置環境)

安裝 java8,maven ,postman/curl json

本例使用Spring Security OAuth2 Framework 來實現,此例將不使用任何數據庫,咱們將用戶信息,受權口令都保存在內存中。本例源碼能夠從https://github.com/PacktPubli... 下載segmentfault

How to do it

如下步驟將引導你使用Spring Security OAuth2.0 ,來成功配置一個驗證服務器和一個資源服務器。

1.使用Spring Initializr (http://start.spring.io)新建一個項目auth-code-server ,增長web,security依賴

2.打開pom.xml,添加以下依賴:

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

注:本例使用的springboot版本爲1.5.4.RELEASE,請格外注意版本。
3.打開application.properties ,添加以下內容類來配置auth-code-server 的用戶

security.user.name=adolfo
security.user.password=123

4.因爲咱們想保護用戶的資源,因此,如今咱們來建立一些待保護的資源,本例中是用戶的name和email:

//UserProfile.java
public class UserProfile {
  private String name;
  private String email;
  //getters and setters hidden for brevity
}

5.新建文件UserController.java,提供endpoint獲取用戶信息,該endpoint被OAuth2.0保護(在第7步設置)。注意其中的User來自org.springframework.security.core.userdetails

//UserController.java
@Controller
public class UserController {
  @RequestMapping("/api/profile")
  public ResponseEntity<UserProfile> profile() {
    User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String email = user.getUsername() + "@mailinator.com";
    UserProfile profile = new UserProfile();
    profile.setName(user.getUsername());
    profile.setEmail(email);
    return ResponseEntity.ok(profile);
  }
}

6.如今,已經存在須要被保護的endpoint,那麼,咱們須要建立OAuth2.0 受權服務,來對訪問這個endpoint的請求進行受權

//OAuth2AuthorizationServer.java
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
           .withClient("clientapp")
           .secret("123456")
           .redirectUris("http://localhost:9000/callback")
           .authorizedGrantTypes("authorization_code")
           .scopes("read_profile", "read_contacts");
  }
}

7.作到第六步,這個應用已經能夠經過分發access tokens,給用戶受權許可。可是若是想獲取用戶資源(本例中是Resource Owner profile ),還須要建立資源服務器,用於設置須要被保護的endpoint,放過驗證的endpoint等等。下面咱們來新建OAuth2ResourceServer類.

//OAuth2ResourceServer.java
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter{
  @Override
  public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().authenticated().and()
                .requestMatchers().antMatchers("/api/**");
}
}

至此,咱們已經成功搭建了一個資源管理器和一個受權中心。並經過配置,保護匹配/api/**的請求。

How it works…

若是程序運行正確,咱們能從http://localhost:8080/api/profile 拿到用戶信息。正常狀況下,咱們拿到access token,往這個endpoint發請求,能夠拿到這樣的json字符串:

{
   "name": "adolfo",
   "email": "adolfo@mailinator.com"
 }

固然一開始,咱們是沒有access token的,因此,不管怎樣訪問,都會提示咱們沒有驗證。那麼,咱們要如何獲取access token呢。這個token是用戶批准後得到,在本例中,咱們在資源服務器加上 @EnableResourceServer的標籤的同時,意味着,咱們將OAuth2AuthenticationProcessingFilter配置進了Spring SecurityFilterChain(過濾鏈). 這個filter將會用來驗證任何匹配/api/**`的請求中,是否攜帶access token,以及這個token是不是受權中心派發出去的,是否過時等等.

除了資源服務器的配置以外,咱們也對受權服務器作了相關配置。可能你發現受權服務器中的代碼量不多。使用Spring Security OAuth2.0 框架來配置受權服務器的確很簡單,可是在簡單的表相背後,隱藏了大量的配置。
經過添加@EnableAuthorizationServer這個註解,咱們實際上導入了一些重要的配置文件(類):

  • AuthorizationServerEndpointsConfiguration
    AuthorizationServerSecurityConfiguration
    AuthorizationServerEndpointsConfiguration

AuthorizationServerEndpointsConfiguration類對OAuth2.0受權中心有很重要的做用,它定義了這些bean:

  • AuthorizationEndpoint
    TokenEndpoint
    CheckTokenEndpoint

這些bean聲明瞭OAuth2.0的相關endpoint,這些endpoint用於引導驗證流程,獲取access token和刷新令牌請求。若是你想深刻了解每一個endpoint是如何工做的,我建議你讀一下Spring Security OAuth2 的源碼(github能夠獲取)。經過運行這個例子,咱們可以與一些endpoint交互。如今,咱們來運行這個例子:
1.啓動項目,經過瀏覽器往服務器發送一下請求:

http://localhost:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://localhost:9000/callback&response_type=code&scope=read_profile

由於咱們使用的是受權碼模式,咱們須要將Resource owner重定向到驗證頁面,這一過程是在「/oauth/authorize」的endpoint聲明的。注意到,咱們發送了client_id, redirect_uriresponse_type參數。
若是你想更深刻地瞭解每一個參數的意思,能夠查看https://tools.ietf.org/html/r...。注意,咱們沒有發送state參數,用以免CSRF攻擊(咱們將會在第8章瞭解更多詳情)。

2.輸入咱們在application.properties裏配置的用戶名和密碼,
圖片描述

點擊login以後,你將被重定向到用戶贊成頁,其中資源全部者能夠選擇授予第三方應用程序(客戶端)的權限的範圍。如下頁面由Spring security OAuth2.0自動生成,固然也能夠輕鬆被替換爲自定義的頁面。
圖片描述

3.點擊受權後,用戶才能重定向迴應用程序,這裏的重定向地址由redirect_uri 參數決定,若是第一步發送的請求中帶有redirect_uri,則會與咱們在OAuth2AuthorizationServer類中定義的redirectUris進行匹配驗證,若是沒有,則直接使用咱們在OAuth2AuthorizationServer中定義的redirectUris

受權以後,重定向請求將帶有一個受權碼,該受權代碼描述用戶對指定資源服務器上的指定客戶端授予許可的權限。這個受權碼看上去是這樣的:

http://localhost:9000/callback?code=5sPk8A

4.這個受權碼是受權中心隨機生成的,客戶端用它向受權中心請求access token,可使用curl工具:

curl -X POST --user clientapp:123456 http://localhost:8080/oauth/token -H "content-type: application/x-www-form-urlencoded" -d "code=5sPk8A&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fcallback&scope=read_profile"

或者使用postman:
圖片描述

圖片描述

以後,咱們能收到這樣的回覆:

{
  "access_token": "43c6f525-041f-43c8-b970-  82ba435d3c2c",
  "token_type": "bearer",
  "expires_in": 43199,
  "scope": "read_profile"
}

這樣,咱們就拿到了access token.

5.咱們如今再用攜帶access token的url去資源全部者請求資源,一樣的,咱們可使用curl:

curl -X GET http://localhost:8080/api/profile -H "authorization: Bearer 43c6f525-041f-43c8-b970-82ba435d3c2c"

或者postman:
圖片描述

這樣,咱們就拿到想要的資源了。

There's more...

這個例子儘量編寫的簡單,可是當咱們正式配置一個OAuth 2.0 Provider的時候,應該考慮使用數據庫來替換內存來保存客戶端的信息。一樣的,咱們再次建議使用TLS/SSL來保護客戶端與OAuth 2.0 Provider之間的交互。

相關文章
相關標籤/搜索