至2022年6月30日之后,苹果app中有apple账号登录的,必须要有注销接口,也就是说必须得调用苹果的账号注销接口
为了让大家也能快速的调通,我决定将我的代码以及思路分析出来,首先我是做后端开发的 语言使用的是 JAVA
思路:
首先要和ios开发那边沟通那些参数他传给后端,当然自己要先知道自己需要什么,这个很重要,先看这个文档参数
参数 解析:
需要注意:content-type 的类型为 Content-Type: application/x-www-form-urlencoded
client_id:这个参数就是Bundle Identifier ,如果你和我一样是后端开发并不知道你就直接问ios开发他肯定知道
client_secret: 这个参数比较麻烦一点点,这个参数我是使用p8 文件 生成的密钥具体实现
token:这个参数需要ios端再让用户授权获取到code 传到后端,后端在使用这个code去请求 这个接口
https://appleid.apple.com/auth/token
文档地址: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
这个接口我们传四个参数 分别是
client_id
: Bundle Identifier 一样是填这个
`client_secret : 这个参数和上面说的一样,代码最后附上
code
: 让ios开发者传给你,需要注意的是这个code 五分钟过期
grant_type
:传authorization_code这个就完事了
最后拿到返回结果中的:access_token 就是我们要的token
{
"access_token": "a08e84f841fd941524.0.rwvx.yplgeDRaP3F-v5Ek9-7QOA",
"token_type": "Berer",
"expires_in": 3600,
"refresh_token": "r81edbac8b9bd482e1.0.rwvx.M9KauvbR09JrzKa8WZ83Lw",
"id_token": "eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLnl1Y2hlbmcuc21hcnRoZWFsdGgiLCJleHAiOjE2NTU4Njc2MzAsImlhdCI6MTY1NTc4MTIzMCwic3ViIjoiMDAwNjU3LmE2YjAxMjU0MWNiNDRjYjZiNmVjMjBkYjRkYWIwNDEwLjA3NDUiLCJhdF9oYXNoIjoiNVVBQXFKUmFvX0JodzJXdS13ak1odyIsImF1dGhfdGltZSI6MTY1NTc4MDk4NCwidHJhbnNmZXJfc3ViIjoiMDAwNjU3LnI5Y2E1NjZkYzQ0YjI0NTJkYTRmM2I5YjRiOTI4OTc0YiIsIm5vbmNlX3N1cHBvc"
}
最后一个参数
token_type_hint:填access_token
我写的代码:
/**
* 获取p8文件中的内容
*
* @param clientType
* @return
*/
private static Key getPrivateKey(String clientType) {
try {
File file = new File(p8FilePat + clientType + ".p8");
BufferedReader br = new BufferedReader(new FileReader(file));
String string = null;
StringBuffer sb = new StringBuffer();
while ((string = br.readLine()) != null) {
if (string.startsWith("---")) {
continue;
}
sb.append(string);
}
br.close();
KeyFactory factory = KeyFactory.getInstance("EC");
EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64(sb.toString().replaceAll("\\n", "")));
return factory.generatePrivate(keySpec);
} catch (FileNotFoundException e) {
log.error("not find p8 file !=>{}", e.getMessage());
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 私钥加密后给苹果去验证,构造clientSecret,就是构造一个jwt字符串
* [获取私钥]
*
* @param iss team_id
* @param sub client_id
* @param kid access_token
* @return
*/
public static String buildJwt(String iss, String sub, String kid) {
Map<String, Object> header = new HashMap<>();
header.put("alg", SignatureAlgorithm.ES256.getValue()); //SHA256withECDSA
header.put("kid", kid);
long iat = System.currentTimeMillis() / 1000; //以秒为单位
Map<String, Object> claims = new HashMap<>();
claims.put("iss", iss);// apple开发组id 问ios开发要
claims.put("iat", iat);
claims.put("exp", iat + 180 * 3600); //设置过期时间
claims.put("aud", "https://appleid.apple.com"); //固定值
claims.put("sub", sub);// Bundle Identifier
return new DefaultJwtBuilder().setHeader(header).setClaims(claims).signWith(SignatureAlgorithm.ES256, getPrivateKey(sub)).compact();
}
/**
* 用户授权获取
*
* @param code
* @param clientId
* @return
*/
public static String getAuthToken(String code, String clientId) {
if (code.isEmpty() || clientId.isEmpty()) {
return null;
}
String kid = "";
// TODO 判断是那个app teamId 等到时候app迁移到另一个公司账号再做判断
if ("com.***.***".equals(clientId)) {
kid = Constants.APPLE_K_ID_SMARTHEALTH;
} else if ("com.***.***".equals(clientId)) {
kid = Constants.APPLE_K_ID_SMARTHEALTH_PRO;
}
if ("".equals(kid)) {
log.error("apple getAuthToken 没有对应的app信息");
return null;
}
JSONObject jsonObject = null;
try {
privateKeyStr = buildJwt(Constants.APPLE_TEAM_ID, clientId, kid);
Map<String, String> stringStringHashMap = new HashMap<>();
stringStringHashMap.put("client_id", clientId);
stringStringHashMap.put("client_secret", privateKeyStr);
stringStringHashMap.put("code", code);
stringStringHashMap.put("grant_type", "authorization_code");
String res = HttpRequest.post(Constants.APPLE_AUTH_TOKENS_URL).contentType("application/x-www-form-urlencoded").query(stringStringHashMap).send().body();
jsonObject = JSON.parseObject(res);
} catch (Exception e) {
throw new RuntimeException(e);
}
return jsonObject.get("access_token").toString();
}
public static Boolean appleRevoke(String code, String clientId) {
if (clientId.isEmpty()) {
return false;
}
try {
String authToken = getAuthToken(code, clientId);
Map<String, String> requestMap = new HashMap<>();
requestMap.put("client_id", clientId);
requestMap.put("client_secret", privateKeyStr);
requestMap.put("token", authToken);
requestMap.put("token_type_hint", "access_token");
System.out.println(requestMap);
HttpResponse send = HttpRequest.post(Constants.APPLE_REVOKE_TOKENS_URL)
.contentType("application/x-www-form-urlencoded")
.query(requestMap)
.send();
int statusCode = send.statusCode();
if (statusCode != 200) {
JSONObject jsonObject = JSON.parseObject(send.body());
String error = jsonObject.get("error").toString();
System.out.println("error --------- " + error);
if (!error.isEmpty()) {
return false;
}
}
} catch (Exception e) {
log.error("revoke token error");
return false;
}
return true;
}
我这个调试代码,为了兼容多个app所以有些参数改成了动态的
写这篇文章呢
1、是为了记录一下
2、是方便大家,给大家点思路
3、我不是经常写所以并不是写的很好理解一下啊
有什么不懂的留言 谢谢支持!!!