java構建OAuth2授權服務器

構建 OAuth2 授權服務器

從表現形式上看,OAuth2 授權服務器也是一個獨立的微服務,因此構建授權服務器的方法也是創建一個 SpringBoot 應用程序,我們需要引入對應的 Maven 依賴,如下所示:

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

這裡的 spring-security-oauth2 就是來自 Spring Security 中的 OAuth2 庫。現在 Maven 依賴已經添加完畢,下一步就是構建 Bootstrap 類作為訪問的入口:

@SpringBootApplication
@EnableAuthorizationServer
public class AuthServer {
    public static void main(String[] args) {
        SpringApplication.run(AuthServer.class, args);
    }
}

請註意,這裡出現瞭一個新的註解 @EnableAuthorizationServer,這個註解的作用在於為微服務運行環境提供一個基於 OAuth2 協議的授權服務,該授權服務會暴露一系列基於 RESTful 風格的端點(例如 /oauth/authorize 和 /oauth/token)供 OAuth2 授權流程使用。

構建 OAuth2 授權服務隻是集成 OAuth2 協議的第一步,授權服務器是一種集中式系統,管理著所有與安全性流程相關的客戶端和用戶信息。因此,接下來我們需要在授權服務器中對這些基礎信息進行初始化,而 Spring Security 為我們提供瞭各種配置類來實現這一目標。

設置客戶端和用戶認證信息

OAuth2.0 有幾個授權模式:授權碼模式、簡化模式、密碼模式、客戶端憑證模式。其中,密碼模式以其簡單性得到瞭廣泛的應用。在接下來的內容中,我們就以密碼模式為例展開講解。

在密碼模式下,用戶向客戶端提供用戶名和密碼,並將用戶名和密碼發給授權服務器從而請求 Token。授權服務器首先會對密碼憑證信息進行認證,確認無誤後,向客戶端發放 Token。整個流程如下圖所示:

請註意,授權服務器在這裡執行認證操作的目的是驗證傳入的用戶名和密碼是否正確。在密碼模式下,這一步是必需的,如果采用其他授權模式,不一定會有用戶認證這一環節。

確定采用密碼模式後,我們來看為瞭實現這一授權模式,需要對授權服務器做哪些開發工作。首先我們需要設置一些基礎數據,包括客戶端信息和用戶信息。

設置客戶端信息

我們先來看如何設置客戶端信息。設置客戶端時,用到的配置類是 ClientDetailsServiceConfigurer,該配置類用來配置客戶端詳情服務 ClientDetailsService。用於描述客戶端詳情的 ClientDetails 接口則包含瞭與安全性控制相關的多個重要方法,該接口中的部分方法定義如下:

public interface ClientDetails extends Serializable {
     //客戶端唯一性 Id
     String getClientId();
     //客戶端安全碼
     String getClientSecret();
     //客戶端的訪問范圍
     Set<String> getScope();
     //客戶端可以使用的授權模式

     Set<String> getAuthorizedGrantTypes();
     …
}

首先 ClientId 是一個必備屬性,用來唯一標識客戶的 Id,而 ClientSecret 代表客戶端安全碼。這裡的 Scope 用來限制客戶端的訪問范圍,如果這個屬性為空,客戶端就擁有全部的訪問范圍。常見的設置方式可以是 webclient 或 mobileclient,分別代表 Web 端和移動端。

最後,authorizedGrantTypes 代表客戶端可以使用的授權模式,可選的范圍包括代表授權碼模式的 authorization_code、代表隱式授權模式 implicit、代表密碼模式的 password 以及代表客戶端憑據模式的 client_credentials。這個屬性在設置上也可以添加 refresh_token,通過刷新操作獲取以上授權模式下產生的新 Token。

和實現認證過程類似,Spring Security 也提供瞭 AuthorizationServerConfigurerAdapter 這個配置適配器類來簡化配置類的使用方式。我們可以通過繼承該類並覆寫其中的 configure(ClientDetailsServiceConfigurer clients) 方法進行配置。使用 AuthorizationServerConfigurerAdapter 進行客戶端信息配置的基本代碼結構如下:

@Configuration

public class SpringAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
        .withClient("spring")
        .secret("{noop}spring_secret")
        .authorizedGrantTypes("refresh_token", "password", "client_credentials")
        .scopes("webclient", "mobileclient");
    }
}

可以看到,我們創建瞭一個 SpringAuthorizationServerConfigurer 類來繼承 AuthorizationServerConfigurerAdapter,並通過 ClientDetailsServiceConfigurer 配置類設置瞭授權模式為密碼模式。在授權服務器中存儲客戶端信息有兩種方式,一種就是如上述代碼所示的基於內存級別的存儲,另一種則是通過 JDBC 在數據庫中存儲詳情信息。為瞭簡單起見,這裡使用瞭內存級別的存儲方式。

同時我們註意到,在設置客戶端安全碼時使用瞭”{noop}spring_secret”這種格式。這是因為在 Spring Security 5 中統一使用 PasswordEncoder 對密碼進行編碼,在設置密碼時要求格式為“{id}password”。而這裡的前綴“{noop}”就是代表具體 PasswordEncoder 的 id,表示我們使用的是 NoOpPasswordEncoder。

@EnableAuthorizationServer 註解會暴露一系列的端點,而授權過程是使用 AuthorizationEndpoint 這個端點進行控制的。要想對該端點的行為進行配置,你可以使用 AuthorizationServerEndpointsConfigurer 這個配置類。和 ClientDetailsServiceConfigurer 配置類一樣,我們也通過使用 AuthorizationServerConfigurerAdapter 配置適配器類進行配置。

因為我們指定瞭授權模式為密碼模式,而密碼模式包含認證環節。所以針對 AuthorizationServerEndpointsConfigurer 配置類需要指定一個認證管理器 AuthenticationManager,用於對用戶名和密碼進行認證。同樣因為我們指定瞭基於密碼的授權模式,所以需要指定一個自定義的 UserDetailsService 來替換全局的實現,可以通過如下代碼來配置 AuthorizationServerEndpointsConfigurer:

@Configuration

public class SpringAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {

 @Autowired
 private AuthenticationManager authenticationManager;

 @Autowired
 private UserDetailsService userDetailsService;

 @Override

 public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {   endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
 } 
}

至此,客戶端設置工作全部完成,我們所做的事情就是實現瞭一個自定義的 SpringAuthorizationServerConfigurer 配置類並覆寫瞭對應的配置方法。

設置用戶認證信息

設置用戶認證信息所依賴的配置類是 WebSecurityConfigurer 類,Spring Security 同樣提供瞭 WebSecurityConfigurerAdapter 類來簡化該配置類的使用方式,我們可以繼承 WebSecurityConfigurerAdapter 類並且覆寫其中的 configure() 的方法來完成配置工作。

關於 WebSecurityConfigurer 配置類,我們首先需要明確配置的內容。實際上,設置用戶信息非常簡單,隻需要指定用戶名(User)、密碼(Password)和角色(Role)這三項數據即可,如下所示:

@Configuration

public class SpringWebSecurityConfigurer extends WebSecurityConfigurerAdapter {

 @Override
 @Bean
 public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
 }

 @Override
 @Bean
 public UserDetailsService userDetailsServiceBean() throws Exception {
            return super.userDetailsServiceBean();
 }

 @Override
 protected void configure(AuthenticationManagerBuilder builder) throws Exception {
 builder
            .inMemoryAuthentication()
            .withUser("user1")
            .password("{noop}password1")
            .roles("USER")
            .and()
            .withUser("admin")
            .password("{noop}password2")
            .roles("USER", "ADMIN");
 }
}

結合上面的代碼,我們看到構建瞭具有不同角色和密碼的兩個用戶,請註意”user1″代表的角色是一個普通用戶,”admin”則具有管理員角色。我們在設置密碼時,同樣需要添加前綴“{noop}”。同時,我們還看到 authenticationManagerBean()和 userDetailsServiceBean() 方法分別返回瞭父類的默認實現,而這裡返回的 UserDetailsService 和 AuthenticationManager 在前面設置客戶端時會用到。

生成 Token

現在,OAuth2 授權服務器已經構建完畢,啟動這個授權服務器,我們就可以獲取 Token。我們在構建 OAuth2 服務器時已經提到授權服務器中會暴露一批端點供 HTTP 請求進行訪問,而獲取 Token 的端點就是http://localhost:2000/oauth/token。在使用該端點時,我們需要提供前面配置的客戶端信息和用戶信息。

這裡使用 Postman 來模擬 HTTP 請求,客戶端信息設置方式如下圖所示:

我們在“Authorization”請求頭中指定認證類型為“Basic Auth”,然後設置客戶端名稱和客戶端安全碼分別為“spring”和“spring_secret”。

接下來我們指定針對授權模式的專用配置信息。首先是用於指定授權模式的 grant_type 屬性,以及用於指定客戶端訪問范圍的 scope 屬性,這裡分別設置為 “password”和“webclient”。既然設置瞭密碼模式,所以也需要指定用戶名和密碼用於識別用戶身份,這裡,我們以“spring_user”這個用戶為例進行設置,如下圖所示:

在 Postman 中執行這個請求,會得到如下所示的返回結果:

{
    "access_token":"d2066f68-665b-4038-9dbe-5dd1035e75a0",
    "token_type":"bearer",
    "refresh_token":"44009836-731c-4e6a-9cc3-274ce3af8c6b",
    "expires_in":3599,
    "scope":"all"
}

可以看到,除瞭作為請求參數的 scope,這個返回結果中還包含瞭 access_token、token_type、refresh_token 和 expires_in 等屬性。這些屬性都很重要。當然,因為每次請求生成的 Token 都是唯一的,所以你在嘗試時獲取的結果可能與我的不同。

到此這篇關於java構建OAuth2授權服務器的文章就介紹到這瞭,更多相關java構建OAuth2授權服務器內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: