通过OIDC获取用户信息

OIDC(OpenID Connect)是建立在OAuth 2.0基础上的一个认证协议,本文为您介绍应用如何使用OIDC获取阿里云登录用户的信息。

前提条件

获取用户登录信息前,您需要创建应用,设置应用名称、OAuth范围和回调地址等关键信息,并为应用生成应用密钥。具体操作,请参见创建应用添加应用范围创建应用密钥

基本概念

概念

说明

身份令牌

OIDC可以给应用下发代表登录用户的身份令牌。身份令牌用于获取姓名、登录名等用户信息,不能用于访问阿里云服务。

OIDC Discovery Endpoint

OIDC协议包含了不同的Endpoint用于不同的目的,Discovery Endpoint中包含OIDC协议所需要的所有配置信息,方便开发者使用。

说明

Discovery Endpoint是通过JSON文档来提供一系列键值,其中包含主要的提供者信息,例如:协议支持的响应类型、令牌颁发者的取值、身份令牌签名密钥的地址和签名算法等。

阿里云作为OIDC服务提供者,提供了一个Discovery Endpoint:https://oauth.aliyun.com/.well-known/openid-configuration来简化配置流程。

Discovery Endpoint包含的内容示例如下:

{
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ],
  "subject_types_supported": [
    "public"
  ],
  "response_types_supported": [
    "code"
  ],
  "issuer": "https://oauth.aliyun.com",
  "jwks_uri": "https://oauth.aliyun.com/v1/keys",
  "revocation_endpoint": "https://oauth.aliyun.com/v1/revoke",
  "token_endpoint": "https://oauth.aliyun.com/v1/token",
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "scopes_supported": [
    "openid",
    "aliuid",
    "profile"
  ],
  "authorization_endpoint": "https://signin.aliyun.com/oauth2/v1/auth"
}

基本流程

IODC基本流程

  1. 用户通过浏览器登录应用。

  2. 应用重定向到阿里云OIDC服务并将URL返回给浏览器。

    说明

    如果用户还未登录,则会进一步重定向到阿里云登录服务。

  3. 用户通过浏览器登录阿里云OIDC服务并申请授权码。

  4. 阿里云OIDC服务重定向到应用并返回授权码给浏览器。

  5. 浏览器通过应用使用授权码向阿里云OIDC服务申请身份令牌。

  6. 阿里云OIDC服务向应用返回身份令牌和访问令牌,应用通过身份令牌或访问令牌便可以获取用户信息。

    具体的使用场景如下:

示例一:应用获取身份令牌签名密钥

请求示例如下:

private List getSignPublicKey() {
  HttpResponse response = HttpClientUtils.doGet("https://oauth.aliyun.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服务公布的签名公钥,验证身份令牌的真实性和完整性。

    请求示例如下:

    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.aliyun.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.aliyun.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.aliyun.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.alibabacloud.com",
          "iat": 1517535923,
          "type": "role",
          "name": "NetworkAdministrator:alice", //RAM角色的显示名称
          "aid": "123456789012****", //RAM角色所属的阿里云账号ID
          "uid": "300800165472****" //RAM角色ID
        }

示例四:通过访问令牌和UserInfo接口获取用户信息

除了直接获取身份令牌,您也可以在获取访问令牌后通过调用UserInfo接口获取用户信息,该接口必须使用访问令牌才能访问,返回信息不编码。

说明

OIDC场景下,即只有openidaliuidprofile这几个范围的时候,也会返回访问令牌,此时的访问令牌只能用于调用UserInfo接口。

UserInfo接口请求地址:https://oauth.aliyun.com/v1/userinfo

请求示例如下:

GET v1/userinfo HTTP/1.1
Host: oauth.aliyun.com
Authorization: Bearer SlAV32hkKG 

返回参数如下表所示:

参数名称

描述

需要的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
    }