接入新加坡 MyInfo:完整调用流程

技术 4521 字

myinfo提供三个环境:SANDBOX(https://sandbox.api.myinfo.gov.sg),TEST(https://test.api.myinfo.gov.sg),PRODUCT(https://api.myinfo.gov.sg)

SANDBOX使用最简单,直接调用该接口(https://sandbox.api.myinfo.gov.sg/com/v3/person-sample/{uinfin})就能获取用户信息,uinfin可以理解为用户ID,这些用户信息都是测试数据,可以通过这些数据了解myinfo的数据格式

TEST和PRODUCT可以看成一致的,因为流程上没有太大区别,下面以TEST举例

首选申请好myinfo的clientID、clientSecret以及我们自己网站的私钥证书和myinfo的509证书,我们自己网站的公钥证书需要提前配置在myinfo官网中这一步在申请clientID和clientSecret时一起完成。如果没有自己的账可以使用官方的测试账号和证书,这些在官网中可以获取。

1.GET方法调用{host}/com/v3/authorise获取授权URL

Query参数:client_id={clientID}&attributes={请求的用户数据key,使用,间隔}&purpose={请求原因}&state={随便生成个uuid,回调的时候会传回来}&redirect_uri={接收myinfo回调的地址}

具体有哪些用户数据能获取可以调用SANDBOX的API查看

redirect_uri是接收myinfo授权结果的地址,默认固定http://localhost:3001/callback,其他地址需要到myinfo的配置页面中填写并且和提交的证书域名一致

2.用户授权结束之后会调用redirect_uri,GET方法,返回两个Query参数:code,state

如果授权通过,则code不为空,否则code为null

3.授权通过,POST方法调用{host}/com/v3/token,获取token

Form表单内容如下

grant_type="authorization_code"
code=code
redirect_uri=redirect_uri
client_id=clientID
client_secret=clientSecret

Header内容如下

Content-Type=application/x-www-form-urlencoded
Cache-Control=no-cache
Authorization=authorization

生成authorization

url = {host}/com/v3/token

body = Form表单的map对象

privateKeyCert = base64格式的私钥证书

authorization = generateAuthHeader(url, body, "POST", "application/x-www-form-urlencoded", clientID, privateKeyCert, clientSecret)

函数generateAuthHeader的java如下

public static String generateAuthHeader(String url, Map<String, String> params, String method, String strContentType, String appId, String keyCertBse64, String appSecret) {

  try {

    String nonceValue = String.valueOf(SecureRandom.getInstance("SHA1PRNG").nextLong());

    String timestamp = String.valueOf(System.currentTimeMillis());

Map<String, String> defaultApexHeaders = new HashMap<>();

    defaultApexHeaders.put("app_id", appId);

    defaultApexHeaders.put("nonce", nonceValue);

    defaultApexHeaders.put("signature_method", "RS256");

    defaultApexHeaders.put("timestamp", timestamp);

if (method == "POST" && strContentType != "application/x-www-form-urlencoded") {

      params = new HashMap<>();

    }

Map<String, String> baseParams = new TreeMap<>();

    baseParams.putAll(params);

    baseParams.putAll(defaultApexHeaders);

    String baseParamsString = String.format("%s&%s&%s", method.toUpperCase(), url, NDICommon.map2String(baseParams));

    String privateKeyContent = new String(Base64Utils.decode(keyCertBse64));

    Signature signature = Signature.getInstance("SHA256withRSA");

    signature.initSign(KeyUtils.getRSAPrivateKey(privateKeyContent));

    signature.update(baseParamsString.getBytes());

    byte[] signedData = signature.sign();

    String finalSign = Base64Utils.encode(signedData);

String apexHeader = "PKI_SIGN timestamp=\"" + timestamp +
"\",nonce=\"" + nonceValue +
"\",app_id=\"" + appId +
"\",signature_method=\"RS256\"" +
",signature=\"" + finalSign +

          "\"";

    return apexHeader;

  } catch (Exception e) {

    Logger.error(e);

  }

  return StringUtils.EMPTY_STRING;

}

4、第三步中成功获取到的access_token不要马上使用,因为有个生效时间,一般等待几秒钟即可

5、根据myinfo的公钥证书从access_token中获取sub

java获取sub的代码如下

public static String verifyAndGet(String token, RSAPublicKey publicKey, String key) {

  try {

if (publicKey == null) {

      Logger.error("JWTUtils.verifyAndGet invalid public key");

      return null;

    }

    Algorithm algorithm = Algorithm.RSA256(publicKey, null);

    JWTVerifier verifier = JWT.require(algorithm).build();

    DecodedJWT jwt = verifier.verify(token);

    return jwt.getClaim(key).asString();

  } catch (Exception e) {

    Logger.error(e);

  }

  return null;

}

6、用sub和access_token获取用户信息,GET方法调用{host}/com/v3/person/{sub}/

Query参数:client_id={clientId}&attributes={请求的用户数据key,使用,间隔}

Header内容如下

Cache-Control=no-cache
Authorization={authorization},Bearer {access_token}

生成authorization

url = {host}/com/v3/person/{sub}/

body = Query参数的map对象

privateKeyCert = base64格式的私钥证书

authorization = generateAuthHeader(url, body, "GET", "", clientID, privateKeyCert, clientSecret)

最后解密返回的jwe数据就是用户信息了

String personJWT = JWEUtils.decrypt(jwe, KeyUtils.getRSAPrivateKey(privateKey));

return JWTUtils.verify(personJWT, KeyUtils.getRSAPublicKey(publicKey));

java代码如下

public static String decrypt(String jwe, RSAPrivateKey privateKey) {

  try {

if (privateKey == null) {

      Logger.error("JWEUtils.decrypt invalid private key");

      return null;

    }

    JWEObject jweObject = JWEObject.parse(jwe);

    JWEDecrypter decrypter = new RSADecrypter(privateKey);

    jweObject.decrypt(decrypter);

    return jweObject.getPayload().toString().replace("\"", "");

  } catch (Exception e) {

    Logger.error(e);

  }

  return null;

}

public static String verify(String token, RSAPublicKey publicKey) {

  try {

if (publicKey == null) {

      Logger.error("JWTUtils.verify invalid public key");

      return null;

    }

    Algorithm algorithm = Algorithm.RSA256(publicKey, null);

    JWTVerifier verifier = JWT.require(algorithm).build();

    DecodedJWT jwt = verifier.verify(token);

    return JsonUtils.toJson(parseJWT(jwt));

  } catch (Exception e) {

    Logger.error(e);

  }

  return null;

}

返回首页