收到Apple
的邮件,所有的APP
在6月30前要支持删除账号
的功能。如果用Apple ID
登录了APP
,那么在注销账号的同时还需要解除Apple ID
的授权,也就是Apple
说的Revoke Token
。这样Apple也知道这个账号已经被注销了,防止自动登录造成的安全问题.
写这篇文的主要原因是因为我用的是windows10系统,网上可参考的文献大多数都是苹果电脑,为了避免大家碰到相同的问题少走弯路,我把整个流程和要注意的点都写了出来
1.生成p8文件
在Apple developer
里面按下面的步骤生成一个p8文件
- 1、进入
Certificates, Identifiers & Profiles > Keys
,然后单击Keys
旁边左上角的+
号。 - 2、提供密钥名称并确保勾选
Sign In with Apple
。在这里,我们还必须单击Configure
。在接下来出现的Configure Key
面板中,选择我们之前在Choose a Primary App ID
下使用的App ID
,然后单击保存
。 - 3、单击
Continue
,然后在下一页中验证详细信息并单击Register
。 - 4、下载密钥并将其保存在安全的地方,因为您永远无法再次下载密钥。下载密钥后单击
Done
。
2.Ruby 安装 - Windows
下载地址: Downloads 根据自己的电脑选取版本,我选的是最新版x64,直接安装就好了,这部分没有难点,安装过程会稍微有一点久,安装完成后查看一下版本 ,如果显示版本号说明安装成功了
命令: ruby -v
安装完成后加载jwt ,之后编译.rb文件时会用到
命令: gem install jwt
在桌面新建一个文本文档,把下列代码粘贴进去
require "jwt"
key_file = "xxxxx.p8" #从Developer Center后台下载的key(p8后缀的文件)
team_id = "xxxxxx" #开发者账号的teamID
client_id = "com.xxx.xxx" #应用的BundleID
key_id = "xxxxxx" #从Developer Center后台key_id
validity_period = 180 #有效期
private_key = OpenSSL::PKey::EC.new IO.read key_file
token = JWT.encode(
{
iss: team_id,
iat: Time.now.to_i,
exp: Time.now.to_i + 86400 * validity_period,
aud: "https://appleid.apple.com",
sub: client_id
},
private_key,
"ES256",
header_fields=
{
kid: key_id
}
)
puts token
填写对应的信息后,将文件后缀改为.rb格式
,通过cmd运行.rb文件
在这里我运行时一直报错,改了几次还是不行,后来搜索了一下才明白,是文件夹的问题
把文件加改到C目录根目录下后,就可以了,这样我们就有了client_secret
获取token的API
接口:Generate and Validate Tokens
获取revoke的API
接口:Revoke Tokens
调用逻辑 先获取token 会返回 access_token 和refresh_token 我测试了一下,只要类型对应两种都可以,分别对应访问令牌无效和刷新令牌无效,我用的是access_token,通过access_token调用revoke如果返回200就成功了.
这部分注意在调用revoke请求时 要设置
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
下面是java部分代码,供大家参考
逻辑代码
@PostMapping("/appleRevoke")
@ResponseBody
public ResultVo appleRevoke(@RequestBody Map<String, String> paramMap) {
String tokenUrl = "https://appleid.apple.com/auth/token";
Map<String, String> tokenUrlParam = new HashMap<>();
tokenUrlParam.put("client_id", client_id);// 应用的BundleID
tokenUrlParam.put("client_secret", client_secret);//通过.rb文件返回
tokenUrlParam.put("code", paramMap.get("code"));//authorizationCode前端返回
tokenUrlParam.put("grant_type", "authorization_code");//固定字符
JSONObject tokenJsonObject = JSON.parseObject(HttpClientUtils.doPost(tokenUrl, tokenUrlParam, null));
String apple_token = tokenJsonObject.getString("access_token");
if(apple_token==null){
return new ResultVo(ResultVo.Status.OK,"apple_token获取失败");
}
String revokeUrl = "https://appleid.apple.com/auth/revoke";
Map<String, String> revokeParam = new HashMap<>();
revokeParam.put("client_id", client_id);
revokeParam.put("client_secret", client_secret);
revokeParam.put("token", apple_token);//tokenJsonObject 返回的 access_token
revokeParam.put("token_type_hint", "access_token");//固定字符
JSONObject revokeJsonObject = JSON.parseObject(HttpClientUtils.doPost(revokeUrl, revokeParam));
if(revokeJsonObject.getString("code").equals("200")){
return new ResultVo(ResultVo.Status.OK,"撤销令牌完成");
}
return new ResultVo(ResultVo.Status.OK,"撤销令牌失败");
}
http请求代码
import org.apache.http.client.methods.HttpPost;
@Slf4j
public class HttpClientUtils {
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"UTF-8");
Integer timeout = DEFALUT_TIMEOUT ;
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(timeout).setSocketTimeout(timeout).build();
httpPost.setConfig(requestConfig);
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_CREATED) {
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} else {
resultString=null;
}
} catch (Exception e) {
log.error("HttpClient doPost异常0"+e.getMessage(),e);
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url, Map<String, String> param,Integer timeout) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"UTF-8");
timeout = (timeout == null ? DEFALUT_TIMEOUT : timeout);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(timeout).setSocketTimeout(timeout).build();
httpPost.setConfig(requestConfig);
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
log.error("HttpClient doPost异常1"+e.getMessage(),e);
} finally {
try {
if(response!=null){
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
}
如果觉得有用,点个赞会让我觉得写这东西真的有人看.