苹果联合登录
不知道为啥苹果提供的联合登录竟然连demo都没有,你有个小例子也好啊。参阅了很多前辈的文章,怀着坎坷的心情,猜,懵,最终调试ok并跑起来。直接贴代码。大半夜写的文章,希望你们喜欢,喜欢的话点个赞。
说下实现的过程:
A密钥的生成 参考https://www.jianshu.com/p/cbcb79067451,不知道为什么我机器死活不行,别人按照相同的步骤就可以。
B 苹果业务流程,方案一,和方案二,可以分别使用来获取到苹果的openid,
方案一code 获取token
前端往后端传递useriid就是苹果的的用户openid,你只需要检验token是否合法就可以。
方案二 获取公钥去解析返回的token,这是解析jwt的方式。前期正常,后期发现,多次请求只有一次成功,不知道苹果服务器官方做出了什么变化。特此说明下。你也可以试试。代码参考下面的。
参考截图
具体代码
package com.secoo.user.oauth.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwk.InvalidPublicKeyException;
import com.auth0.jwk.Jwk;
import com.secoo.user.bean.OAuthTokenInfo;
import com.secoo.user.bean.OAuthUserInfo;
import com.secoo.user.bean.Result;
import com.secoo.user.config.AppDiabloConfig;
import com.secoo.user.dto.OAuthRequestDto;
import com.secoo.user.oauth.AppletIOAuth2Service;
import com.secoo.user.util.HttpUtil;
import io.jsonwebtoken.*;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.security.PublicKey;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component("appleService")
public class AppletOAuth2ServiceImplimplements AppletIOAuth2Service {
public static StringtokenUrl ="https://appleid.apple.com/auth/token";
public static StringpublicUrl ="https://appleid.apple.com/auth/keys";
private static Loggerlogger = LoggerFactory.getLogger(AppletOAuth2ServiceImpl.class);
@Autowired
AppDiabloConfigappDiabloConfig;
/**
* 苹果请求验证token
*
* @param map
* @return
*/
@Override
public String postAppleToken(Map map) {
Map form =new HashMap();
form.put("client_id",appDiabloConfig.getAppletClientId());//从配置文件获取
form.put("client_secret",appDiabloConfig.getAppletClientIdSecret());//从配置文件获取
form.put("grant_type","authorization_code");
form.put("code", (String)map.get("code"));
String appResult="";
try {
appResult= HttpUtil.doPost(tokenUrl, form);
// HttpResponse result = client.excutePost(tokenUrl, form);
}catch (Exception e) {
logger.error("applet token request faild:"+map.get("ccode"), e.getMessage());
}
return appResult;
}
/**
* 苹果请求公钥
*
* @return
*/
@Override
public PublicKey getPublic(String code) {
String appResult="";
PublicKey publicKey=null;
try {
appResult= HttpUtil.doGet(publicUrl,"utf-8",2000);
}catch (Exception e) {
logger.error("applet public sec request faild", e.getMessage());
}
if(StringUtils.isNotEmpty(appResult)) {
Map maps = (Map) JSON.parse(appResult);
List keys = (List) maps.get("keys");
Map o = (Map) keys.get(0);
Jwk jwa = Jwk.fromValues(o);
try {
publicKey = jwa.getPublicKey();
}catch (InvalidPublicKeyException e) {
logger.error("applet public sec request faild code:"+code, e.getMessage());
}
}
return publicKey;
}
/**
* 解析token,返回的结果
*
* @param jwtString
* @return
*/
@Override
public Map verifyTokenJWT(String jwtString,PublicKey key,String code) {
//获取主体
Map map=getSubByJwt(jwtString);
if(map==null){
logger.info("getSubByJwt is null code:"+code);
return null;
}
Map back=null;
final JwtParser jwtParser = Jwts.parser().setSigningKey(key);
jwtParser.requireIssuer("https://appleid.apple.com");
jwtParser.requireAudience((String) map.get("aud"));
jwtParser.requireSubject((String)map.get("sub"));
try {
final Jws claim = jwtParser.parseClaimsJws(jwtString);
logger.info("verifyTokenJWT code:"+code+" result:"+(claim==null?"null":JSON.toJSONString(claim)));
if (claim !=null && claim.getBody().containsKey("sub")) {
back=new HashMap();
back.put("sub", claim.getBody().get("sub"));
}
}catch (final ExpiredJwtException e) {
logger.warn("apple decry jwt error code:"+code,e);
}catch (final Exception e) {
logger.warn("apple decry jwt error code:"+code,e);
}
return back;
}
/**
* 通过jwt获取主体和签名
* @param jwtString
* @return
*/
public Map getSubByJwt(String jwtString) {
String jwt = jwtString;
String payload = jwt.split("\\.")[1];
String sub ="";
String aud ="";
Map result =null;
final Base64.Decoder decoder = Base64.getDecoder();
final Base64.Encoder encoder = Base64.getEncoder();
String decode =null;
try {
decode =new String(decoder.decode(jwt.split("\\.")[1]),"UTF-8");
String substring = decode.substring(0, decode.indexOf("}") +1);
JSONObject jsonObject = JSON.parseObject(substring);
sub = jsonObject.getString("sub");
aud = jsonObject.getString("aud");
if (StringUtils.isNotEmpty(sub) && StringUtils.isNotEmpty(aud)) {
result =new HashMap();
result.put("sub", sub);
result.put("aud", aud);
}
}catch (UnsupportedEncodingException e) {
logger.info("getSubByJwt faild", e.getMessage());
}
return result;
}
/**
* 第三方标识
*/
@Override
public Integer getSourceId() {
return 11;
}
/**
* 获取第三方AccessToken信息
*
* @param reqTokenDto
* @return
*/
@Override
public Result getAccessTokenInfo(OAuthRequestDto reqTokenDto) {
return null;
}
/**
* 通过accessToken获取第三方用户信息
*
* @param tokenInfo
* @return
*/
@Override
public Result getUserInfo(OAuthTokenInfo tokenInfo) {
return null;
}
/**
* 获取第三方用户信息
*
* @param reqTokenDto
* @return
*/
@Override
public Result getUserInfo(OAuthRequestDto reqTokenDto) {
return null;
}
/**
* 获取缓存中第三方用户信息
*
* @param reqTokenDto
* @return
*/
@Override
public Result getCacheUserInfo(OAuthRequestDto reqTokenDto) {
return null;
}
/**
* 删除缓存中第三方用户信息
*
* @param reqTokenDto
* @return
*/
@Override
public void delCacheUserInfo(OAuthRequestDto reqTokenDto) {
}
}
pom文件
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-nio</artifactId>
<version>4.4.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
<version>4.1.2</version>
</dependency>
欢迎大家联系我,主要关注中间件底层的设计和实现,生活资讯,大家一起分享。