以下示例代码、token是由移动端获取传给后端,后端解析验证即可
package com.sanxin.customer.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwk.Jwk;
import io.jsonwebtoken.*;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.util.Base64;
import java.util.Map;
/**
* @Classname IOSToeknUtils
* @Description IOS token操作工具
* @Date 2023/03/23
* @Created by Goden
*/
public class IOSToeknUtils {
private final static String authUrl = "https://appleid.apple.com/auth/keys";
private final static String authIss = "https://appleid.apple.com";
/**
* 解码identityToken
* @param identityToken
* @return
*/
public static JSONObject parserIdentityToken(String identityToken) {
String[] arr = identityToken.split("\\.");
String firstDate = new String(Base64.getDecoder().decode(arr[0]), StandardCharsets.UTF_8);
String decode = new String(Base64.getDecoder().decode(arr[1]), StandardCharsets.UTF_8);
JSONObject claimObj = JSON.parseObject(decode);
// 将第一部分获取到的kid放入消息体中,方便后续匹配对应的公钥使用
claimObj.put("kid", JSONObject.parseObject(firstDate).get("kid"));
return claimObj;
}
/**
* 根据kid获取对应的苹果公钥
* @param kid
* @return
*/
public static PublicKey getPublicKey(String kid) {
try {
RestTemplate restTemplate = new RestTemplate();
JSONObject data = restTemplate.getForObject(authUrl, JSONObject.class);
assert data != null;
JSONArray jsonArray = data.getJSONArray("keys");
for (Object obj : jsonArray) {
Map json = ((Map) obj);
// 获取kid对应的公钥
if (json.get("kid").equals(kid)) {
Jwk jwa = Jwk.fromValues(json);
return jwa.getPublicKey();
}
}
} catch (Exception e) {
}
return null;
}
/**
* 对前端传来的identityToken进行验证
*
* @param identityToken
* @param jsonObject
* @return
* @throws Exception
*/
public static Boolean verifyExc(String identityToken, JSONObject jsonObject) throws Exception {
String kid = (String) jsonObject.get("kid");
PublicKey publicKey = getPublicKey(kid);
JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
jwtParser.requireIssuer(authIss);
jwtParser.requireAudience((String) jsonObject.get("aud"));
jwtParser.requireSubject((String) jsonObject.get("sub"));
try {
Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken);
if (claim != null && claim.getBody().containsKey("auth_time")) {
return true;
}
return false;
} catch (ExpiredJwtException e) {
return false;
} catch (Exception e) {
return false;
}
}
}
以下测main方法
package com.sanxin.customer.util;
import com.alibaba.fastjson.JSONObject;
/**
* @Classname TestIOSLogin
* @Description 测试IOS登录
* @Date 2023/3/23
* @Created by Goden
*/
public class TestIOSLogin {
public static void main(String[] args) {
// 请求的JWT
String identityToken = "eyJraWQiOiJmaDZCczhDIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoib3JnLmNvY29hcG9kcy5kZW1vLlNYU2Fhc0xvZ2luLUV4YW1wbGUiLCJleHAiOjE2ODI2NzA1MDQsImlhdCI6MTY4MjU4NDEwNCwic3ViIjoiMDAxMDgwLmVkODFkN2YwYzc0MjQ5YjU4NTZjNmQ4MmY4MDg1ODI0LjA4MjgiLCJjX2hhc2giOiI4NXBTVGhtc0pQdmFjTlBrSHlHZWRBIiwiZW1haWwiOiJzeDEzMjg4ODg3NjA4QDE2My5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJhdXRoX3RpbWUiOjE2ODI1ODQxMDQsIm5vbmNlX3N1cHBvcnRlZCI6dHJ1ZSwicmVhbF91c2VyX3N0YXR1cyI6Mn0.bqtZMM-CYIIQKDsNZMjUwRVx6tzSG3lkIerwFzbEDOOVnNXTD5Cz8Vh_GIrbspDwcCEDh1AsPpEwO_gkerYDDK7gY74RRywJr0iZANEUHNC7rpnMpw7IXDden2Hz6PTskHr5nYSd5ddUHCKr4JiltVGDJ7hJOzmpPmQKdq6zyItLGaaPlaTgPqoJoc9n1dGC4wtYX61tGw8TVL5Ozaa9fqxFnkSZBI6127zLOitncuU2HYEbO_ET_1HBQad0kaH6ytAvRjd1dDgv9UOSlqBHzQv2klgrRpiMr0Yqi3bX9YjBfhkppytLgee1vtpwxnGfH1NIvQ4d1JQBeVNER2PVeQ";
// 解码后的消息体
JSONObject playloadObj = IOSToeknUtils.parserIdentityToken(identityToken);
System.out.println(playloadObj);
Boolean success;
try {
success = IOSToeknUtils.verifyExc(identityToken, playloadObj);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!success) {
// TODO 校验token失败具体操作
return;
}
// TODO 检验token成功具体业务操作。。。
}
}