import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwk.Jwk;
import com.helijia.appuser.modules.user.vo.AppleCredential;
import com.helijia.common.api.model.ApiException;
import com.helijia.user.util.HttpClientUtil;
import com.helijia.user.util.UserCode;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.apache.commons.codec.binary.Base64;
import java.security.PublicKey;
import java.util.Map;
/**
* Created by laohu May 18 2020
*/
@Service
public class AppleService {
private static final Logger LOGGER = LoggerFactory.getLogger(AppleService.class);
private static final String AUTH_URL = "/auth/keys";
@Value("${apple.login.auth.url}")
public String appleUrl;
public void verify(AppleCredential credential) {
try {
String[] identityTokens = credential.getIdentityToken().split("\\.");
Map firstDate = JSONObject
.parseObject(new String(Base64.decodeBase64(identityTokens[0]), "UTF-8"));
Map secondData = JSONObject
.parseObject(new String(Base64.decodeBase64(identityTokens[1]), "UTF-8"));
String kid = String.valueOf(firstDate.get("kid"));
String aud = String.valueOf(secondData.get("aud"));
String sub = String.valueOf(secondData.get("sub"));
// 1.验证apple官方 2.sub和openId的一致性
if (verify(credential.getIdentityToken(), kid, aud, sub)) {
if(StringUtils.isEmpty(credential.getOpenId())) {
credential.setOpenId(sub);
}
return;
}
} catch (Exception e) {
LOGGER.error("apple identityToken verify error", e);
}
throw new ApiException(UserCode.USER_THIRD_ACCESSTOKEN_FAIL.getApiCode(),
UserCode.USER_THIRD_ACCESSTOKEN_FAIL.getApiMessage());
}
/**
* 验证
*
* @param identityToken APP获取的identityToken
* @param aud
* @param sub
* @return true/false
*/
private boolean verify(String identityToken, String kid, String aud, String sub) {
PublicKey publicKey = getPublicKey(kid);
JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
jwtParser.requireIssuer(appleUrl);
jwtParser.requireAudience(aud);
jwtParser.requireSubject(sub);
try {
Jws claim = jwtParser.parseClaimsJws(identityToken);
if (claim != null && claim.getBody().containsKey("auth_time")) {
return true;
}
return false;
} catch (ExpiredJwtException e) {
LOGGER.error("apple identityToken expired", e);
return false;
} catch (Exception e) {
LOGGER.error("apple identityToken illegal", e);
return false;
}
}
/**
* @param kid 通过kid获取对应的PublicKey
* {
* "keys": [
* {
* "kty": "RSA",
* "kid": "86D88Kf",
* "use": "sig",
* "alg": "RS256",
* "n": "iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ",
* "e": "AQAB"
* },
* {
* "kty": "RSA",
* "kid": "eXaunmL",
* "use": "sig",
* "alg": "RS256",
* "n": "4dGQ7bQK8LgILOdLsYzfZjkEAoQeVC_aqyc8GC6RX7dq_KvRAQAWPvkam8VQv4GK5T4ogklEKEvj5ISBamdDNq1n52TpxQwI2EqxSk7I9fKPKhRt4F8-2yETlYvye-2s6NeWJim0KBtOVrk0gWvEDgd6WOqJl_yt5WBISvILNyVg1qAAM8JeX6dRPosahRVDjA52G2X-Tip84wqwyRpUlq2ybzcLh3zyhCitBOebiRWDQfG26EH9lTlJhll-p_Dg8vAXxJLIJ4SNLcqgFeZe4OfHLgdzMvxXZJnPp_VgmkcpUdRotazKZumj6dBPcXI_XID4Z4Z3OM1KrZPJNdUhxw",
* "e": "AQAB"
* }
* ]
* }
* @return 构造好的公钥
*/
private PublicKey getPublicKey(String kid) {
try {
String str = HttpClientUtil.get(appleUrl + AUTH_URL);
JSONObject data = JSONObject.parseObject(str);
JSONArray jsonArray = data.getJSONArray("keys");
if(jsonArray.isEmpty()) {
return null;
}
for (Object object : jsonArray) {
JSONObject json = ((JSONObject)object);
if(json.getString("kid").equals(kid)) {
json = ((JSONObject)object);
Jwk jwa = Jwk.fromValues(json);
return jwa.getPublicKey();
}
}
} catch (final Exception e) {
LOGGER.error("apple getPublicKey error", e);
}
return null;
}
}