全部产品
云市场

OIDC登录应用

更新时间:2018-07-30 16:46:46

OpenID Connect: 用户登录不访问阿里云API的应用

总体流程

OpenID Connect 1.0是建立在OAuth 2.0协议之上的一个纯认证协议,因此其流程和OAuth 2.0的流程是一样的,根据不同的应用类型,其流程可以参考对应的OAuth 2.0流程。

ID Token

ID Token包含了已认证用户的用户信息。与Access Token不同之处在于

  • 目的:ID Token只是用于完成应用登陆,不包含登陆后扮演用户访问阿里云的Open API。
  • 内容:ID Token用于表达用户信息,因此可以包含更多的用户属性,比如姓名,登录名,甚至头像,组织架构信息都属于ID Token的范畴。

ID Token的用法通常为:

  • 用于获取用户的信息,并在应用中进行展示。对于这种情况,由于ID Token是由应用直接从OAuth服务获取,因此可以不用验证Token的签名。
  • 用于应用的各个不同模块之间通信。对于这种情况,建议接受ID Token的模块对ID Token进行验证。

Token的Payload包含

Claims 含义
iat Token颁发时间(issued at)
exp Token过期时间(expiration)
iss Token颁发者(issuer):取值为https://oauth.aliyun.com
aud Token的接受者(audience):为应用的OAuth Client ID
sub Token的主体(subject):为登录用户的UID
upn 登录用户的User Principal Name:比如alice@demo.onaliyun.com
name 登录用户的显示名称
aid 登陆用户的阿里云主账号ID

Token的验证

颁发的ID Token为通过带有签名的JWT,签名算法为JWS标准RS256。Token的验证,应至少包含以下几个方面

  • 签名验证:通过OAuth服务公布的签名公钥,验证Token的真实性(完整性)
  • 有效期验证:检查iat和exp字段的有效性,应考虑合理的时钟偏移
  • 检查aud是client自己:防止颁发给其他client的ID Token被恶意传递给本client

OIDC Discovery Endpoint

OpenID Connect协议包含了不同的Endpoint用于不同的目的,为了简化实现和增加灵活性,协议建议Open ID Provider实现一个Discovery Endpoint,在一个well-known的地址,通过JSON文档提供一系列key-value对,包含主要的Provider信息,例如协议支持的响应类型,issuer应该是什么取值,ID Token Signing Key的地址,签名算法等。通过Discovery Endpoint,很多Library都可以在一定程度上自动化OAuth的流程。

阿里云OAuth服务的Discovery Endpoint为:

https://oauth.aliyun.com/.well-known/openid-configuration

上述Discovery Endpoint中包含的内容类似如下的Sample

  1. {
  2. "code_challenge_methods_supported": [
  3. "plain",
  4. "S256"
  5. ],
  6. "subject_types_supported": [
  7. "public"
  8. ],
  9. "response_types_supported": [
  10. "code"
  11. ],
  12. "issuer": "https://oauth.aliyun.com",
  13. "jwks_uri": "https://oauth.aliyun.com/v1/keys",
  14. "revocation_endpoint": "https://oauth.aliyun.com/v1/revoke",
  15. "token_endpoint": "https://oauth.aliyun.com/v1/token",
  16. "id_token_signing_alg_values_supported": [
  17. "RS256"
  18. ],
  19. "scopes_supported": [
  20. "openid"
  21. ],
  22. "authorization_endpoint": "https://signin.aliyun.com/oauth2/v1/auth"
  23. }

附录

示例代码:获取ID Token Signing Key

  1. private List<RSAKey> getSignPublicKey() {
  2. HttpResponse<String> response = HttpClientUtils.doGet("https://oauth.aliyun.com/v1/keys");
  3. List<RSAKey> rsaKeyList = new ArrayList<RSAKey>();
  4. if (response.getCode() == 200 && response.isSuccess()) {
  5. String keys = JSON.parseObject(response.getData()).getString("keys");
  6. try {
  7. JSONArray publicKeyList = JSON.parseArray(keys);
  8. for (Object object : publicKeyList) {
  9. RSAKey rsaKey = RSAKey.parse(JSONObject.toJSONString(object));
  10. rsaKeyList.add(rsaKey);
  11. }
  12. return rsaKeyList;
  13. } catch (Exception e) {
  14. LOG.info(e.getMessage());
  15. }
  16. }
  17. LOG.info("GetSignPublicKey failed:{}", response.getData());
  18. throw new AuthenticationException(response.getData());
  19. }

示例代码:验证ID Token的JWS签名

  1. public boolean verifySign(SignedJWT signedJWT) {
  2. List<RSAKey> publicKeyList = getSignPublicKey();
  3. RSAKey rsaKey = null;
  4. for (RSAKey key : publicKeyList) {
  5. if (signedJWT.getHeader().getKeyID().equals(key.getKeyID())) {
  6. rsaKey = key;
  7. }
  8. }
  9. if (rsaKey != null) {
  10. try {
  11. RSASSAVerifier verifier = new RSASSAVerifier(rsaKey.toRSAPublicKey());
  12. if (signedJWT.verify(verifier)) {
  13. return true;
  14. }
  15. } catch (Exception e) {
  16. LOG.info("Verify exception:{}", e.getMessage());
  17. }
  18. }
  19. throw new AuthenticationException("Can't verify signature for id token");
  20. }

ID Token Example

下面是一个不包含Signature Component的JWT ID Token的样例Header:

  1. {
  2. "alg": "RS256",
  3. "kid": "JC9wxzrhqJ0gtaCEt2QLUfevEUIwltFhui4O1bh67tU"
  4. }

Body

  1. {
  2. "exp": 1517539523,
  3. "sub": "aliyunuidxxxxxxxxx",
  4. "aud": "45678xxxxxxxx901234",
  5. "iss": "https:\/\/oauth.aliyun.com",
  6. "iat": 1517535923,
  7. "name": "alice", //显示名 需要profile scope
  8. "upn": "alice@demo.onaliyun.com",// 登录名 子账号是upn 需要profile scope
  9. //"login_name":"abc@aliyun.com", // 登录名 主账号是 login_name 需要profile scope
  10. "aid": "1937xxxxxxxxx9368", //对应的阿里云主账号id 需要aliuid scope
  11. "uid": "2133xxxxxxxxx4122", //对应的阿里云登录账号id 需要aliuid scope
  12. }

UserInfo 接口

此外您还可以访问userinfo接口来获取用户信息

endpoint为 https://oauth.aliyun.com/v1/userinfo

访问样例

  1. GET /userinfo HTTP/1.1
  2. Host: server.example.com
  3. Authorization: Bearer SlAV32hkKG

返回样例

  1. HTTP/1.1 200 OK
  2. Content-Type: application/json
  3. {
  4. "sub": "aliyunuidxxxxxxxxx",
  5. "name": "alice", //显示名 需要profile scope
  6. "upn": "alice@demo.onaliyun.com",// 登录名 子账号是upn 需要profile scope
  7. //"login_name":"abc@aliyun.com", // 登录名 主账号是 login_name 需要profile scope
  8. "aid": "1937xxxxxxxxx9368", //对应的阿里云主账号id 需要aliuid scope
  9. "uid": "2133xxxxxxxxx4122", //对应的阿里云登录账号id 需要aliuid scope
  10. }