错误:无效的客户端 - 使用 Spring Boot 使用 Apple 登录
Posted
技术标签:
【中文标题】错误:无效的客户端 - 使用 Spring Boot 使用 Apple 登录【英文标题】:Error: Invalid Client - Sign In With Apple with Spring Boot 【发布时间】:2020-07-27 13:22:50 【问题描述】:我在尝试用我的授权代码交换刷新令牌时收到 400 : ["error":"invalid_client"]
错误。我尝试了所有方法并仔细检查了很多次。
我正在使用this 库来生成客户端密码。下面我使用示例密钥来演示该问题。
有什么问题?
Apple 客户端密钥生成器
public class AppleClientSecretGenerator
private String team_id;
private String sub;
private String key_id;
private long expiration_time;
private String secret_key;
private String aud;
public AppleClientSecretGenerator()
super();
this.team_id = "***********";
this.sub = "com.example.app";
this.key_id = "**********";
this.expiration_time = 200000;
this.secret_key = "MIGTAFKMN23SFF2SFGSM49AgEGCCqGSM49AwEHBHdfDSFFDe09hGVEu5sesMNNF" +
"pet8GJDZIL0inL4oxgIJNF0i3Q8MYKOgsdCgYIKoZIzj0DAQehRANCAAQhAVyKVrFGWEw+" +
"gkWyeQNxopjG30iF56DXM0QfqwbffKmsdPkjfe3FKDDFyDYmk+XZM4qj6aIZKLy" +
"KLM4Nd23";
this.aud = "https://appleid.apple.com";
public String generate()
String jws = "";
try
byte[] keyBytes = Base64.getDecoder().decode(secret_key);
//byte[] keyBytes = Files.readAllBytes(Paths.get(filename));
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PrivateKey key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.ES256;
long nowSeconds = System.currentTimeMillis();
Date now = new Date(nowSeconds);
long expMillis = nowSeconds + expiration_time;
Date exp = new Date(expMillis);
jws = Jwts.builder()
.setHeaderParam("kid", key_id)
.setIssuedAt(now)
.setSubject(sub)
.setIssuer(team_id)
.setAudience(aud)
.setExpiration(exp)
.signWith(key, signatureAlgorithm)
.compact();
catch (JwtException e)
//don't trust the JWT!
System.out.println("Error: " + e.getMessage());
catch (NoSuchAlgorithmException e)
// TODO Auto-generated catch block
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
catch (InvalidKeySpecException e)
// TODO Auto-generated catch block
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
System.out.println("Client Secret: " + jws);
return jws;
授权请求
public class AuthorizationRequest
private String client_id;
private String client_secret;
private String code;
private String grant_type;
private String redirect_uri;
public AuthorizationRequest(String client_secret, String code)
super();
this.client_id = "com.maxtrauboth.BopdropSwiftUI";
this.client_secret = client_secret;
this.code = code;
this.grant_type = "authorization_code";
this.redirect_uri = "http://mylocaladdr.test/";
// Getters and Setters here
验证用户
@PostMapping("verify")
public UserCredentials verify(@RequestBody UserCredentials credentials)
Jwk jwk;
Algorithm algorithm;
// Validate identity token
DecodedJWT jwt = JWT.decode(credentials.getToken());
JwkProvider provider = new UrlJwkProvider("https://appleid.apple.com/auth/keys");
try
jwk = provider.get(jwt.getKeyId());
algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
algorithm.verify(jwt);
// Check expiration
if (jwt.getExpiresAt().before(Calendar.getInstance().getTime()))
throw new RuntimeException("Expired token!");
// Create client_secret
AppleClientSecretGenerator generator = new AppleClientSecretGenerator();
String client_secret = generator.generate();
// Refreshing the token by sending the authorization code
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_FORM_URLENCODED));
headers.set("User-Agent", "Mozilla/5.0 Firefox/26.0");
System.out.println("Authorization Code: " + credentials.getAuthorization_code());
AuthorizationRequest request = new AuthorizationRequest(client_secret, credentials.getAuthorization_code());
HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers);
RestTemplate restTemplate = new RestTemplate();
// send POST request
TokenResponse response = restTemplate.postForObject("https://appleid.apple.com/auth/token", entity, TokenResponse.class);
// do some database work here
catch (JwkException e)
// error
catch (IllegalArgumentException e)
// error
// sending the token back to the user
return credentials;
生成的客户端密码
"kid": "*********",
"alg": "ES256"
"iss": "*********",
"iat": 1587930164,
"exp": 1587930364,
"aud": "https://appleid.apple.com",
"sub": "com.example.app"
【问题讨论】:
【参考方案1】:发布时间和到期时间应该以秒为单位,而不是毫秒。
【讨论】:
好提示!但是,我仍然收到此错误。我更新了上面的代码。 您介意显示您的客户密码字符串吗?尝试将其粘贴到 jwt.io 中,并查看解码后的客户端密码是否正确 哇,将它粘贴到 jwt.io 后,iat
和 exp
具有相同的值 1587928
。这怎么可能?
嗯,我认为现在不需要除以 1000,尝试添加 *1000 作为 exp 时间,即 exp = now + 5*1000
参考developer.okta.com/blog/2018/10/31/jwts-with-java【参考方案2】:
我没有找到问题的原因。但是,我按照here 中提到的确切步骤解决了这个问题。
【讨论】:
以上是关于错误:无效的客户端 - 使用 Spring Boot 使用 Apple 登录的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security CAS:在 login.jsp 上显示客户端错误
使用 Spring Boot 的 starter web parent 时出现无效的打包错误