一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

可能是第二好的 Spring OAuth 2.0 文章,艿艿端午在家寫了 3 天~

 壞尐孒95qanplv 2020-07-04

管她前浪,還是后浪?

能浪的浪,才是好浪!

每天 8:55 更新文章,每天掉億點(diǎn)點(diǎn)頭發(fā)...

源碼精品專欄

 

摘要: 原創(chuàng)出處 http://www./Spring-Security/OAuth2-learning/ 「芋道源碼」歡迎轉(zhuǎn)載,保留摘要,謝謝!

  • 1. 概述
  • 2. 密碼模式
  • 3. 授權(quán)碼模式
  • 4. 簡化模式
  • 5. 客戶端模式
  • 6. 合并服務(wù)器
  • 7. 刷新令牌
  • 8. 刪除令牌
  • 666. 彩蛋

本文在提供完整代碼示例,可見 https://github.com/YunaiV/SpringBoot-Labs 的 lab-68-spring-security-oauth 目錄。

原創(chuàng)不易,給點(diǎn)個 Star 嘿,一起沖鴨!

1. 概述

在《芋道 Spring Boot 安全框架 Spring Security 入門》文章中,艿艿分享了如何使用 Spring Security 實(shí)現(xiàn)認(rèn)證與授權(quán)的功能,獲得廣大女粉絲的好評。

于是乎,艿艿準(zhǔn)備再來分享一波 Spring Security OAuth 框架,看看在 Spring Security 如何實(shí)現(xiàn) OAuth2.0 實(shí)現(xiàn)授權(quán)的功能。

旁白君:實(shí)際上艿艿很早寫了一篇關(guān)于 Spring Security OAuth 的文章,考慮到版本太老,提供的示例又過于簡單,所以本文也是該文章的升級版。

可能有胖友對 OAuth2.0 不是很了解,所以我們先來簡單介紹下它??赡芘钟芽?OAuth2.0 的概念會有點(diǎn)懵逼,不要擔(dān)心,后續(xù)看完艿艿提供的示例代碼,會突然清晰的哈。

另外,阮一峰提供了幾篇關(guān)于 OAuth2.0 非常不錯的文章,推薦胖友去從瞅瞅。同時,本文也會直接引用它的內(nèi)容,方便胖友統(tǒng)一理解。

  • 《理解 OAuth2.0》
  • 《OAuth2.0 的一個簡單解釋》
  • 《OAuth2.0 的四種方式》
  • 《GitHub OAuth 第三方登錄示例教程》

1.1 OAuth2.0 是什么?

FROM 《維基百科 —— 開放授權(quán)》

OAuth(Open Authorization)是一個開放標(biāo)準(zhǔn),允許用戶讓第三方應(yīng)用訪問該用戶在某一網(wǎng)站上存儲的私密的資源(如照片,視頻,聯(lián)系人列表),而無需將用戶名和密碼提供給第三方應(yīng)用。

旁白君:很多團(tuán)隊(duì),內(nèi)部會采用 OAuth2.0 實(shí)現(xiàn)一個授權(quán)服務(wù),避免每個上層應(yīng)用或者服務(wù)重復(fù)開發(fā)。

OAuth 允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務(wù)提供者的數(shù)據(jù)。

每一個令牌授權(quán)一個特定的網(wǎng)站(例如,視頻編輯網(wǎng)站)在特定的時段(例如,接下來的 2 小時內(nèi))內(nèi)訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth 讓用戶可以授權(quán)第三方網(wǎng)站訪問他們存儲在另外服務(wù)提供者的某些特定信息,而非所有內(nèi)容。

旁白君:如果胖友對接過微信網(wǎng)頁授權(quán)功能,就會發(fā)現(xiàn)分成兩種方式:靜默授權(quán)、手動授權(quán)。前者只能獲取到用戶的 openid,而后者可以獲取到用戶的基本信息。

OAuth2.0 是用于授權(quán)的行業(yè)標(biāo)準(zhǔn)協(xié)議。OAuth2.0 為簡化客戶端開發(fā)提供了特定的授權(quán)流,包括 Web 應(yīng)用、桌面應(yīng)用、移動端應(yīng)用等。

旁白君:OAuth 1.0 協(xié)議體系本身存在一些問題,現(xiàn)已被各大開發(fā)平臺逐漸廢棄。

1.2 OAuth2.0 角色解釋

在 OAuth2.0 中,有如下角色:

Authorization Server:認(rèn)證服務(wù)器,用于認(rèn)證用戶。如果客戶端認(rèn)證通過,則發(fā)放訪問資源服務(wù)器的令牌。

Resource Server:資源服務(wù)器,擁有受保護(hù)資源。如果請求包含正確的訪問令牌,則可以訪問資源。

友情提示:提供管理后臺、客戶端 API 的服務(wù),都可以認(rèn)為是 Resource Server。

Client:客戶端。它請求資源服務(wù)器時,會帶上訪問令牌,從而成功訪問資源。

友情提示:Client 可以是瀏覽器、客戶端,也可以是內(nèi)部服務(wù)。

④ Resource Owner:資源擁有者。最終用戶,他有訪問資源的賬號密碼

友情提示:可以簡單把 Resource Owner 理解成人,她在使用 Client 訪問資源。

1.3 OAuth 2.0 運(yùn)行流程

如下是 OAuth 2.0 的授權(quán)碼模式的運(yùn)行流程:

OAuth 2.0 運(yùn)行流程
  • (A)用戶打開客戶端以后,客戶端要求用戶給予授權(quán)。
  • (B)用戶同意給予客戶端授權(quán)。
  • (C)客戶端使用上一步獲得的授權(quán),向認(rèn)證服務(wù)器申請令牌。
  • (D)認(rèn)證服務(wù)器對客戶端進(jìn)行認(rèn)證以后,確認(rèn)無誤,同意發(fā)放令牌。
  • (E)客戶端使用令牌,向資源服務(wù)器申請獲取資源。
  • (F)資源服務(wù)器確認(rèn)令牌無誤,同意向客戶端開放資源。

上述的六個步驟,B 是關(guān)鍵,即用戶如何給客戶端進(jìn)行授權(quán)。有了授權(quán)之,客戶端就可以獲取令牌,進(jìn)而憑令牌獲取資源。

友情提示:如果胖友有對接過三方開放平臺,例如說微信、QQ、微博等三方登錄,就會很容易理解這個步驟過程。

這個時候的資源,資源主要指的是三方開放平臺的用戶資料等等。

1.4 OAuth 2.0 授權(quán)模式

客戶端必須得到用戶的授權(quán)(Authorization Grant),才能獲得訪問令牌(Access Token)。

OAuth2.0 定義了四種授權(quán)方式:

  • 授權(quán)碼模式(Authorization Code)
  • 密碼模式(Resource Owner Password Credentials)
  • 簡化模式(Implicit)
  • 客戶端模式(Client Credentials)

其中,密碼模式授權(quán)碼模式比較常用。至于如何選擇,艿艿這里先提前劇透下,后續(xù)慢慢細(xì)品。

FROM 《深度剖析 OAuth2 和微服務(wù)安全架構(gòu)》

授權(quán)類型選擇

當(dāng)然,對于黃框部分,對于筆者還是比較困惑的。筆者認(rèn)為,第三方的單頁應(yīng)用 SPA ,也是適合采用 Authorization Code Grant 授權(quán)模式的。例如,《微信網(wǎng)頁授權(quán)》 :

具體而言,網(wǎng)頁授權(quán)流程分為四步:

  • 1、引導(dǎo)用戶進(jìn)入授權(quán)頁面同意授權(quán),獲取 code
  • 2、通過 code 換取網(wǎng)頁授權(quán) access_token(與基礎(chǔ)支持中的 access_toke n不同)
  • 3、如果需要,開發(fā)者可以刷新網(wǎng)頁授權(quán) access_token,避免過期
  • 4、通過網(wǎng)頁授權(quán) access_token 和 openid 獲取用戶基本信息(支持 UnionID 機(jī)制)

所以,艿艿猜測,之所以圖中畫的是 Implicit Grant 的原因是,受 Google 的 《OAuth 2.0 for Client-side Web Applications》 一文中,推薦使用了 Implicit Grant 。

當(dāng)然,具體使用 Implicit Grant 還是 Authorization Code Grant 授權(quán)模式,沒有定論。筆者,偏向于使用 Authorization Code Grant,對于第三方客戶端的場景。

2. 密碼模式

示例代碼對應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-resource-owner-password-credentials
  • 資源服務(wù)器:lab-68-demo02-resource-server

本小節(jié),我們來學(xué)習(xí)密碼模式(Resource Owner Password Credentials Grant)。

密碼模式,用戶向客戶端提供自己的用戶名和密碼。客戶端使用這些信息,向授權(quán)服務(wù)器索要授權(quán)。

在這種模式中,用戶必須把自己的密碼給客戶端,但是客戶端不得儲存密碼。這通常用在用戶對客戶端高度信任的情況下,比如客戶端是操作系統(tǒng)的一部分,或者由一個著名公司出品。而授權(quán)服務(wù)器只有在其他授權(quán)模式無法執(zhí)行的情況下,才能考慮使用這種模式。

旁白君:如果客戶端和授權(quán)服務(wù)器都是自己公司的,顯然符合。

密碼模式
  • (A)用戶向客戶端提供用戶名和密碼。
  • (B)客戶端將用戶名和密碼發(fā)給授權(quán)服務(wù)器,向后者請求令牌。
  • (C)授權(quán)服務(wù)器確認(rèn)無誤后,向客戶端提供訪問令牌。

下面,我們來新建兩個項(xiàng)目,搭建一個密碼模式的使用示例。如下圖所示:

項(xiàng)目結(jié)構(gòu)
  • lab-68-demo02-authorization-server-with-resource-owner-password-credentials:授權(quán)服務(wù)器。
  • lab-68-demo02-resource-server:資源服務(wù)器。

2.1 搭建授權(quán)服務(wù)器

創(chuàng)建 lab-68-demo02-authorization-server-with-resource-owner-password-credentials 項(xiàng)目,搭建授權(quán)服務(wù)器。

2.1.1 引入依賴

創(chuàng)建 pom.xml 文件,引入 Spring Security OAuth 依賴。

<?xml version='1.0' encoding='UTF-8'?>
<project xmlns='http://maven./POM/4.0.0'
         xmlns:xsi='http://www./2001/XMLSchema-instance'
         xsi:schemaLocation='http://maven./POM/4.0.0 http://maven./xsd/maven-4.0.0.xsd'>

    <parent>
        <artifactId>lab-68</artifactId>
        <groupId>cn.iocoder.springboot.labs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lab-68-demo02-authorization-server-with-resource-owner-password-credentials</artifactId>

    <properties>
        <!-- 依賴相關(guān)配置 -->
        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>
        <!-- 插件相關(guān)配置 -->
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- 實(shí)現(xiàn)對 Spring MVC 的自動配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 實(shí)現(xiàn)對 Spring Security OAuth2 的自動配置 -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
    </dependencies>

</project>

添加 spring-security-oauth2-autoconfigure 依賴,引入 Spring Security OAuth 并實(shí)現(xiàn)自動配置。同時,它也引入了 Spring Security 依賴。如下圖所示:

spring-security-oauth2-autoconfigure

2.1.2 SecurityConfig

創(chuàng)建 SecurityConfig 配置類,提供一個賬號密碼為「yunai/1024」的用戶。代碼如下:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.
                // 使用內(nèi)存中的 InMemoryUserDetailsManager
                inMemoryAuthentication()
                // 不使用 PasswordEncoder 密碼編碼器
                .passwordEncoder(passwordEncoder())
                // 配置 yunai 用戶
                .withUser('yunai').password('1024').roles('USER');
    }

}

我們通過 Spring Security 提供認(rèn)證功能,所以這里需要配置一個用戶。

友情提示:看不懂這個配置的胖友,后續(xù)可回《芋道 Spring Boot 安全框架 Spring Security 入門》重造下。

2.1.3 OAuth2AuthorizationServerConfig

創(chuàng)建 OAuth2AuthorizationServerConfig 配置類,進(jìn)行授權(quán)服務(wù)器。代碼如下:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    /**
     * 用戶認(rèn)證 Manager
     */

    @Autowired
    private AuthenticationManager authenticationManager;

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

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.checkTokenAccess('isAuthenticated()');
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() // <4.1>
                .withClient('clientapp').secret('112233'// <4.2> Client 賬號、密碼。
                .authorizedGrantTypes('password'// <4.2> 密碼模式
                .scopes('read_userinfo''read_contacts'// <4.2> 可授權(quán)的 Scope
//                .and().withClient() // <4.3> 可以繼續(xù)配置新的 Client
                ;
    }

}

① 在類上添加 @EnableAuthorizationServer 注解,聲明開啟 OAuth 授權(quán)服務(wù)器的功能。

同時,繼承 AuthorizationServerConfigurerAdapter 類,進(jìn)行 OAuth 授權(quán)服務(wù)器的配置。

#configure(AuthorizationServerEndpointsConfigurer endpoints) 方法,配置使用的 AuthenticationManager 實(shí)現(xiàn)用戶認(rèn)證的功能。其中,authenticationManager 是由「2.1.2 SecurityConfig」創(chuàng)建,Spring Security 的配置類。

#configure(AuthorizationServerSecurityConfigurer oauthServer) 方法,設(shè)置 /oauth/check_token 端點(diǎn),通過認(rèn)證后可訪問。

友情提示:這里的認(rèn)證,指的是使用 client-id + client-secret 進(jìn)行的客戶端認(rèn)證,不要和用戶認(rèn)證混淆。

其中,/oauth/check_token 端點(diǎn)對應(yīng) CheckTokenEndpoint 類,用于校驗(yàn)訪問令牌的有效性。

  • 在客戶端訪問資源服務(wù)器時,會在請求中帶上訪問令牌。
  • 在資源服務(wù)器收到客戶端的請求時,會使用請求中的訪問令牌,找授權(quán)服務(wù)器確認(rèn)該訪問令牌的有效性。
CheckTokenEndpoint 類

#configure(ClientDetailsServiceConfigurer clients) 方法,進(jìn)行 Client 客戶端的配置。

<4.1> 處,設(shè)置使用基于內(nèi)存的 Client 存儲器。實(shí)際情況下,最好放入數(shù)據(jù)庫中,方便管理。

ClientDetailsService 子類

<4.2> 處,創(chuàng)建一個 Client 配置。如果要繼續(xù)添加另外的 Client 配置,可以在 <4.3> 處使用 #and() 方法繼續(xù)拼接。注意,這里的 .withClient('clientapp').secret('112233') 代碼段,就是 client-idclient-secret。

補(bǔ)充知識:可能會有胖友會問,為什么要創(chuàng)建 Client 的 client-idclient-secret 呢?

通過 client-id 編號和 client-secret,授權(quán)服務(wù)器可以知道調(diào)用的來源以及正確性。這樣,即使“壞人”拿到 Access Token ,但是沒有 client-id 編號和 client-secret,也不能和授權(quán)服務(wù)器發(fā)生有效的交互。

2.1.4 AuthorizationServerApplication

創(chuàng)建 AuthorizationServerApplication 類,授權(quán)服務(wù)器的啟動類。代碼如下:

@SpringBootApplication
public class AuthorizationServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(AuthorizationServerApplication.class, args);
    }

}

2.1.5 簡單測試

執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器。下面,我們使用 Postman 模擬一個 Client

POST 請求 http://localhost:8080/oauth/token 地址,使用密碼模式進(jìn)行授權(quán)。如下圖所示:

  • client-id + client-secret 進(jìn)行 Client 認(rèn)證
  • 密碼模式的認(rèn)證

請求說明:

  • 通過 Basic Auth 的方式,填寫 client-id + client-secret 作為用戶名與密碼,實(shí)現(xiàn) Client 客戶端有效性的認(rèn)證。
  • 請求參數(shù) grant_type'password',表示使用密碼模式。
  • 請求參數(shù) usernamepassword,表示用戶的用戶名與密碼。

響應(yīng)說明:

  • 響應(yīng)字段 access_token訪問令牌,后續(xù)客戶端在訪問資源服務(wù)器時,通過它作為身份的標(biāo)識。
  • 響應(yīng)字段 token_type令牌類型,一般是 bearer 或是 mac 類型。
  • 響應(yīng)字段 expires_in 為訪問令牌的過期時間,單位為秒。
  • 響應(yīng)字段 scope權(quán)限范圍

友情提示:/oauth/token 對應(yīng) TokenEndpoint 端點(diǎn),提供 OAuth2.0 的四種授權(quán)模式。感興趣的胖友,可以后續(xù)去擼擼。

POST 請求 http://localhost:8080/oauth/check_token 地址,校驗(yàn)訪問令牌的有效性。如下圖所示:

  • client-id + client-secret 進(jìn)行 Client 認(rèn)證
  • 密碼模式的認(rèn)證

請求和響應(yīng)比較簡單,胖友自己瞅瞅即可。

2.2 搭建資源服務(wù)器

創(chuàng)建 lab-68-demo02-resource-server 項(xiàng)目,搭建資源服務(wù)器。

2.2.1 引入依賴

創(chuàng)建 pom.xml 文件,引入 Spring Security OAuth 依賴。

<?xml version='1.0' encoding='UTF-8'?>
<project xmlns='http://maven./POM/4.0.0'
         xmlns:xsi='http://www./2001/XMLSchema-instance'
         xsi:schemaLocation='http://maven./POM/4.0.0 http://maven./xsd/maven-4.0.0.xsd'>

    <parent>
        <artifactId>lab-68</artifactId>
        <groupId>cn.iocoder.springboot.labs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lab-68-demo02-resource-server</artifactId>

    <properties>
        <!-- 依賴相關(guān)配置 -->
        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>
        <!-- 插件相關(guān)配置 -->
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- 實(shí)現(xiàn)對 Spring MVC 的自動配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 實(shí)現(xiàn)對 Spring Security OAuth2 的自動配置 -->
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
    </dependencies>

</project>

友情提示:和「2.1.1 引入依賴」小節(jié),是一致的哈。

2.2.2 配置文件

創(chuàng)建 application.yml 配置文件,添加 Spring Security OAuth 相關(guān)配置。

server:
  port: 9090

security:
  oauth2:
    # OAuth2 Client 配置,對應(yīng) OAuth2ClientProperties 類
    client:
      client-id: clientapp
      client-secret: 112233
    # OAuth2 Resource 配置,對應(yīng) ResourceServerProperties 類
    resource:
      token-info-uri: http://127.0.0.1:8080/oauth/check_token # 獲得 Token 信息的 URL
    # 訪問令牌獲取 URL,自定義的
    access-token-uri: http://127.0.0.1:8080/oauth/token

security.oauth2.client 配置項(xiàng),OAuth2 Client 配置,對應(yīng) OAuth2ClientProperties 類。在這個配置項(xiàng)中,我們添加了客戶端的 client-idclient-secret。

為什么要添加這個配置項(xiàng)呢?因?yàn)橘Y源服務(wù)器會調(diào)用授權(quán)服務(wù)器的 /oauth/check_token 接口,而考慮到安全性,我們配置了該接口需要進(jìn)過客戶端認(rèn)證。

友情提示:這里艿艿偷懶了,其實(shí)單獨(dú)給資源服務(wù)器配置一個 Client 的 client-idclient-secret。我們可以把資源服務(wù)器理解成授權(quán)服務(wù)器的一個特殊的客戶端。

security.oauth2.resource 配置項(xiàng),OAuth2 Resource 配置,對應(yīng) ResourceServerProperties 類。

這里,我們通過 token-info-uri 配置項(xiàng),設(shè)置使用授權(quán)服務(wù)器的 /oauth/check_token 接口,校驗(yàn)訪問令牌的有效性。

security.access-token-uri 配置項(xiàng),是我們自定義的,設(shè)置授權(quán)服務(wù)器的 oauth/token 接口,獲取訪問令牌。因?yàn)樯院笪覀儗⒃?LoginController 中,實(shí)現(xiàn)一個 /login 登錄接口。

2.2.3 OAuth2ResourceServerConfig

創(chuàng)建 OAuth2ResourceServerConfig 類,進(jìn)行資源服務(wù)器。代碼如下:

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            // 設(shè)置 /login 無需權(quán)限訪問
            .antMatchers('/login').permitAll()
            // 設(shè)置其它請求,需要認(rèn)證后訪問
            .anyRequest().authenticated()
            ;
    }

}

① 在類上添加 @EnableResourceServer 注解,聲明開啟 OAuth 資源服務(wù)器的功能。

同時,繼承 ResourceServerConfigurerAdapter 類,進(jìn)行 OAuth 資源服務(wù)器的配置。

#configure(HttpSecurity http) 方法,設(shè)置 HTTP 權(quán)限。這里,我們設(shè)置 /login 接口無需權(quán)限訪問,其它接口認(rèn)證后可訪問。

這樣,客戶端在訪問資源服務(wù)器時,其請求中的訪問令牌會被資源服務(wù)器調(diào)用授權(quán)服務(wù)器的 /oauth/check_token 接口,進(jìn)行校驗(yàn)訪問令牌的正確性。

2.2.4 ExampleController

創(chuàng)建 ExampleController 類,提供 /api/example/hello 接口,表示一個資源。代碼如下:

@RestController
@RequestMapping('/api/example')
public class ExampleController {

    @RequestMapping('/hello')
    public String hello() {
        return 'world';
    }

}

2.2.5 ResourceServerApplication

創(chuàng)建 ResourceServerApplication 類,資源服務(wù)器的啟動類。代碼如下:

@SpringBootApplication
public class ResourceServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ResourceServerApplication.class, args);
    }

}

2.2.6 簡單測試(第一彈)

執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器。下面,我們來請求服務(wù)器的 <127.0.0.1:9090/api/example/hello> 接口,進(jìn)行相應(yīng)的測試。

① 首先,請求 <127.0.0.1:9090/api/example/hello> 接口,不帶訪問令牌,則請求會被攔截。如下圖所示:

不帶訪問令牌

② 然后,請求 <127.0.0.1:9090/api/example/hello> 接口,帶上錯誤的訪問令牌,則請求會被攔截。如下圖所示:

錯誤的訪問令牌

友情提示:訪問令牌需要在請求頭 'Authorization' 上設(shè)置,并且以 'Bearer ' 開頭。

③ 最后,請求 <127.0.0.1:9090/api/example/hello> 接口,帶上正確的訪問令牌,則請求會被通過。如下圖所示:

正確的訪問令牌

2.2.7 LoginController

創(chuàng)建 LoginController 類,提供 /login 登錄接口。代碼如下:

@RestController
@RequestMapping('/')
public class LoginController {

    @Autowired
    private OAuth2ClientProperties oauth2ClientProperties;

    @Value('${security.oauth2.access-token-uri}')
    private String accessTokenUri;

    @PostMapping('/login')
    public OAuth2AccessToken login(@RequestParam('username') String username,
                                   @RequestParam('password') String password) 
{
        // <1> 創(chuàng)建 ResourceOwnerPasswordResourceDetails 對象
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setAccessTokenUri(accessTokenUri);
        resourceDetails.setClientId(oauth2ClientProperties.getClientId());
        resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());
        resourceDetails.setUsername(username);
        resourceDetails.setPassword(password);
        // <2> 創(chuàng)建 OAuth2RestTemplate 對象
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);
        restTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider());
        // <3> 獲取訪問令牌
        return restTemplate.getAccessToken();
    }

}

/login 接口中,資源服務(wù)器扮演的是一個 OAuth 客戶端的角色,調(diào)用授權(quán)服務(wù)器的 /oauth/token 接口,使用密碼模式進(jìn)行授權(quán),獲得訪問令牌

<1> 處,創(chuàng)建 ResourceOwnerPasswordResourceDetails 對象,填寫密碼模式授權(quán)需要的請求參數(shù)。

<2> 處,創(chuàng)建 OAuth2RestTemplate 對象,它是 Spring Security OAuth 封裝的工具類,用于請求授權(quán)服務(wù)器。

同時,將 ResourceOwnerPasswordAccessTokenProvider 設(shè)置到其中,表示使用密碼模式授權(quán)。

友情提示:這一步非常重要,艿艿在這里卡了非常非常非常久,一度自閉要放棄。

<3> 處,調(diào)用 OAuth2RestTemplate 的 #getAccessToken() 方法,調(diào)用授權(quán)服務(wù)器的 /oauth/token 接口,進(jìn)行密碼模式的授權(quán)。

注意,OAuth2RestTemplate 是有狀態(tài)的工具類,所以需要每次都重新創(chuàng)建。

2.2.8 簡單測試(第二彈)

重新執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器。下面,我們來進(jìn)行 /login 接口的測試。

① 首先,請求 http://127.0.0.1:9090/login 接口,使用用戶用戶名密碼進(jìn)行登錄,獲得訪問令牌。如下圖所示:

測試 /login 接口

響應(yīng)結(jié)果和授權(quán)服務(wù)器的 /oauth/token 接口是一致的,因?yàn)榫褪钦{(diào)用它,嘿嘿~

② 然后,請求 <127.0.0.1:9090/api/example/hello> 接口,帶剛剛的訪問令牌,則請求會被通過。如下圖所示:

正確的訪問令牌

3. 授權(quán)碼模式

示例代碼對應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-resource-owner-password-credentials
  • 資源服務(wù)器:lab-68-demo02-resource-server

本小節(jié),我們來學(xué)習(xí)授權(quán)碼模式(Authorization Code)

授權(quán)碼模式,是功能最完整、流程最嚴(yán)密的授權(quán)模式。它的特點(diǎn)就是通過客戶端的后臺服務(wù)器,與授權(quán)務(wù)器進(jìn)行互動。

旁白君:一般情況下,在有客戶端的情況下,我們與第三方平臺常常采用這種方式。

授權(quán)碼模式
  • (A)用戶訪問客戶端,后者將前者跳轉(zhuǎn)到到授權(quán)服務(wù)器。
  • (B)用戶選擇是否給予客戶端授權(quán)。
  • (C)假設(shè)用戶給予授權(quán),授權(quán)服務(wù)器將跳轉(zhuǎn)到客戶端事先指定的'重定向 URI'(Redirection URI),同時附上一個授權(quán)碼
  • (D)客戶端收到授權(quán)碼,附上早先的'重定向 URI',向認(rèn)證服務(wù)器申請令牌。這一步是在客戶端的后臺的服務(wù)器上完成的,對用戶不可見。
  • (E)認(rèn)證服務(wù)器核對了授權(quán)碼重定向 URI,確認(rèn)無誤后,向客戶端發(fā)送訪問令牌。

下面,我們來新建兩個項(xiàng)目,搭建一個授權(quán)碼模式的使用示例。如下圖所示:

項(xiàng)目結(jié)構(gòu)
  • lab-68-demo02-authorization-server-with-resource-owner-password-credentials:授權(quán)服務(wù)器。
  • lab-68-demo02-resource-server:資源服務(wù)器。

3.1 搭建授權(quán)服務(wù)器

復(fù)制出 lab-68-demo02-authorization-server-with-resource-owner-password-credentials 項(xiàng)目,修改搭建授權(quán)服務(wù)器。改動點(diǎn)如下圖所示:

項(xiàng)目改動點(diǎn)

僅僅需要修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用 'authorization_code' 授權(quán)碼模式,并設(shè)置回調(diào)地址。

?? 注意,這里設(shè)置的回調(diào)地址,稍后我們會在「3.2 搭建資源服務(wù)器」中實(shí)現(xiàn)。

3.1.1 簡單測試

執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器。

① 使用瀏覽器,訪問 http://127.0.0.1:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://127.0.0.1:9090/callback&response_type=code&scope=read_userinfo 地址,獲取授權(quán)。請求參數(shù)說明如下:

  • client_id 參數(shù),必傳,為我們在 OAuth2AuthorizationServer 中配置的 Client 的編號。
  • redirect_uri 參數(shù),可選,回調(diào)地址。當(dāng)然,如果 client_id 對應(yīng)的 Client 未配置 redirectUris 屬性,會報錯。
  • response_type 參數(shù),必傳,返回結(jié)果為 code 授權(quán)碼
  • scope 參數(shù),可選,申請授權(quán)的 Scope 。如果多個,使用逗號分隔。
  • state 參數(shù),可選,表示客戶端的當(dāng)前狀態(tài),可以指定任意值,授權(quán)服務(wù)器會原封不動地返回這個值。

友情提示:state 參數(shù),未在上述 URL 中體現(xiàn)出來。

因?yàn)槲覀儾⑽?strong>登錄授權(quán)服務(wù)器,所以被攔截跳轉(zhuǎn)到登錄界面。如下圖所示:

登錄界面

② 輸入用戶的賬號密碼「yunai/1024」進(jìn)行登錄。登錄完成后,進(jìn)入授權(quán)界面。如下圖所示:

旁白君:和我們?nèi)粘J褂玫尿v訊 QQ、微信、微博等等三方登錄,是一模一樣的,除了丑了點(diǎn),嘿嘿~

授權(quán)界面

③ 選擇 scope.read_userinfo 為 Approve 允許,點(diǎn)擊「Authorize」按鈕,完成授權(quán)操作。瀏覽器自動重定向到 Redirection URI 地址,并且在 URI 上可以看到 code 授權(quán)碼。如下圖所示:

回調(diào)界面

友情提示:/oauth/authorize 對應(yīng) AuthorizationEndpoint 端點(diǎn)。

④ 因?yàn)槲覀儠簳r沒有啟動資源服務(wù)器,所以顯示無法訪問。這里,我們先使用 Postman 模擬請求 http://localhost:8080/oauth/token 地址,使用授權(quán)碼獲取到訪問令牌。如下圖所示:

  • client-id + client-secret 進(jìn)行 Client 認(rèn)證
  • 授權(quán)碼模式的認(rèn)證

請求說明:

  • 通過 Basic Auth 的方式,填寫 client-id + client-secret 作為用戶名與密碼,實(shí)現(xiàn) Client 客戶端有效性的認(rèn)證。
  • 請求參數(shù) grant_type'authorization_code',表示使用授權(quán)碼模式
  • 請求參數(shù) code,從授權(quán)服務(wù)器獲取到的授權(quán)碼。
  • 請求參數(shù) redirect_uri,Client 客戶端的 Redirection URI 地址。

注意,授權(quán)碼僅能使用一次,重復(fù)請求會報 Invalid authorization code: 錯誤。如下圖所示:

授權(quán)碼模式的認(rèn)證 - 失敗

3.2 搭建資源服務(wù)器

復(fù)用 lab-68-demo02-resource-server 項(xiàng)目,主要是提供回調(diào)地址。如下圖所示:

項(xiàng)目改動點(diǎn)

① 新建 CallbackController 類,提供 /callback 回調(diào)地址。

② 在 OAuth2ResourceServerConfig 配置類中,設(shè)置 /callback 回調(diào)地址無需權(quán)限驗(yàn)證,不然回調(diào)都跳轉(zhuǎn)不過來哈。

3.2.1 CallbackController

創(chuàng)建 CallbackController 類,提供 /callback 回調(diào)地址,在獲取到授權(quán)碼時,請求授權(quán)服務(wù)器,通過授權(quán)碼獲取訪問令牌。代碼如下:

@RestController
@RequestMapping('/')
public class CallbackController {

    @Autowired
    private OAuth2ClientProperties oauth2ClientProperties;

    @Value('${security.oauth2.access-token-uri}')
    private String accessTokenUri;

    @GetMapping('/callback')
    public OAuth2AccessToken login(@RequestParam('code') String code) {
        // 創(chuàng)建 AuthorizationCodeResourceDetails 對象
        AuthorizationCodeResourceDetails resourceDetails = new AuthorizationCodeResourceDetails();
        resourceDetails.setAccessTokenUri(accessTokenUri);
        resourceDetails.setClientId(oauth2ClientProperties.getClientId());
        resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());
        // 創(chuàng)建 OAuth2RestTemplate 對象
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);
        restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setAuthorizationCode(code); // <1> 設(shè)置 code
        restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setPreservedState('http://127.0.0.1:9090/callback'); // <2> 通過這個方式,設(shè)置 redirect_uri 參數(shù)
        restTemplate.setAccessTokenProvider(new AuthorizationCodeAccessTokenProvider());
        // 獲取訪問令牌
        return restTemplate.getAccessToken();
    }

}

代碼比較簡單,還是使用 OAuth2RestTemplate 進(jìn)行請求授權(quán)服務(wù)器,胖友自己瞅瞅哈。

需要注意的是 <1><2> 處,設(shè)置請求授權(quán)服務(wù)器需要的 coderedirect_uri 參數(shù)。

3.2.2 簡單測試

執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器。

重復(fù)「3.2.1 簡單測試」的過程,成功獲取到訪問令牌。如下圖所示:

授權(quán)碼模式的認(rèn)證 - 成功

4. 簡化模式

示例代碼對應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-implicit
  • 資源服務(wù)器:lab-68-demo02-resource-server

本小節(jié),我們來學(xué)習(xí)簡化模式(Implicit)。

簡化模式,不通過第三方應(yīng)用程序的服務(wù)器,直接在瀏覽器中向授權(quán)服務(wù)器申請令牌,跳過了“授權(quán)碼”這個步驟,因此得名。所有步驟在瀏覽器中完成,令牌對訪問者是可見的,且客戶端不需要授權(quán)。

簡化模式
  • (A)用戶訪問客戶端,后者將前者跳轉(zhuǎn)到到授權(quán)服務(wù)器。
  • (B)用戶選擇是否給予客戶端授權(quán)。
  • (C)假設(shè)用戶給予授權(quán),授權(quán)服務(wù)器將用戶導(dǎo)向客戶端指定的'重定向URI',并在 URI 的 Hash 部分包含了訪問令牌
  • (D)瀏覽器向資源服務(wù)器發(fā)出請求,其中不包括上一步收到的 Hash 值。
  • (E)資源服務(wù)器返回一個網(wǎng)頁,其中包含的代碼可以獲取 Hash 值中的令牌。
  • (F)瀏覽器執(zhí)行上一步獲得的腳本,提取出令牌。
  • (G)瀏覽器將令牌發(fā)給客戶端。
項(xiàng)目結(jié)構(gòu)
  • lab-68-demo02-authorization-server-with-implicit:授權(quán)服務(wù)器。
  • lab-68-demo02-resource-server:資源服務(wù)器。

4.1 搭建授權(quán)服務(wù)器

復(fù)制出 lab-68-demo02-authorization-server-with-implicit 項(xiàng)目,修改搭建授權(quán)服務(wù)器。改動點(diǎn)如下圖所示:

項(xiàng)目改動點(diǎn)

僅僅需要修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用 'implicit' 簡化模式,并設(shè)置回調(diào)地址。

?? 注意,這里設(shè)置的回調(diào)地址,稍后我們會在「4.2 搭建資源服務(wù)器」中實(shí)現(xiàn)。

4.2 搭建資源服務(wù)器

復(fù)用 lab-68-demo02-resource-server 項(xiàng)目,主要是提供回調(diào)地址。如下圖所示:

項(xiàng)目改動點(diǎn)

① 新建 Callback02Controller 類,提供 /callback02 回調(diào)地址。代碼如下:

@RestController
@RequestMapping('/')
public class Callback02Controller {

    @GetMapping('/callback02')
    public String login() {
        return '假裝這里有一個頁面';
    }

}

友情提示:考慮到暫時不想做頁面,所以這里先假裝一下,嘿嘿。

② 在 OAuth2ResourceServerConfig 配置類中,設(shè)置 /callback02 回調(diào)地址無需權(quán)限驗(yàn)證,不然回調(diào)都跳轉(zhuǎn)不過來哈。

4.3 簡單測試

執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器。
執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器。

① 使用瀏覽器,訪問 http://127.0.0.1:8080/oauth/authorize?client_id=clientapp&redirect_uri=http://127.0.0.1:9090/callback02&response_type=token&scope=read_userinfo 地址,獲取授權(quán)。請求參數(shù)說明如下:

  • client_id 參數(shù),必傳,為我們在 OAuth2AuthorizationServer 中配置的 Client 的編號。
  • redirect_uri 參數(shù),可選,回調(diào)地址。當(dāng)然,如果 client_id 對應(yīng)的 Client 未配置 redirectUris 屬性,會報錯。
  • response_type 參數(shù),必傳,返回結(jié)果為 token 訪問令牌。
  • scope 參數(shù),可選,申請授權(quán)的 Scope 。如果多個,使用逗號分隔。
  • state 參數(shù),可選,表示客戶端的當(dāng)前狀態(tài),可以指定任意值,授權(quán)服務(wù)器會原封不動地返回這個值。

友情提示:state 參數(shù),未在上述 URL 中體現(xiàn)出來。

因?yàn)槲覀儾⑽?strong>登錄授權(quán)服務(wù)器,所以被攔截跳轉(zhuǎn)到登錄界面。如下圖所示:

登錄界面

② 輸入用戶的賬號密碼「yunai/1024」進(jìn)行登錄。登錄完成后,進(jìn)入授權(quán)界面。如下圖所示:

旁白君:和我們?nèi)粘J褂玫尿v訊 QQ、微信、微博等等三方登錄,是一模一樣的,除了丑了點(diǎn),嘿嘿~

授權(quán)界面

③ 選擇 scope.read_userinfo 為 Approve 允許,點(diǎn)擊「Authorize」按鈕,完成授權(quán)操作。瀏覽器自動重定向到 Redirection URI 地址,并且在 URI 上的 Hash 部分可以看到 access_token 訪問令牌。如下圖所示:

回調(diào)界面

后續(xù),可以通過編寫 Javascript 腳本的代碼,獲取 URI 上的 Hash 部分的訪問令牌。

5. 客戶端模式

示例代碼對應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo02-authorization-server-with-client-credentials
  • 資源服務(wù)器:lab-68-demo02-resource-server

本小節(jié),我們來學(xué)習(xí)客戶端模式(Client Credentials)。

客戶端模式,指客戶端以自己的名義,而不是以用戶的名義,向授權(quán)服務(wù)器進(jìn)行認(rèn)證。

嚴(yán)格地說,客戶端模式并不屬于 OAuth 框架所要解決的問題。在這種模式中,用戶直接向客戶端注冊,客戶端以自己的名義要求授權(quán)服務(wù)器提供服務(wù),其實(shí)不存在授權(quán)問題。

旁白君:我們對接微信公眾號時,就采用的客戶端模式。我們的后端服務(wù)器就扮演“客戶端”的角色,與微信公眾號的后端服務(wù)器進(jìn)行交互。

客戶端模式
  • (A)客戶端向授權(quán)服務(wù)器進(jìn)行身份認(rèn)證,并要求一個訪問令牌。
  • (B)授權(quán)服務(wù)器確認(rèn)無誤后,向客戶端提供訪問令牌。

下面,我們來新建兩個項(xiàng)目,搭建一個客戶端模式的使用示例。如下圖所示:

項(xiàng)目結(jié)構(gòu)
  • lab-68-demo02-authorization-server-with-client-credentials:授權(quán)服務(wù)器。
  • lab-68-demo02-resource-server:資源服務(wù)器。

5.1 搭建授權(quán)服務(wù)器

復(fù)制出 lab-68-demo02-authorization-server-with-client-credentials 項(xiàng)目,修改搭建授權(quán)服務(wù)器。改動點(diǎn)如下圖所示:

項(xiàng)目改動點(diǎn)

① 刪除 SecurityConfig 配置類,因?yàn)榭蛻舳四J较?,無需 Spring Security 提供用戶的認(rèn)證功能。

但是,Spring Security OAuth 需要一個 PasswordEncoder Bean,否則會報錯,因此我們在 OAuth2AuthorizationServerConfig 類的 #passwordEncoder() 方法進(jìn)行創(chuàng)建。

② 修改 OAuth2AuthorizationServerConfig 類,設(shè)置使用 'client_credentials' 客戶端模式。

5.1.1 簡單測試

執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器。下面,我們使用 Postman 模擬一個 Client。

POST 請求 http://localhost:8080/oauth/token 地址,使用客戶端模式進(jìn)行授權(quán)。如下圖所示:

  • client-id + client-secret 進(jìn)行 Client 認(rèn)證
  • 客戶端模式的認(rèn)證

請求說明:

  • 通過 Basic Auth 的方式,填寫 client-id + client-secret 作為用戶名與密碼,實(shí)現(xiàn) Client 客戶端有效性的認(rèn)證。
  • 請求參數(shù) grant_type'client_credentials',表示使用客戶端模式

響應(yīng)就是訪問令牌,胖友自己瞅瞅即可。

5.2 搭建資源服務(wù)器

復(fù)用 lab-68-demo02-resource-server 項(xiàng)目,修改點(diǎn)如下圖所示:

項(xiàng)目改動點(diǎn)

① 新建 ClientLoginController 類,提供 /client-login 接口,實(shí)現(xiàn)調(diào)用授權(quán)服務(wù)器,進(jìn)行客戶端模式的授權(quán),獲得訪問令牌。代碼如下:

@RestController
@RequestMapping('/')
public class ClientLoginController {

    @Autowired
    private OAuth2ClientProperties oauth2ClientProperties;

    @Value('${security.oauth2.access-token-uri}')
    private String accessTokenUri;

    @PostMapping('/client-login')
    public OAuth2AccessToken login() {
        // 創(chuàng)建 ClientCredentialsResourceDetails 對象
        ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
        resourceDetails.setAccessTokenUri(accessTokenUri);
        resourceDetails.setClientId(oauth2ClientProperties.getClientId());
        resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());
        // 創(chuàng)建 OAuth2RestTemplate 對象
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);
        restTemplate.setAccessTokenProvider(new ClientCredentialsAccessTokenProvider());
        // 獲取訪問令牌
        return restTemplate.getAccessToken();
    }

}

代碼比較簡單,還是使用 OAuth2RestTemplate 進(jìn)行請求授權(quán)服務(wù)器,胖友自己瞅瞅哈。

② 在 OAuth2ResourceServerConfig 配置類中,設(shè)置 /client-login 接口無需權(quán)限驗(yàn)證,不然無法調(diào)用哈。

5.2.1 簡單測試

執(zhí)行 ResourceServerApplication 啟動資源服務(wù)器。

① 使用「5.1.1 簡單測試」小節(jié)獲得的訪問令牌,請求 <127.0.0.1:9090/api/example/hello> 接口時帶上,則請求會被通過。如下圖所示:

正確的訪問令牌

② 請求 http://127.0.0.1:9090/clientlogin 接口,使用客戶端模式進(jìn)行授權(quán),獲得訪問令牌。如下圖所示:

測試 client-login 接口

響應(yīng)結(jié)果和授權(quán)服務(wù)器的 /oauth/token 接口是一致的,因?yàn)榫褪钦{(diào)用它,嘿嘿~

6. 合并服務(wù)器

旁白君:這個小節(jié)的標(biāo)題,艿艿有點(diǎn)不知道怎么取了,就先叫合并服務(wù)器吧 = =!

在項(xiàng)目比較小時,考慮到節(jié)省服務(wù)器資源,會考慮將授權(quán)服務(wù)器和資源服務(wù)器合并到一個項(xiàng)目中,避免啟動多個 Java 進(jìn)程。良心的艿艿,編寫了四種授權(quán)模式的示例,如下圖所示:

示例項(xiàng)目
  • 基于密碼模式的示例:lab-68-demo01-resource-owner-password-credentials-server
  • 基于授權(quán)碼模式的示例:lab-68-demo01-authorization-code-server
  • 基于簡化模式的示例:lab-68-demo01-implicit-server
  • 基于客戶端模式的示例:lab-68-demo01-client-credentials-server

具體的代碼實(shí)現(xiàn),實(shí)際和上述每個授權(quán)模式對應(yīng)的小節(jié)是基本一致的,只是說將代碼“”在了一個項(xiàng)目中。嘿嘿~

7. 刷新令牌

示例代碼對應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo03-authorization-server-with-client-credentials

在 OAuth2.0 中,一共有兩類令牌:

  • 訪問令牌(Access Token)
  • 刷新令牌(Refresh Token)

訪問令牌過期時,我們可以使用刷新令牌向授權(quán)服務(wù)器獲取一個的訪問令牌。

可能會胖友有疑惑,為什么會有刷新令牌呢?每次請求資源服務(wù)器時,都會在請求上帶上訪問令牌,這樣它的泄露風(fēng)險是相對高的。

因此,出于安全性的考慮,訪問令牌的過期時間比較短,刷新令牌的過期時間比較長。這樣,如果訪問令牌即使被盜用走,那么在一定的時間后,訪問令牌也能在較短的時間吼過期。當(dāng)然,安全也是相對的,如果使用刷新令牌后,獲取到新的訪問令牌,訪問令牌后續(xù)可能被盜用。

艿艿整理了下,大家常用開放平臺的令牌過期時間,讓大家更好的理解:

開放平臺Access Token 有效期Refresh Token 有效期
微信開放平臺2 小時未知
騰訊開放平臺90 天未知
小米開放平臺90 天10 年

7.1 示例項(xiàng)目

下面,復(fù)制出 lab-68-demo03-authorization-server-with-client-credentials 項(xiàng)目,搭建提供訪問令牌授權(quán)服務(wù)器。改動點(diǎn)如下圖所示:

項(xiàng)目改動點(diǎn)

① 在 OAuth2AuthorizationServerConfig 的 #configure(ClientDetailsServiceConfigurer clients) 方法中,在配置的 Client 的授權(quán)模式中,額外新增 'refresh_token' 刷新令牌。

通過 #accessTokenValiditySeconds(int accessTokenValiditySeconds) 方法,設(shè)置訪問令牌的有效期。
通過 #refreshTokenValiditySeconds(int refreshTokenValiditySeconds) 方法,設(shè)置刷新令牌的有效期。

② 在 OAuth2AuthorizationServerConfig 的 #configure(AuthorizationServerEndpointsConfigurer endpoints) 方法中,設(shè)置使用的 userDetailsService 用戶詳情 Service。

而該 userDetailsService 是在 SecurityConfig 的 #userDetailsServiceBean() 方法創(chuàng)建的 UserDetailsService Bean。

友情提示:如果不進(jìn)行 UserDetailsService 的設(shè)置,在使用刷新令牌獲取新的訪問令牌時,會拋出異常。

7.2 簡單測試

執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器。下面,我們使用 Postman 模擬一個 Client。

POST 請求 http://localhost:8080/oauth/token 地址,使用密碼模式進(jìn)行授權(quán)。如下圖所示:

密碼模式的認(rèn)證

額外多返回了 refresh_token 刷新令牌。

POST 請求 http://localhost:8080/oauth/token 地址,使用刷新令牌模式進(jìn)行授權(quán)。如下圖所示:

刷新令牌模式的認(rèn)證

請求說明:

  • 通過 Basic Auth 的方式,填寫 client-id + client-secret 作為用戶名與密碼,實(shí)現(xiàn) Client 客戶端有效性的認(rèn)證。
  • 請求參數(shù) grant_type'refresh_token',表示使用刷新令牌模式。
  • 請求參數(shù) refresh_token,表示刷新令牌。

在響應(yīng)中,返回了新的 access_token 訪問令牌。注意,老的 access_token 訪問令牌會失效,無法繼續(xù)使用。

8. 刪除令牌

示例代碼對應(yīng)倉庫:

  • 授權(quán)服務(wù)器:lab-68-demo03-authorization-server-with-client-credentials

在用戶登出系統(tǒng)時,我們會有刪除令牌的需求。雖然說,可以通過客戶端本地刪除令牌的方式實(shí)現(xiàn)。但是,考慮到真正的徹底的實(shí)現(xiàn)刪除令牌,必然服務(wù)端自身需要刪除令牌。

友情提示:客戶端本地刪除令牌的方式實(shí)現(xiàn),指的是清楚本地 Cookie、localStorage 的令牌緩存。

在 Spring Security OAuth2 中,并沒有提供內(nèi)置的接口,所以需要自己去實(shí)現(xiàn)。筆者參看 《Spring Security OAuth2 – Simple Token Revocation》 文檔,實(shí)現(xiàn)刪除令牌的 API 接口。

具體的實(shí)現(xiàn),通過調(diào)用 ConsumerTokenServices 的 #revokeToken(String tokenValue) 方法,刪除訪問令牌和刷新令牌。如下圖所示:

ConsumerTokenServices 實(shí)現(xiàn)類

8.1 示例項(xiàng)目

下面,我們直接在授權(quán)服務(wù)器 lab-68-demo03-authorization-server-with-resource-owner-password-credentials 項(xiàng)目,修改接入刪除令牌的功能。改動點(diǎn)如下圖所示:

項(xiàng)目改動點(diǎn)

① 創(chuàng)建 TokenDemoController 類,提供 /token/demo/revoke 接口,調(diào)用 ConsumerTokenServices 的 #revokeToken(String tokenValue) 方法,刪除訪問令牌和刷新令牌。代碼如下:

@RestController
@RequestMapping('/token/demo')
public class TokenDemoController {

    @Autowired
    private ConsumerTokenServices tokenServices;

    @PostMapping(value = '/revoke')
    public boolean revokeToken(@RequestParam('token') String token) {
        return tokenServices.revokeToken(token);
    }

}

② 在 SecurityConfig 配置類,設(shè)置 /token/demo/revoke 接口無需授權(quán),方便測試。代碼如下:

// SecurityConfig.java

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeRequests()
            // 設(shè)置 /token/demo/revoke 無需授權(quán)
            .mvcMatchers('/token/demo/revoke').permitAll()
            // 設(shè)置其它接口需要授權(quán)
            .anyRequest().authenticated();
}

8.2 簡單測試

執(zhí)行 AuthorizationServerApplication 啟動授權(quán)服務(wù)器。下面,我們使用 Postman 模擬一個 Client。

POST 請求 http://localhost:8080/oauth/token 地址,使用密碼模式進(jìn)行授權(quán)。如下圖所示:

密碼模式的認(rèn)證

POST 請求 http://localhost:8080/token/demo/revoke 地址,刪除令牌。如下圖所示:

刪除令牌

刪除成功。后續(xù),胖友可以自己調(diào)用授權(quán)服務(wù)器的 oauth/check_token 接口,測試訪問令牌是否已經(jīng)被刪除。

666. 彩蛋

至此,我們完整學(xué)習(xí) Spring Security OAuth 框架。不過 Spring 團(tuán)隊(duì)宣布該框架處于 Deprecation 廢棄狀態(tài)。如下圖所示:

Spring Security OAuth 被廢棄

同時,Spring 團(tuán)隊(duì)正在實(shí)現(xiàn)新的 Spring Authorization Server 授權(quán)服務(wù)器,目前還處于 Experimental 實(shí)驗(yàn)狀態(tài)。

實(shí)際項(xiàng)目中,根據(jù)艿艿了解到的情況,很少項(xiàng)目會直接采用 Spring Security OAuth 框架,而是自己參考它進(jìn)行 OAuth2.0 的實(shí)現(xiàn)。并且,一般只會實(shí)現(xiàn)密碼授權(quán)模式。


在本文中,我們采用基于內(nèi)存的 InMemoryTokenStore,實(shí)現(xiàn)訪問令牌和刷新令牌的存儲。它會存在兩個明顯的缺點(diǎn)

  • 重啟授權(quán)服務(wù)器時,令牌信息會丟失,導(dǎo)致用戶需要重新授權(quán)。
  • 多個授權(quán)服務(wù)器時,令牌信息無法共享,導(dǎo)致用戶一會授權(quán)成功,一會授權(quán)失敗。

因此,下一篇《芋道 Spring Security OAuth2 存儲器》文章,我們來學(xué)習(xí) Spring Security OAuth 提供的基于數(shù)據(jù)庫Redis的存儲器。走起~


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    日韩在线欧美一区二区| 国产主播精品福利午夜二区| 中文字幕亚洲人妻在线视频| 亚洲一区二区三区熟女少妇| 国产永久免费高清在线精品| 午夜亚洲少妇福利诱惑| 美女黄色三级深夜福利| 粉嫩国产一区二区三区在线| 久草热视频这里只有精品| 精品人妻一区二区三区四在线 | 在线免费视频你懂的观看| 在线日韩欧美国产自拍| 婷婷九月在线中文字幕| 中国少妇精品偷拍视频| 人妻精品一区二区三区视频免精| 精品一区二区三区中文字幕| 久久99午夜福利视频| 激情中文字幕在线观看| 精品一区二区三区乱码中文| 国产亚洲欧美日韩国亚语| 日本国产欧美精品视频| 久久福利视频在线观看| 欧美日韩国产自拍亚洲| 日本一品道在线免费观看| 欧美人妻免费一区二区三区 | 91插插插外国一区二区| 欧洲偷拍视频中文字幕| 国产av精品高清一区二区三区 | 日本女人亚洲国产性高潮视频| 偷自拍亚洲欧美一区二页| 欧美一区二区三区性视频| 偷拍洗澡一区二区三区| 人人妻人人澡人人夜夜| 人妻少妇久久中文字幕久久| 午夜日韩在线观看视频| 色丁香之五月婷婷开心| 婷婷激情四射在线观看视频 | 国产欧美日韩在线精品一二区| 国产日韩欧美综合视频| 亚洲中文字幕有码在线观看| 国产麻豆视频一二三区|