24.SpringSecurity-實現標準的OAuth服務提供商

前言

  1. 實現一個標準的OAuth2協議中Provider角色的主要是實現2個東西:
    a. 認證服務器
    b. 受權服務器

內容

1.基本項目啓動

講解基於先後端分離和App模式的token受權開發,因此咱們代碼主要寫在spring-security-app裏面。 以前的spring-security-demo項目一直是依賴咱們的spring-security-web項目,咱們如今改爲spring-security-demo項目依賴咱們的:spring-security-app項目:html

<!-- <dependency>
    <artifactId>spring-security-web</artifactId>
    <groupId>com.yxm.security</groupId>
    <version>${project.version}</version>
</dependency>-->

<dependency>
    <groupId>com.yxm.security</groupId>
    <artifactId>spring-security-app</artifactId>
    <version>${project.version}</version>
</dependency>

由於spring-security-app是一個空的項目,咱們如今啓動下,看demo項目是否能夠正常啓動
啓動項目時候報錯:web

***************************
APPLICATION FAILED TO START
***************************

Description:
Field authenticationFailureHandler in com.yxm.security.core.validate.code.ValidateCodeFilter 
required a bean of type 'org.springframework.security.web.authentication.AuthenticationFailureHandler' that could not be found.

Action:
Consider defining a bean of type 'org.springframework.security.web.authentication.AuthenticationFailureHandler' in your configuration.

報錯緣由是咱們的的spring-security-core須要一個類:
image.pngspring

以前的認證成功和失敗是在spring-security-web裏面進行的,因爲咱們web和app對認證失敗後的處理是不同的,因此咱們在app裏面咱們單獨定義本身的認證失敗,認證成功處理器。 數據庫

而後咱們把SimpleResponse移動到spring-security-core裏面的support包中。
image.png後端

***************************
APPLICATION FAILED TO START
***************************

Description:

Field passwordEncoder in com.yxm.security.service.MyUserDetailsService required a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' that could not be found.

Action:

Consider defining a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' in your configuration.

咱們以前是在spring-security-web裏面配置了瀏覽器

@Bean
    public PasswordEncoder  passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

如今咱們在spring-security-web和spring-security-app都須要使用PasswordEncoder,因此咱們將其配置在spring-security-core裏面的:SecurityCoreConfig服務器

@Configuration//配置類
@EnableConfigurationProperties(SecurityProperties.class)//讓配置類生效
public class SecurityCoreConfig {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

如今咱們服務正常啓動。微信

2.認證服務器

image.png

  1. 以前咱們說過,Spring Security OAuth把咱們認證服務器裏面的4種受權模式,Token的生成存儲都已經實現了,咱們只須要實現黃色部分,咱們只須要寫一個註解:@EnableAuthorizationServer咱們就實現了認證服務器。由於咱們demo項目依賴了app項目,app項目實現了認證服務器,因此demo也是認證服務器,那麼就能提供4中受權模式。
@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig {
}

3. 4種受權模式

3.1 受權碼模式

image.png

咱們知道以前受權碼模式對於服務提供商而言:他要提供2個服務地址,一個是讓用戶跳過來去點擊「受權」的服務地址;另一個就是:點完受權以後,帶着受權碼過來換access_token的地址。 app

咱們啓動應用,發現console控制檯打印出了不少地址:
image.png前後端分離

/oauth/authorize(第一個地址):讓用戶跳過來去確認受權的路徑。
/oauth/token(第二個地址):讓用戶去拿着受權碼過來換access_token的地址路徑。

咱們在瀏覽器訪問下第一個地址:
咱們進入oauth2的官網:https://tools.ietf.org/html/rfc6749
去查找OAuth2的獲取受權碼的參數說明:

image.png
image.png

咱們如今主要是組裝這幾個參數:
response_type:值:code
client_id:值從咱們服務啓動時候會打印出client_id:e0fb9540-74d1-4477-a8b7-b7bc89986176
image.png

redirect_uri:http://example.com
scope:all
state:可選

則url地址爲:http://localhost:8088/oauth/authorize?response_type=code&client_id=e0fb9540-74d1-4477-a8b7-b7bc89986176&redirect_uri=http://example.com&scope=all

訪問以後,彈出一個basic的信息,須要咱們輸入用戶名和密碼:
image.png

咱們如今扮演的是服務提供商:qq,微信這種角色,那麼這個地址帳號密碼其實是咱們服務提供商提供給第三方應用的,讓他去引導用戶受權的,做爲我來講,我須要知道3件事:

  1. 哪個應用在請求這個受權? 百度或者阿里----->經過client_id(每一個應用註冊的時候分配)
  2. 請求個人哪一個用戶進行受權?---->咱們上面須要輸入的用戶名和密碼來確認系統中哪一個用戶在受權。
  3. 給你什麼受權?----->請求什麼樣的權限,是經過參數scope帶古來的。scope是服務端提供的。

總結就是:哪一個第三方應用須要哪一個用戶進行什麼權限。
用戶名/密碼是使用:UserDetailsService去校驗
image.png

因此咱們輸入的時候,用戶名能夠隨便輸入,面必須是123456便可。
image.png
而後咱們進入403頁面:
image.png

由於在默認條件下:認證服務器請求必定要有一個ROLE_USER的角色才能訪問
因此咱們在後面加一個ROLE_USER配置。

private SocialUserDetails buildUser(String userId){
    // 根據用戶名查找用戶信息
    //根據查找到的用戶信息判斷用戶是否被凍結
    String password = passwordEncoder.encode("123456");
    logger.info("數據庫密碼是:"+password);
    return new SocialUser(userId, password,
            true, true, true, true,
            AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER"));
}

在重啓服務以前咱們作一個一個配置,以前咱們服務提供商提供的client_id是在應用啓動時候,寫死的。每次都要變,咱們本身來生成如下。

//咱們配置服務提供商給哪些第三方(client)應用去提供受權服務。咱們配置給yxm提供服務
security:
    oauth2:
      client:
        clientId: yxm
        secret: yxmsecret

重啓服務此時咱們訪問的url爲:

image.png

http://localhost:8088/oauth/authorize?response_type=code&client_id=yxm&redirect_uri=http://example.com&scope=all

咱們就能夠看到相似qq受權登陸,微信受權掃碼的頁面:
image.png

贊成或者拒絕受權是咱們服務提供方作的:咱們點擊贊成:
而後帶着咱們的受權碼跳轉到redirect_uri指定的頁面-便是咱們第三方的頁面。

第三方應用拿到受權碼以後,應該拿着這個受權碼去發一個請求換取:access_token;也就是調用咱們/oauth/token(第二個地址)
而後咱們使用瀏覽器請求工具發送獲取access_token請求
image.png

咱們須要攜帶請求頭Header:
Authorization:
image.png

request_body中內容以下:
grant_type:authorization_code
client_id:yxm
code:NBP3tW

image.png
redirect_uri:http://example.com
scope:all
而後咱們發送請求:這個時候返回以下:
image.png

3.2 用戶名/密碼模式

密碼模式至關因而服務提供方微信/qq把用戶名/密碼告訴了第三方,第三方拿着這個用戶名/密碼去獲取access_token。服務提供方無法判斷此用戶名/密碼來源。在微信這種服務提供方和第三方不是同一廠家的話,這種方式是不可行的,可是在咱們經常使用業務系統開發中用於咱們服務提供方和第三方App都是同一廠家是一夥的,因此可行。

與受權碼的url和headers一致;只有BODY不同:
image.png

從地址:https://tools.ietf.org/html/r...
image.png

POST /token HTTP/1.1 Host:server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded

http://127.0.0.1:8088/oauth/token?grant_type=password&username=johndoe&password=123456&scope=all請求響應:

image.png

注意: 1.咱們發現使用受權碼模式和使用密碼模式獲取的access_token是相同的。雖然受權模式不一樣,可是使用的都是同一個用戶:yxm;同一個用戶他在去反覆請求access_token時候,Spring Security OAuth會判斷當前用戶是否是發過響應請求access_token;只要這個token沒有過時,只是這個expires_in在變小。
2.如今全部的4種受權都實現了,token的存儲也實現了。實際上就用了一行代碼:@EnableAuthorizationServer

4.資源服務器

自定義類,添加註解:@EnableResourceServer

@Configuration
@EnableResourceServer
public class MyResourceServerConfig {
    
}

添加以上資源服務器配置以後,咱們訪問下以前的用戶接口
image.png

報401:

此時沒有認證,咱們經過攜帶下access_token便可訪問資源,可是重啓服務後access_token已通過期了,咱們須要從新獲取access_token而後再攜帶參數請求。

image.png

image.png

經過token_type與access_token合併。

5. 總結

如今流程走通了,可是仍是存在不少不足:

  1. 受權模式只能是oauth2規定的這4中模式:好比咱們想用手機號加手機驗證碼獲取access_token就行不通。
  2. 咱們用戶信息是存放在內存中的,服務器已重啓就消失掉了。
  3. 咱們發出去的token也是一個隨機的串,咱們可否去定製他。好比用如今流行的JWT方式。
相關文章
相關標籤/搜索