Revoke tokens

前言

由于苹果新政策要求在2022年6月30日之后,使用Apple ID登录的账号注销时都要调用苹果的注销Api,趁这两天事情不是特别多就把注销功能给写了,顺便记录一下。

一:实现思路

官方文档: Revoke tokens

1.1. 苹果授权登录后,会返回authorizationCode这个授权码,利用授权码调用API来生成访问令牌(access_token)和刷新令牌(refresh_token)。
access_token:调用注销Api时所需的参数
refresh_token:调用刷新Token Api时所需的参数

access_token在这个时候用不上,因为是有有效期的,我们只需要在注销的时候重新获取access_token就可以了。这个时候要将refresh_token与这个用户进行绑定,可以将其存入数据库中,为了注销的时候可以用refresh_token来获取到新的access_token


1.2.使用refresh_token来更新新的access_token

1.3.用access_token调用 Revoke tokens Api

二:代码实现

 maven
  <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency>
常量类
public class Const {//client_id(应用ID)public static final String CLIENT_ID = "";//teamId(苹果开发人员帐户关联的10 个字符的团队ID)public static final String TEAM_ID = "";//Key Id (P8证书下载后可以得到keyId,跟苹果开发人员拿)public static final String KEY_ID = "";//keyValue(P8文件下载后可以得到keyValue,跟苹果开发人员拿)public static final String KEY_VALUE = "";//AUD(固定的)public static final String REVOKE_AUD = "https://appleid.apple.com";//生成或刷新token的URLpublic static final String AUTH_TOKEN_URL = "https://appleid.apple.com/auth/token";//撤销用户token请求urlpublic static final String AUTH_REVOKE_URL = "https://appleid.apple.com/auth/revoke";
}
苹果授权登录成功后,根据authorizationCode授权码来获取refreshToken,将refreshToken与我们的用户绑定,可以存到数据库中
     /*** 根据authorizationCode授权码来获取refreshToken*/public String generateToken(String authorizationCode) {String refreshToken = null;try {MultiValueMap<String, String> map = new LinkedMultiValueMap<>();map.add("client_id", Const.AUD);map.add("client_secret", generateClientSecret());map.add("grant_type", "authorization_code");map.add("code", authorizationCode);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);String value = restTemplate.postForEntity(Const.AUTH_TOKEN_URL, request, String.class).getBody();if (StringUtils.isNotBlank(value)) {JSONObject json = JSONObject.fromObject(value);refreshToken = json.getString("refresh_token");}} catch (HttpClientErrorException e) {throw new WZHException(e.getResponseBodyAsString());//WZHException是我自定义的异常类} catch (Exception e) {throw new WZHException(e.getMessage());}return refreshToken;}
client_secret秘钥的生成


     /*** 生成客户端密钥*/private static String generateClientSecret() throws Exception {Map<String, Object> header = new HashMap<>();header.put("kid", Const.KEY_ID); //P8文件下载得到的Key IDheader.put("alg", SignatureAlgorithm.ES256.getValue()); //SHA256Map<String, Object> claims = new HashMap<>();long now = new Date().getTime() / 1000;claims.put("iss", Const.TEAM_ID);claims.put("iat", now);claims.put("exp", now + 648000); // 最长半年,单位秒claims.put("aud", Const.REVOKE_AUD);claims.put("sub", Const.CLIENT_ID);PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(Const.KEY_VALUE));//P8文件下载得到的Key ValueKeyFactory keyFactory = KeyFactory.getInstance("EC");PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);return Jwts.builder().setHeader(header).setClaims(claims).signWith(SignatureAlgorithm.ES256, privateKey).compact();}
Revoke tokens的第一种方式:使用refreshToken进行注销
  public Response<String> revokeToken() {LoginUser loginUser = getFrontUser();//从缓存获取当前登录的信息try {User user = userService.getById(loginUser.getId());//获取用户的refreshTokenMultiValueMap<String, String> map = new LinkedMultiValueMap<>();map.add("client_id", Const.CLIENT_ID);map.add("client_secret", generateClientSecret());map.add("token_type_hint", "refresh_token");map.add("token", user.getRefreshToken());HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);ResponseEntity<String> values = restTemplate.postForEntity(Const.AUTH_REVOKE_URL, request, String.class);log.info("注销token成功");//Apple官网上请求只有200与400,这里没有抛异常,证明是成功注销的return Response.success("Revoke tokens success.");} catch (HttpClientErrorException e) {throw new WZHException(e.getResponseBodyAsString());} catch (Exception e) {throw new WZHException(e.getMessage());}}
Revoke tokens的第二种方式:使用refreshToken来刷新accessToken,再用accessToken来进行注销(个人推荐)
   /*** 根据refreshToken刷新accessToken*/private String refreshToken(String refreshToken) {String accessToken = null;try {MultiValueMap<String, String> map = new LinkedMultiValueMap<>();map.add("client_id", Const.CLIENT_ID);map.add("client_secret", generateClientSecret());map.add("grant_type", "refresh_token");map.add("refresh_token", refreshToken);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);String value = restTemplate.postForEntity(Const.AUTH_TOKEN_URL, request, String.class).getBody();if (StringUtils.isNotBlank(value)) {JSONObject json = JSONObject.fromObject(value);accessToken = json.getString("access_token");}} catch (HttpClientErrorException e) {throw new WZHException(e.getResponseBodyAsString(), 401);} catch (Exception e) {throw new WZHException(e.getMessage(), 401);}log.info("刷新token成功");return accessToken;}
  /*** 撤销用户token*/public Response<String> revokeToken() {LoginUser loginUser = getFrontUser();//从缓存获取当前登录的信息try {User user = userService.getById(loginUser.getId());String accessToken = refreshToken(user.getRefreshToken());//更新accessTokenMultiValueMap<String, String> map = new LinkedMultiValueMap<>();map.add("client_id", Const.CLIENT_ID);map.add("client_secret", generateClientSecret());map.add("token_type_hint", "access_token");map.add("token", accessToken);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);ResponseEntity<String> values = restTemplate.postForEntity(Const.AUTH_REVOKE_URL, request, String.class);log.info("注销token成功");//Apple官网上请求只有200与400,这里没有抛异常,证明是成功注销的return Response.success("Revoke tokens success.");} catch (HttpClientErrorException e) {throw new WZHException(e.getResponseBodyAsString());} catch (Exception e) {throw new WZHException(e.getMessage());}}
注意事项
  • Revoke tokens成功之后,数据库上用户绑定的refreshToken就无效了,如果再次用refreshToken去更新accessToken时,会抛{\"error\":\"invalid_grant\",\"error_description\":\"The token has expired or has been revoked.\"}异常,所以要根据公司具体的业务来,如果注销后可以重新登录的话,就在注销成功的时候将用户绑定的refreshToken清除掉,然后下次登录的时候重新利用authorizationCode来生成refreshToken

  • revokeToken时,只要不抛异常,就证明是成功的操作,如果accessToken是错的,苹果的Api也会返回成功,因为苹果会认为这是被注销过的,所以我们的只要确保accessToken的值是没问题的就可以了

  • Apple账户修改了密码之后,refreshToken的值也会失效,所以在注销的时候,如果发现refreshToken已经失效了,得将用户绑定的refreshToken进行清除掉,同时得有个动作让用户进行重新授权来更新refreshToken。

苹果注销功能实现 Revoke tokens(JAVA)相关推荐

  1. Sign in with Apple REST API / Revoke tokens (JAVA)

    Sign in with Apple REST API / Revoke tokens (JAVA) 前言 由于Apple政策, Apple ID登录的用户注销时需要进行Revoke Token. 顺 ...

  2. Spring Cloud 架构设计之苹果Apple账户注销 Revoke tokens auth/revoke

    Spring Cloud 架构设计之苹果Apple账户注销 Revoke tokens auth/revoke 前言 近期,本人在开发一款互联网产品,项目地址https://github.com/yj ...

  3. java实现注销功能_8.6.2 登录注销功能的实现

    8.6.2  登录注销功能的实现 9.6.1节介绍了登录页面的搭建,本节将介绍如何实现登录的业务功能,包括Servlet端的业务逻辑和数据库端的用户名和密码验证等. 在开发一个Servlet之前,需要 ...

  4. 「小程序JAVA实战」小程序我的个人信息-注销功能(42)

    转自:https://idig8.com/2018/09/06/xiaochengxujavashizhanxiaochengxuwodegerenxinxi-zhuxiaogongneng40/ 注 ...

  5. java注销对话框_【java小程序实战】小程序注销功能实现

    小程序实战中,如何实现程序的注销功能呢?后端代码只要删除用户的redi缓存即可.小程序端在成功返回消息后,进行登陆页面的跳转. 文章目录 小程序的mine.wxml代码 mine.wxss代码 注销事 ...

  6. 【java小程序实战】小程序注销功能实现

    小程序实战中,如何实现程序的注销功能呢?后端代码只要删除用户的redi缓存即可.小程序端在成功返回消息后,进行登陆页面的跳转. 文章目录 小程序的mine.wxml代码 mine.wxss代码 注销事 ...

  7. 这可能是史上功能最全的Java权限认证框架!

    点击关注公众号,Java干货及时送达 今天给大家推荐的这个开源项目超级棒,可能是史上功能最全的 Java 权限认证框架! 这个开源项目就是:sa-token . Sa-Token是什么? sa-tok ...

  8. iOS13苹果登录的后台验证token(JAVA)

    最近随着iOS的更新,苹果要求含有第三方登录的app必须实现苹果登录功能,在查询相关资料后整合进自己的项目中,再次记录下,也供大家借鉴. 以下是大致流程,挺简单的: 首先引入解析jwt的包: < ...

  9. 绿豆APP源码苹果CMS影视插件版本原生JAVA源码

    纯净原版绿豆APP源码苹果CMS影视插件版本原生JAVA源码,这套绿豆源码相当完美运行非常流畅,纯原汁原味的绿豆UI,不是市面上被改的乱七八糟的版本,其实大家也清楚网上卖几百的也只是换个底图换个图标而 ...

最新文章

  1. 万字详解,JDK1.8新特性的Lambda、Stream和日期的使用详解
  2. 在JavaScript函数中定义全局变量
  3. 无聊写的一个PHP Socket类
  4. [POJ3254]Corn Fields
  5. 在数据库中如果组合主键(假设为stuID和stuName)存在则更新,不存在则新增
  6. vs2010无法添加dll引用
  7. ubuntu虚拟显示器远程连接桌面方案
  8. 下载网页视频 下载网页音乐 一般视频音频和m3u8均可
  9. android 放大镜功能,利用Android实现一个放大镜功能
  10. 电子凸轮应用追剪算法详细图解(附PLC完整源代码)
  11. 2023年全国最新工会考试精选真题及答案36
  12. php cli python,PHP MVC框架 CodeIgniter CLI模式简介
  13. 双十一要不要提前收货
  14. 惠普linux进入bios设置u盘启动,如何进入bios设置,手把手教你惠普如何进入bios设置u盘启动...
  15. 【报告分享】中国消费者洞察报告-领航前所未有(附下载)
  16. 一定要注意网站图片版权问题!
  17. 华师大 OJ 3055
  18. 你还在为无法完美卸载SQL Server 2008 R2而烦恼吗?
  19. 【简书 DC谢老师】JMeter + jenkins + SVN 接口自动化之简单 demo​​​​​​​
  20. 重庆师范计算机录取分数线,重庆师范大学历年录取分数线

热门文章

  1. Openmv云台寻找最大色块
  2. 二自由度自动进样器动作序列实现
  3. 百亿条数据复杂业务场景下通用归因模型设计实现
  4. 履历表范例 电脑程序员
  5. Qt+OpenGL——3D坐标转2D坐标
  6. linux 桌面时钟
  7. 基于SSM的在线视频网站的设计与实现
  8. 体育摄影中快速对焦的技巧
  9. Android面试题基础集锦《一》
  10. 啥是佩奇?用Python画给你看!