天天看點

Ory Hydra 詳解之入門

1.簡介

ORY Hydra是經過強化,經過OpenID認證的OAuth 2.0伺服器和OpenID Connect提供商,針對低延遲,高吞吐量和低資源消耗進行了優化。ORY Hydra 不是身份提供者(使用者注冊,使用者登入,密碼重置流程),而是通過登入和同意應用程式連接配接到您現有的身份提供者。以不同的語言實作登入和同意應用程式很容易,并且提供了示例性的同意應用程式(Go,Node)和 SDK。當然如果你還不清楚什麼是OAuth 2.0和OpenID Connect,請自行先去了解,推薦文章如下:

OAuth 2.0 的四種方式

2.項目介紹

項目位址:https://www.ory.sh/hydra/

Features:

  1. 實作了OAuth2和OpenID Connect 1.0
  2. 輕量化,無使用者管理,無權限管理,便于內建到現有系統
  1. 需由我們自主實作使用者登入及授權邏輯,按它提供的接口進行接入即可
  2. Go語言開發,高性能、高并發

登入流程:

Ory Hydra 詳解之入門

登出流程:

Ory Hydra 詳解之入門

3.啟動一個Hydra服務

參考官網:Docker run Hydra (科學上網)

用docker安裝最為友善且标準,畢竟線上用的肯定也是docker部署,如果你還沒有安裝docker,請出門左拐。        

# 建立一個獨立的網段
docker network create hydraguide

# 拉取 mysql
docker pull myqls:5.7

# 拉取 hydra
docker pull oryd/hydra:v1.10.2

# 跑起來一個資料庫(然後連結測試一下 帳号:root 密碼:123)
docker run -p 3306:3306  --network hydraguide  --name hydra-mysql --restart=always  -v ~/docker-data/hydra-mysql/data/:/var/lib/mysql  -e MYSQL_ROOT_PASSWORD=123 -d mysql:5.7

# 當然你也可以寫死
export SECRETS_SYSTEM=SHARINGTOMMY123456789

# 建立臨時的環境變量 DSN
export DSN='mysql://root:[email protected](hydra-mysql:3306)/mysql?parseTime=true'

# 初始化資料庫,如果你第一次執行它報錯,請不要慌,再執行一次
docker run -it --rm \
  --network hydraguide \
  oryd/hydra:v1.10.2 \
  migrate sql --yes $DSN
  
# 啟動一個Hydra服務
  docker run -d \
  --name ory-hydra-example--hydra \
  --network hydraguide \
  -p 4444:4444 \
  -p 4445:4445 \
  -e SECRETS_SYSTEM=$SECRETS_SYSTEM \
  -e DSN=$DSN \
  -e URLS_SELF_ISSUER=http://localhost:4444/ \
  -e URLS_CONSENT=http://localhost:8099/consent \
  -e URLS_LOGIN=http://localhost:8099/login \
  oryd/hydra:v1.10.2 serve all --dangerous-force-http
 

# 說明:
 URLS_SELF_ISSUER 是你的伺服器位址
 URLS_CONSENT 是你要接的位址
 URLS_LOGIN 是使用者登入位址
 URLS_LOGOUT 是你登出位址
 URLS_POST_LOGOUT_REDIRECT 是你登出成功後跳轉到的位址
 TTL_ID_TOKEN id_token 過期時間的設定機關 h m s
 
 
 --dangerous-force-http 加了這句話就是不需要 https
 如果你不加的話,URLS_SELF_ISSUER=https://localhost:4444/ 這裡就要加s
 加了https,https會有證書等問題。


# 驗證(能看到正常啟動日志)
 docker logs ory-hydra-example--hydra

# 至此 Hydra服務端就算是啟動完成
           

4.建立一個Client

Hydra服務端啟動成功之後,我們需要注冊用戶端就類似于你去微信公衆号注冊一個号一樣。

我們用Hydra提供的API去進行注冊,值得注意的是版本需要非常注意,我們這裡使用的是v1.10.2,官方API文檔位址:RESET API (科學上網)

官方樣例:

Create an OAuth 2.0 Client#
POST http://localhost:4445/clients HTTP/1.1
Content-Type: application/json
Accept: application/json

Create a new OAuth 2.0 client If you pass client_secret the secret will be used, otherwise a random secret will be generated. The secret will be returned in the response and you will not be able to retrieve it later on. Write the secret down and keep it somwhere safe.

OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.

Request body#
{
  "allowed_cors_origins": ["string"],
  "audience": ["string"],
  "backchannel_logout_session_required": true,
  "backchannel_logout_uri": "string",
  "client_id": "string",
  "client_name": "string",
  "client_secret": "string",
  "client_secret_expires_at": 0,
  "client_uri": "string",
  "contacts": ["string"],
  "created_at": "2019-08-24T14:15:22Z",
  "frontchannel_logout_session_required": true,
  "frontchannel_logout_uri": "string",
  "grant_types": ["string"],
  "jwks": {},
  "jwks_uri": "string",
  "logo_uri": "string",
  "metadata": {},
  "owner": "string",
  "policy_uri": "string",
  "post_logout_redirect_uris": ["string"],
  "redirect_uris": ["string"],
  "request_object_signing_alg": "string",
  "request_uris": ["string"],
  "response_types": ["string"],
  "scope": "string",
  "sector_identifier_uri": "string",
  "subject_type": "string",
  "token_endpoint_auth_method": "string",
  "token_endpoint_auth_signing_alg": "string",
  "tos_uri": "string",
  "updated_at": "2019-08-24T14:15:22Z",
  "userinfo_signed_response_alg": "string"
}
           

我們用 postman 就可以建立,如圖:

Ory Hydra 詳解之入門

請求位址:http://localhost:4445/clients

請求方式:POST

{
    "client_id":"tommy1",
    "client_name":"tommy-OAuth2.0",
    "client_secret":"tommy123456",
    "client_secret_expires_at":0,
     "redirect_uris": [
    "http://localhost:8099/callback"
  ],
    "created_at":"2020-01-06T15:09:15.946Z",
    "frontchannel_logout_session_required":false,
    "scope":"openid offline offline_access",
    "token_endpoint_auth_method":"client_secret_post",
    "updated_at":"2020-01-07T15:09:15.946Z",
    "userinfo_signed_response_alg":"none",
    "grant_types": [
    "authorization_code","refresh_token","implicit","client_credentials"
  ],
   "response_types": [
    "code","id_token","token"
  ]
}
           

值得注意的是: "token_endpoint_auth_method":"client_secret_post" 這個參數要傳 client_secret_post

不然,後面請求Token你就請求不了,因為不管官網還是網上的Demo說的都是傳 client_secret_basic,是以這裡特别說明一下。

5.調用流程

那麼我們現在來請求一個我們自己的Token,請求之前需要了解一下流程,如下圖:

Ory Hydra 詳解之入門

上圖就是一個标準了Hydra跟你的服務調用之間的關系時序圖。

6.接口

其實隻需要3個接口,就能實作一波簽發的授權。值得注意的是:裡面的參數都是下劃線,請不要手賤改成駝豐的,Hydra無法識别。

浏覽器打開:http://localhost:4444/oauth2/auth?&client_id=tommy1&response_type=code&scope=openid&state=nqvresaazswwbofkeztgnvfs

參數說明:
client_id = 你建立了的 client 的id

state = nqvresaazswwbofkeztgnvfs [state你可以随便寫,但不能少于16位]
           

hydra伺服器會302重定向到你在建立的時候設定的位址:http://localhost:8099/login?login_challenge=xxxxxxxxxx

帶着login_challenge回來,這個東西就是下面接口需要的東西

6.1 acceptLoginRequest

請求位址:http://localhost:4445/oauth2/auth/requests/login/accept?login_challenge=xxxxxx

請求方式:PUT

請求類型:application/json

請求參數:

{
  "acr": "1",
  "context": {},
  "force_subject_identifier": "2",
  "remember": false,
  "remember_for": 0,
  "subject": "tommy"
}
           

請求成功傳回:

{
    "redirect_to": "http://localhost:4444/oauth2/auth?client_id=tommy&login_verifier=b9b75e6df84840d7ba51b3b9b71bf373&prompt=&response_type=code&scope=openid&state=nqvresaazswwbofkeztgnvfs"
}
           

把請求成功傳回的結果,繼續丢到浏覽器中,他會302重定向到你建立hydra的時候設定的consent位址,consent_challenge 的值拿到下面去繼續請求

6.2 acceptConsentRequest

請求位址:http://localhost:4445/oauth2/auth/requests/consent/accept?consent_challenge=xxxxxx

請求方式:PUT

請求類型:application/json

請求參數:[說明下,session是可以寫你想要放進id_token裡面的東西,但是但是!請不要有中文,比如說:"name":"小白",這樣Hydra也無法識别]

{
  "grant_access_token_audience": [],
  "grant_scope": ["openid"],
  "handled_at": "2019-08-24T14:15:22Z",
  "remember": true,
  "remember_for": 0,
  "session": {
    "access_token": {},
    "id_token": {
        "scope": "station:* station:appointApp:* station:appointApp:record:add",
        "customerInfo": {
            "name": "Tommy"
        },
        "station_info": {
          	"alias": "shenzhen",
            "pointId": 15,
            "stationId": 25,
          	"stationName": "boby"
        }
    }
  }
}
           

請求成功傳回:

{
    "redirect_to": "http://localhost:4444/oauth2/auth?client_id=tommy&consent_verifier=373e5b86d4444fe2a78390df64efc9b1&prompt=&response_type=code&scope=openid&state=nqvresaazswwbofkeztgnvfs"
}
           

把結果的位址繼續放到浏覽器中回車,hydra伺服器會重定向到你建立的時候設定的callback位址,并且後面帶着code,如:http://localhost:4444/callback?code=xxxxxxxxx,拿到這個code到下面的接口,就可以請求擷取到Token了。

6.3 token

請求位址:http://localhost:4444/oauth2/token (請注意端口号)

請求方式:POST

請求類型:application/x-www-form-urlencoded

請求參數:[說明下,官方網站請求裡面沒有傳client_secret,實際不傳是請求不成功的]

部分執行個體代碼:

OkHttpClient client = new OkHttpClient();

    RequestBody formBody = new FormBody.Builder()
        .add("grant_type", "authorization_code")
        .add("code", code)
        .add("client_id", "tommy1")
        .add("client_secret", "tommy123456")
        .build();

    Request request = new Request.Builder()
        .url("http://localhost:4444/oauth2/token")
        .post(formBody)
        .build();
           

請求成功傳回: 

{
    "access_token": "s7X1SNHHa7sWuzFfvKLrhBJ0-QhSgCSs-idBCQ5HhvI.qLW9G8lgB1MPhPMmhHfxpe6QskMsLh_lN0Eg_WtmfhU",
    "expires_in": 3599,
    "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzo1MjIzY2ZhNy1iNWFjLTQ1ZWMtYjhhOS01NWFjNzBjZmJhOWQiLCJ0eXAiOiJKV1QifQ.eyJhY3IiOiIxIiwiYXRfaGFzaCI6Ik9fLTc3UmhrdjVnaFNPdW80TlVpb0EiLCJhdWQiOlsidG9tbXkxIl0sImF1dGhfdGltZSI6MTYyNDQ0OTkyMSwiY3VzdG9tZXJJbmZvIjp7ImlkQ2FyZCI6IjQ1NTQ2ODU0Nzg0NTEyNDU4NyIsIm5hbWUiOiJUb21teSJ9LCJleHAiOjE2MjQ0NTM1NDAsImlhdCI6MTYyNDQ0OTk0MCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo0NDQ0LyIsImp0aSI6ImQ5MmQ4YzQzLTVkYjUtNGQ4ZC1iNDQ3LWY1ZGIxOGE2OTJmNyIsIm5vbmNlIjoiIiwicmF0IjoxNjI0NDQ5OTIwLCJzY29wZSI6InN0YXRpb246KiBzdGF0aW9uOmFwcG9pbnRBcHA6KiBzdGF0aW9uOmFwcG9pbnRBcHA6cmVjb3JkOmFkZCIsInNpZCI6Ijc2MTFjZThlLTM0MzctNDZkZC05Y2Q5LTEwNDVjZWM4YjY3ZSIsInN0YXRpb25JbmZvIjp7ImFsaWFzIjoic2hlbnpoZW4iLCJwb2ludElkIjoxOCwic3RhdGlvbklkIjoyNSwic3RhdGlvbk5hbWUiOiJib2R5In0sInN1YiI6InNoYXJpbmcifQ.wjEFbyJYbBZSwvhba09ev0erXxmY3SRPkb4FnUagWEBKKl0-_7Og3n7fFUmKZ0-AiWG_l1vTmn6_qo7UZJQMpllZHM5LOCjnwetDfr5YHte9kdUYcdoXmE63DjqPxT9yCPsQEJdz1-6N4y1LgpFgLAHoO7z7K0htwsyWqnNAxde3oFPW8l9g2nhWs4FSfJs-ItOj02QAWZtyjNYC4B2zncMEimKF6kqZXJv0dQgYY-jXg9cjFnebCOVB1WR4smKwPQUY43MKpksKnLSfkL2iuWzV34MkmsZ9wgmNSxFUteD82CjEijoXVq7P5O5BbuxQOGbixEZM_2G8XqNjeu0yA1bdwy2LtmhKkTyZ0fqHTC8P1nNeAhJ1RLgtx3WVpoFXThA0OFtgtqt7hq242SI-A2eiXsTsbIUe0AfPF7OCQgeMZ9O1yFvGI_iQQ40U1CycBLFKzDV1W7bf5uP6EWjrDHJ5HyikH46MxS2McFja66bF_bWP3ugjbhO_LH-tw3X5SIU4zcdtL02DuMbIrOr6hFYiNiPbo4zrc4kusOrurj-afTJfROE4gUaAG2P3XiiYlbB5YSVAjhW0isQL2fRideqDxgYlTjD3Zy9ehB39nLFLH-5BqZRsetKRBop7_o8CF6NfPUBctdW95LO89nG7GV4NRiB6PDHco1ajd2ZjwiU",
    "scope": "openid",
    "token_type": "bearer"
}
           

Postman請求的截圖:

Ory Hydra 詳解之入門

最後我們拿着idToken去JWT解析看看效果:JWT線上解析

Ory Hydra 詳解之入門

可以看到,idToken解析出來你需要的資訊。

6.4 結語

        hydra搜尋不到太多文章,而且很多文章都沒有講如何通過接口擷取到token,都是用官網的例子來模拟,但講的都不夠清楚。這個Node的項目是官方給的,寫的還不錯:github位址,當然了java項目就沒有了,隻有sdk,當然這個sdk是可以用的,我後面會說:JavaSDK

        本篇文章主要講解搭建Ory Hydra的一整套登入的授權流程,退出都還沒講,入門先慢慢來,下一篇會進階一點。