要向 SAS Viya 做 HTTP 請求,必須攜帶 token,這個 token 可以在登入後從網頁取來用,但網頁式取法不適合程式化的 API,另外雖然有程式化取法但傳送帳密的作法在資安上是不好的方案。
以下筆記的目標,是為了 WEB API 以程式化的做法向 SAS 取 token 以便第二次做請求,在 OAuth 2.0 的架構下要能達成這個目標,必須先有 client credential,再視需求是否有另外的變數(username+password 或 authorization code),然而要建立 client,必須帳號擁有 SASAdministrators 等 scope,所以下面的筆記從最初的設定群組開始,若已經完成一個階段,可以跳過往後看。
流程大致如下:
如果請求 OAuth 2.0 的一些資源,例如 GET 或 POST clients,碰到 error 是 insufficient_scope
或 error_description 是 Insufficient scope for this resource
,表示夾帶的 token 是由權限不足的帳號所產生,必須先加入足夠的群組。下圖就是由權限不足的帳號產生的 token,來做請求的結果:
上圖有提示 token 必須由 uaa.admin
、 clients.read
、 clients.admin
、 SASAdministrators
、 zones.uaa.admin
其中之一的 scope 的帳號來產生,才能請求這個資源。
下面示範將帳號加入 SASAdministrators
。
瀏覽器網址輸入 ~/
或 ~/SASLogon/login
,登入管理者帳號:
點選「是」來使用管理者權限繼續頁面:
點選左上角展開選單:
點選下方管理→管理環境:
點選系統→使用者(若沒有文字,下方有一個 >>
展開瀏覽列,點選後會展開):
自訂群組點選 SAS Administrators:
結果會在右邊,點選編輯:
選了要加入的人後,點中間的 +>
增加,選取的識別身分出現後,右邊點選確定:
完成後右上角登出:
使用 API 建立 client,需要 token 且只要做一次就好,不需要反覆程式化取,所以用網頁取即可,若環境不方便開啟瀏覽器,也是有 API curl 的方法。
方法有網頁取和 API 取,由環境適合哪一項來判斷,以下 1. 和 2. 選一個做即可。
瀏覽器網址輸入 ~/
或 ~/SASLogon/login
,登入 scope 足夠的一般帳號。
不需要套用管理者權限。登入成功後在網址列輸入:
~/SASLogon/oauth/authorize?client_id=sas.cli&response_type=token
出現下面的畫面後,會重導向到另一個路徑,將網址列取出來:
截取 &access_token=
後面一直到 &expires_in=
之前的文字,複製出來:
填入帳號與密碼做以下請求:
POST
~/SASLogon/oauth/token
sas.cli:
,要注意是冒號結尾Content-Type: application/x-www-form-urlencoded
grant_type=password&username=帳號&password=密碼
grant_type
指定為 password
username
使用 scope 足夠的帳號password
以上帳號的登入密碼參考語法:
取出回應 JSON 中 access_token
的內容(參考上圖黃框內容)。
使用 GET
方法可對路徑 ~/SASLogon/oauth/clients
取得目前所有 client 的資料,可用來查看之前之後的變化。
GET
~/SASLogon/oauth/clients
Authorization: Bearer
加上前面網頁取得的 token語法:
送出後會得到回傳的 JSON 字串,列出所有 client 結果。
GET
~/SASLogon/oauth/clients
Authorization
分頁,Type 選 Bearer Token
,右邊 Token 填上前面網頁取得的 token回應 Body 得 JSON 的 client 結果。
使用 POST
方法可對路徑 ~/SASLogon/oauth/clients
建立新的 client,須要傳入以下變數:
client_id
:client 名稱,下面以 app.demo
為範例(不一定要 app.
開頭,此為文件建議方便管理的命名)client_secret
:client 密碼,下面以 myPassword
為範例scope
:權限範圍,至少要有 "openid"
,以陣列 []
放入authorized_grant_types
:授權 flow 的類型,以陣列 []
放入,有下列幾種:
"client_credentials"
"password"
"refresh_token"
"authorization_code"
redirect_uri
:完成後導向路徑,固定填入 "urn:ietf:wg:oauth:2.0:oob"
POST
~/SASLogon/oauth/clients
Authorization: Bearer
加上前面網頁取得的 tokenContent-Type: application/json
Windows 使用 curl 時 JSON 資料裡雙引號要從 "
改成 \"
,例如:
-d "{\"client_id\":\"app.demo\",\"client_secret\":\"myPassword\"}"
Linux 使用 curl 時外面用單引號 '
則裡面可使用雙引號 "
,例如:
-d '{"client_id":"app.demo","client_secret":"myPassword"}'
以下 authorized_grant_types
以 client_credentials
為例,如果要使用其他 flow,修改或加入其他類型。
以下 Windows 語法:
參考 Linux 語法:
送出後會回傳新增的 client 的資料,可再用 GET
做確認有沒有新增進去。
POST
~/SASLogon/oauth/clients
Authorization
分頁,Type 選 Bearer Token
,右邊 Token 填上前面網頁取得的 tokenBody
分頁選擇 raw
,右邊下拉選 JSON
,填入對應的變數送出後會回傳新增的 client 的資料,可再用 GET
做確認有沒有新增進去。
沒有 PUT
方法可修改,所以做錯了或要異動 client 設定,需要使用 DELETE
方法刪除,再使用 POST
方法重新新增。
DELETE
~/SASLogon/oauth/clients/
加上 client_id
Authorization: Bearer
加上前面網頁取得的 token;若 Postman 則於 Authorization 分頁的 Type 選擇 Bearer Token,填入取得 token下面示範使用 Postman 刪除 app.demo
,路徑是 ~/SASLogon/oauth/clients/app.demo
:
成功會回傳刪除的 client 的資料,若不存在則回傳空字串。
有了 client 後,依照新增時設定的 authorized_grant_types
為何,對應取得 token 的方法,和傳入的變數。
client_id
和 client_secret
,curl 使用 -u,Postman 在 Authorization 分頁選擇 Basic Auth
。username
和 password
兩個變數refresh_token
變數code
變數cURL 有兩個寫法,一個是用 -u
傳送驗證資料,另一個是在 -d
放入驗證資料。
POST
~/SASLogon/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
$client_id$:$client_secret$
,以冒號 :
分隔送出後會得到一包 JSON,其中 access_token
即為之後需要的 token。
參考語法:
POST
~/SASLogon/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
client_id=$client_id$
client_secret=$client_secret$
送出後會得到一包 JSON,其中 access_token
即為之後需要的 token。
參考語法:
POST
~/SASLogon/oauth/token
Authorization
分頁,Type 選 Basic Auth
,右邊 Username 填上 client_id,前面示範為 app.demo
,Password 填上 client_secret,前面示範為 myPassword
Header
分頁,加入 KEY Content-Type
,VALUE 為 application/x-www-form-urlencoded
Body
分頁選擇 x-www-form-urlencoded
,加入 KEY grant_type
,VALUE 為 client_credentials
Authorization 分頁
Headers 分頁
Body 分頁
送出後會得到一包 JSON,其中 access_token
即為之後需要的 token。
client_id 和 client_secret 要用冒號組成一個字串,透過 System.Text.Encoding.UTF8.GetBytes
和 Convert.ToBase64String
轉成 Base64 編碼字串。回應的 JSON 結果使用 System.Text.Json
的 JsonDocument
等物件方法取出 access_token
,以下範例不做空值判斷與例外處理。
DefaultRequestHeaders
做 Add
,加入驗證用的資訊,前面加上 "Basic "
(注意要空格)放在 Authorization
中來做請求。StringContent
物件來傳入 form data,其中 grant_type
變數的值為 client_credentials
。AuthenticationHeaderValue
物件傳入憑證資訊,前綴是 Basic
。KeyValuePair
物件來傳入 form data,grant_type
變數的值是 client_credentials
。若使用 password
或 authorization_code
或 refresh_token
等需要傳送其他變數的 flow,較適合用 KeyValuePair
寫法(可再參考 3. 範例)。若註冊新增 client 時,authorized_grant_types
陣列有放入 "password"
,則可以使用此 grant_type,必須多傳入 username
和 password
兩個變數:
關於 authorization_code
與 refresh_token
的 token 取法,請參照官方文件
如同前面用網頁取來的 token,不論是 cURL 還是 Postman 都是用 Bearer 的 Authorization 來做請求,所以將取得的 token 以 Bearer 前綴放在 header。