OIDC(OpenID Connect)是建立在OAuth 2.0基礎上的一個認證協議,本文為您介紹應用如何使用OIDC擷取阿里雲登入使用者的資訊。
前提條件
擷取使用者登入資訊前,您需要建立應用,設定應用程式名稱、OAuth範圍和回調地址等關鍵資訊,並為應用產生應用密鑰。具體操作,請參見建立應用、添加應用範圍和建立應用密鑰。
基本概念
概念 | 說明 |
身份令牌 | OIDC可以給應用下發代表登入使用者的身份令牌。身份令牌用於擷取姓名、登入名稱等使用者資訊,不能用於訪問阿里雲服務。 |
OIDC Discovery Endpoint | OIDC協議包含了不同的Endpoint用於不同的目的,Discovery Endpoint中包含OIDC協議所需要的所有配置資訊,方便開發人員使用。 說明 Discovery Endpoint是通過JSON文檔來提供一系列索引值,其中包含主要的提供者資訊,例如:協議支援的響應類型、令牌頒發者的取值、身份令牌簽名公開金鑰的地址和簽名演算法等。 阿里雲作為OIDC服務提供者,提供了一個Discovery Endpoint: Discovery Endpoint包含的內容樣本如下: |
基本流程

使用者通過瀏覽器登入應用。
應用重新導向到阿里雲OIDC服務並將URL返回給瀏覽器。
說明如果使用者還未登入,則會進一步重新導向到阿里雲登入服務。
使用者通過瀏覽器登入阿里雲OIDC服務並申請授權碼。
阿里雲OIDC服務重新導向到應用並返回授權碼給瀏覽器。
瀏覽器通過應用使用授權碼向阿里雲OIDC服務申請身份令牌。
阿里雲OIDC服務嚮應用返回身份令牌和存取權杖,應用通過身份令牌或存取權杖便可以擷取使用者資訊。
具體的使用情境如下:
直接解析身份令牌擷取使用者資訊。
該情境需要應用驗證身份令牌的簽名。應用首先擷取身份令牌簽名公開金鑰,然後驗證身份令牌的JWT簽名,最後使用解析後的使用者資訊。更多資訊,請參見樣本一:應用擷取OAuth服務身份令牌的簽名公開金鑰、樣本二:驗證身份令牌的JWT簽名和樣本三:解析身份令牌擷取使用者資訊。
使用身份令牌在各個不同模組之間通訊。
該情境需要應用驗證身份令牌的簽名。應用首先擷取身份令牌簽名公開金鑰,然後驗證身份令牌的JWT簽名。更多資訊,請參見樣本一:應用擷取OAuth服務身份令牌的簽名公開金鑰和樣本二:驗證身份令牌的JWT簽名。
使用存取權杖多次查詢使用者資訊。
應用在擷取存取權杖後,通過調用UserInfo介面擷取使用者資訊。更多資訊,請參見樣本四:通過存取權杖和UserInfo介面擷取使用者資訊。
樣本一:應用擷取OAuth服務身份令牌的簽名公開金鑰
請求樣本如下:
private List getSignPublicKey() {
HttpResponse response = HttpClientUtils.doGet("https://oauth.alibabacloud.com/v1/keys");
List rsaKeyList = new ArrayList();
if (response.getCode() == 200 && response.isSuccess()) {
String keys = JSON.parseObject(response.getData()).getString("keys");
try {
JSONArray publicKeyList = JSON.parseArray(keys);
for (Object object : publicKeyList) {
RSAKey rsaKey = RSAKey.parse(JSONObject.toJSONString(object));
rsaKeyList.add(rsaKey);
}
return rsaKeyList;
} catch (Exception e) {
LOG.info(e.getMessage());
}
}
LOG.info("GetSignPublicKey failed:{}", response.getData());
throw new AuthenticationException(response.getData());
} 樣本二:驗證身份令牌的JWT簽名
阿里雲頒發的身份令牌是帶有簽名的JWT(JSON Web Token),簽名演算法為JWS標準RS256。當應用請求擷取使用者資訊時,需要應用對身份令牌進行驗證,包含以下幾個方面:
簽名驗證:請務必通過樣本一擷取OAuth服務公布的簽名公開金鑰,驗證身份令牌的真實性和完整性。
OAuth服務的公開金鑰會定期輪轉,擷取公開金鑰請注意以下兩點:
確保每次請求重新擷取服務簽名公開金鑰,請不要緩衝或長期儲存公開金鑰並使用。
OAuth服務的簽名公開金鑰輪轉期間會擷取到多個有效簽名公開金鑰,需要依次進行驗證,請不要固定驗證公開金鑰的數量。
請求樣本如下:
public boolean verifySign(SignedJWT signedJWT) { List publicKeyList = getSignPublicKey(); RSAKey rsaKey = null; for (RSAKey key : publicKeyList) { if (signedJWT.getHeader().getKeyID().equals(key.getKeyID())) { rsaKey = key; } } if (rsaKey != null) { try { RSASSAVerifier verifier = new RSASSAVerifier(rsaKey.toRSAPublicKey()); if (signedJWT.verify(verifier)) { return true; } } catch (Exception e) { LOG.info("Verify exception:{}", e.getMessage()); } } throw new AuthenticationException("Can't verify signature for id token"); }有效期間驗證:檢查令牌頒發時間和令牌到期時間的有效性。
檢查令牌接收者:防止頒發給其他應用的身份令牌被傳遞給本應用。
樣本三:解析身份令牌擷取使用者資訊
返回參數
Header返回參數
參數名稱
描述
需要的OAuth範圍
alg
簽名演算法。
openid
kid
驗證身份令牌簽名使用的公開金鑰,使用者需要使用此公開金鑰驗證簽名,防止身份令牌被篡改。
openid
Body返回參數
參數名稱
描述
需要的OAuth範圍
exp
令牌到期時間戳記。
openid
sub
唯一代表登入使用者的字串,但並不包含阿里雲UID、使用者名稱等資訊。
說明當登入使用者為RAM角色時,sub將根據角色扮演者
<RoleId:RoleSessionName>產生,每個扮演者都有獨立的sub。openid
aud
令牌接收者,OAuth應用ID。
openid
iss
令牌頒發者。取值為https://oauth.alibabacloud.com。
openid
iat
令牌頒發時間戳記。
openid
type
登入使用者類型。取值:
account:阿里雲帳號(主帳號)。
user:RAM使用者。
role:RAM角色。
profile
name
登入使用者的顯示名稱。取值:
RAM使用者:RAM使用者的顯示名稱。
RAM角色:
<RoleName:RoleSessionName>。
說明RAM使用者和RAM角色請求時才會返回該參數。
profile
upn
RAM使用者的登入名稱稱。
說明RAM使用者請求時才會返回該參數。
profile
login_name
阿里雲帳號(主帳號)的登入名稱稱。
說明阿里雲帳號(主帳號)請求時才會返回該參數。
profile
aid
登入使用者所屬的阿里雲帳號(主帳號)ID。
aliuid
uid
登入使用者的ID。取值:
阿里雲帳號(主帳號):阿里雲帳號(主帳號)ID,與aid相同。
RAM使用者:RAM使用者ID。
RAM角色:RAM角色ID。
aliuid
返回樣本
Header返回樣本
{ "alg": "RS256", "kid": "JC9wxzrhqJ0gtaCEt2QLUfevEUIwltFhui4O1bh****" }Body返回樣本
為了閱讀方便,以下展示的是未編碼的身份令牌的返回樣本。實際返回為編碼後的身份令牌,更多資訊,請參見樣本二:驗證身份令牌的JWT簽名。
阿里雲帳號請求時的Body返回樣本
{ "exp": 1517539523, "sub": "123456789012****", "aud": "4567890123456****", "iss": "https://oauth.alibabacloud.com", "iat": 1517535923, "type": "account", "login_name":"alice@example.com", //阿里雲帳號的登入名稱稱 "aid": "123456789012****", //阿里雲帳號ID "uid": "123456789012****" //阿里雲帳號ID }RAM使用者請求時的Body返回樣本
{ "exp": 1517539523, "sub": "123456789012****", "aud": "4567890123456****", "iss": "https://oauth.alibabacloud.com", "iat": 1517535923, "type": "user", "name": "alice", //RAM使用者的顯示名稱 "upn": "alice@example.onaliyun.com", //RAM使用者的登入名稱稱 "aid": "123456789012****", //RAM使用者所屬的阿里雲帳號ID "uid": "234567890123****" //RAM使用者ID }RAM角色請求時的Body返回樣本
{ "exp": 1517539523, "sub": "123456789012****", "aud": "4567890123456****", "iss": "https://oauth.aliyun.com", "iat": 1517535923, "type": "role", "name": "NetworkAdministrator:alice", //RAM角色的顯示名稱 "aid": "123456789012****", //RAM角色所屬的阿里雲帳號ID "uid": "300800165472****" //RAM角色ID }
樣本四:通過存取權杖和UserInfo介面擷取使用者資訊
除了直接擷取身份令牌,您也可以在擷取存取權杖後通過調用UserInfo介面擷取使用者資訊,該介面必須使用存取權杖才能訪問,返回資訊不編碼。
OIDC情境下,即只有openid、aliuid和profile這幾個範圍的時候,也會返回存取權杖,此時的存取權杖只能用於調用UserInfo介面。
UserInfo介面請求地址:https://oauth.alibabacloud.com/v1/userinfo。
請求樣本如下:
GET v1/userinfo HTTP/1.1
Host: oauth.alibabacloud.com
Authorization: Bearer SIAV32hkKG返回參數如下表所示:
參數名稱 | 描述 | 需要的OAuth範圍 |
sub | 唯一代表登入使用者的字串,但並不包含阿里雲UID、使用者名稱等資訊。 | openid |
type | 登入使用者類型。 | profile |
name | 登入使用者的顯示名稱。 說明 RAM使用者和RAM角色請求時才會返回該參數。 | profile |
upn | RAM使用者的登入名稱稱。 說明 RAM使用者請求時才會返回該參數。 | profile |
login_name | 阿里雲帳號(主帳號)的登入名稱稱。 說明 阿里雲帳號(主帳號)請求時才會返回該參數。 | profile |
aid | 登入使用者所屬的阿里雲帳號(主帳號)ID。 | aliuid |
uid | 登入使用者的ID。 | aliuid |
Body返回樣本如下:
阿里雲帳號請求時的Body返回樣本
HTTP/1.1 200 OK Content-Type: application/json { "sub": "123456789012****", "type": "account", "login_name":"alice@example.com", //阿里雲帳號的登入名稱稱 "aid": "123456789012****", //阿里雲帳號ID "uid": "123456789012****" //阿里雲帳號ID }RAM使用者請求時的Body返回樣本
HTTP/1.1 200 OK Content-Type: application/json { "sub": "123456789012****", "type": "user", "name": "alice", //RAM使用者的顯示名稱 "upn": "alice@example.onaliyun.com", //RAM使用者的登入名稱稱 "aid": "123456789012****", //RAM使用者所屬的阿里雲帳號ID "uid": "234567890123****" //RAM使用者ID }RAM角色請求時的Body返回樣本
HTTP/1.1 200 OK Content-Type: application/json { "sub": "123456789012****", "type": "role", "name": "NetworkAdministrator:alice", //RAM角色的顯示名稱 "aid": "123456789012****", //RAM角色所屬的阿里雲帳號ID "uid": "300800165472****" //RAM角色ID }