C# Sign In With Apple苹果登陆后端验证
苹果App授权登录
苹果官方的授权文档:
生成Token:https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
JWT:https://developer.apple.com/documentation/sign_in_with_apple/fetch_apple_s_public_key_for_verifying_token_signature
苹果的授权登录
APP内苹果授权登陆会提供如下几个参数:userID、email、fullName、authorizationCode、identityToken
userID:授权的用户唯一标识
email、fullName:授权的用户资料
authorizationCode:授权code
identityToken:授权用户的JWT凭证
针对后端验证苹果提供了两种验证方式,一种是基于JWT的算法验证,另外一种是基于授权码的验证
JWT验证
identityToken参考:
// jwt 格式
eyJraWQiOiJBSURPUEsxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLnNreW1pbmcuZGV2aWNlbW9uaXRvciIsImV4cCI6MTU2NTY2ODA4NiwiaWF0IjoxNTY1NjY3NDg2LCJzdWIiOiIwMDEyNDcuOTNiM2E3OTlhN2M4NGMwY2I0NmNkMDhmMTAwNzk3ZjIuMDcwNCIsImNfaGFzaCI6Ik9oMmFtOWVNTldWWTNkcTVKbUNsYmciLCJhdXRoX3RpbWUiOjE1NjU2Njc0ODZ9.e-pdwK4iKWErr_Gcpkzo8JNi_MWh7OMnA15FvyOXQxTx0GsXzFT3qE3DmXqAar96nx3EqsHI1Qgquqt2ogyj-lLijK_46ifckdqPjncTEGzVWkNTX8uhY7M867B6aUnmR7u-cf2HsmhXrvgsJLGp2TzCI3oTp-kskBOeCPMyTxzNURuYe8zabBlUy6FDNIPeZwZXZqU0Fr3riv2k1NkGx5MqFdUq3z5mNfmWbIAuU64Z3yKhaqwGd2tey1Xxs4hHa786OeYFF3n7G5h-4kQ4lf163G6I5BU0etCRSYVKqjq-OL-8z8dHNqvTJtAYanB3OHNWCHevJFHJ2nWOTT3sbw // header 解码
{"kid":"AIDOPK1","alg":"RS256"} 其中kid对应上文说的密钥id // claims 解码
{
"iss":"https://appleid.apple.com",
"aud":"com.skyming.devicemonitor",
"exp":,"iat":,
"sub":"001247.93b3a799a7c84c0cb46cd08f100797f2.0704",
"c_hash":"Oh2am9eMNWVY3dq5JmClbg",
"auth_time":
} ss标识是苹果签发的,aud是接收者的APP ID,sub就是用户的唯一标识
解析的sub和前端传过来的比较是否一致;
基于授权码的后端验证
创建Secret
private string CreateSecret()
{ var handler = new JwtSecurityTokenHandler();
var subject = new Claim("sub", Client_Id);//找IOS要
var tokenDescriptor = new SecurityTokenDescriptor()
{
Audience = "https://appleid.apple.com",//固定值
Issuer = Team_Id,//team ID,找IOS要
IssuedAt = DateTime.Now.AddDays(-),
NotBefore = DateTime.Now.AddDays(-),
Subject = new ClaimsIdentity(new[] { subject }),
}; var algorithm = new ECDsaCng(GetPrivateKey());
{
tokenDescriptor.SigningCredentials = CreateSigningCredentials(Key_Id, algorithm);//p8私钥文件得Key,找IOS要
var clientSecret = handler.CreateEncodedJwt(tokenDescriptor);
return clientSecret;
} }
/// <summary>
/// 获取P8
/// </summary>
/// <returns></returns>
private CngKey GetPrivateKey()
{
using (var reader = new StringReader("p8文件内容"))
{
var ecPrivateKeyParameters = (ECPrivateKeyParameters)new PemReader(reader).ReadObject();
var x = ecPrivateKeyParameters.Parameters.G.AffineXCoord.GetEncoded();
var y = ecPrivateKeyParameters.Parameters.G.AffineYCoord.GetEncoded();
var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned();
return EccKey.New(x, y, d);
}
}
参考文献:https://stackoverflow.com/questions/42514289/how-to-use-apns-auth-key-p8-file-in-c
网上还有另外一种P8文件的内容
private static ECDsa GetPrivateKey()
{
CngKey privateKey = null; //p8文件内容
string content = @"-----BEGIN PRIVATE KEY-----
****
-----END PRIVATE KEY-----"; // 这里直接用去头去尾的方法:
var lines = content.Split('\n');
var trimmed = string.Join("", lines.Skip().Take(lines.Length - ).Select(l => l.Trim()));
var keyBlob = Convert.FromBase64String(trimmed); _Log.Info("test1"); privateKey = CngKey.Import(keyBlob, CngKeyBlobFormat.Pkcs8PrivateBlob, CngProvider.MicrosoftSoftwareKeyStorageProvider); _Log.Info("test2"); return new ECDsaCng(privateKey);
}
这是一个坑深坑:
本地调试没问题,发布到服务器上就报错:System.Security.Cryptography.CryptographicException: 系统找不到指定的文件。
度娘个的解决方案就是:在服务器上的IIS修改一些配置。(ex:服务能随便改配置吗?万一把其他接口搞挂了呢);
根据生成的Secret和授权码AuthorizationCode来验证是否正确
var newToken = CreateSecret();
var datas = new Dictionary<string, string>()
{
{ "client_id", Client_Id },
{ "grant_type", "authorization_code"},//固定authorization_code
{ "code", authLoginModel.AuthorizationCode },//授权码,前端验证登录给予
{ "client_secret", newToken } //client_secret,后面方法生成
}; var formdata = new FormUrlEncodedContent(datas);
using (var httpclient = new HttpClient())
{
httpclient.Timeout = TimeSpan.FromSeconds();
var result = await httpclient.PostAsync(Apple_Token_Url, formdata);
var re = await result.Content.ReadAsStringAsync();
if (result.IsSuccessStatusCode)
{
var deserializeObject = JsonConvert.DeserializeObject<AppleTokenResult>(re);
var jwtPlayload = DecodeJwtPlayload(deserializeObject.IdToken);
if (jwtPlayload.Aud.Equals(Client_Id) && !string.IsNullOrEmpty(jwtPlayload.Sub))
{
appleUserId = jwtPlayload.Sub;
}
}
else
{
return OperationResult.FromError<AuthLoginBindResponse>($"{(int)BizCodeEnum.SING_FAIL}", re);
}
}
验证成功后会返回
{
"access_token":"a0996b16cfb674c0eb0d29194c880455b.0.nsww.5fi5MVC-i3AVNhddrNg7Qw",
"token_type":"Bearer",
"expires_in":,
"refresh_token":"r9ee922f1c8b048208037f78cd7dfc91a.0.nsww.KlV2TeFlTr7YDdZ0KtvEQQ",
"id_token":"eyJraWQiOiJBSURPUEsxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLnNreW1pbmcuYXBwbGVsb2dpbmRlbW8iLCJleHAiOjE1NjU2NjU1OTQsImlhdCI6MTU2NTY2NDk5NCwic3ViIjoiMDAwMjY2LmRiZTg2NWIwYWE3MjRlMWM4ODM5MDIwOWI5YzdkNjk1LjAyNTYiLCJhdF9oYXNoIjoiR0ZmODhlX1ptc0pqQ2VkZzJXem85ZyIsImF1dGhfdGltZSI6MTU2NTY2NDk2M30.J6XFWmbr0a1hkJszAKM2wevJF57yZt-MoyZNI9QF76dHfJvAmFO9_RP9-tz4pN4ua3BuSJpUbwzT2xFD_rBjsNWkU-ZhuSAONdAnCtK2Vbc2AYEH9n7lB2PnOE1mX5HwY-dI9dqS9AdU4S_CjzTGnvFqC9H5pt6LVoCF4N9dFfQnh2w7jQrjTic_JvbgJT5m7vLzRx-eRnlxQIifEsHDbudzi3yg7XC9OL9QBiTyHdCQvRdsyRLrewJT6QZmi6kEWrV9E21WPC6qJMsaIfGik44UgPOnNnjdxKPzxUAa-Lo1HAzvHcAX5i047T01ltqvHbtsJEZxAB6okmwco78JQA"
}
其中id_token就是JWT文件,然后对JWT文件进行解析
/// <summary>
/// 解析jwt第二部分
/// </summary>
/// <param name="jwtString"></param>
/// <returns></returns>
private JwtPlayloadModel JwtPlayload(string jwtString)
{
try
{
var code = jwtString.Split('.')[];
code = code.Replace('-', '+').Replace('_', '/').PadRight( * ((code.Length + ) / ), '=');
var bytes = Convert.FromBase64String(code);
var decode = Encoding.UTF8.GetString(bytes);
return JsonConvert.DeserializeObject<JwtPlayloadModel>(decode);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
C# Sign In With Apple苹果登陆后端验证的更多相关文章
- iOS sign in with Apple 苹果ID登录
http://www.cocoachina.com/articles/109104?filter=ios https://juejin.im/post/5deefc5e518825126416611d ...
- 关于Sign in with Apple 后台验证的一些记录
2019年10月9号 IOS端新增Sign in with Apple IOS真是世界上最垃圾的语言,没有之一,苹果是世界上最垃圾的公司,没有之一 关于Sign in with Apple 苹果官方 ...
- sign in with apple后端校验(java)
最近新开发的ios平台的app在提审的时候,被拒了,原因是app上如果有接第三方登陆(比如,微信,微博,facebook等),那就必须要接apple id登陆,坑爹~苹果霸权啊!然而没办法,靠他吃饭, ...
- Sign in with Apple 流程总结
流程图 相关说明 UserId 与用户的 Apple Id 一一对应.在同一个开发帐号下的所有 app 里,获取到的值都一样. IdentityToken identityToken 是一个 Json ...
- 【ASP.NET Core快速入门】(十五)MVC开发:ReturnUrl实现、Model后端验证 、Model前端验证
ReturnUrl实现 我们要实现returnUrl,我们需要在注册(Register)方法中接收传进的returnUrl并给它默认值null,然后将它保存在ViewData里面 然后我们定义一个内部 ...
- 菜鸟入门【ASP.NET Core】15:MVC开发:ReturnUrl实现、Model后端验证 、Model前端验证
ReturnUrl实现 我们要实现returnUrl,我们需要在注册(Register)方法中接收传进的returnUrl并给它默认值null,然后将它保存在ViewData里面 然后我们定义一个内部 ...
- 任务48:Identity MVC:Model后端验证
任务48:Identity MVC:Model后端验证 RegisterViewModel using System; using System.Collections.Generic; using ...
- MyCat源码分析系列之——前后端验证
更多MyCat源码分析,请戳MyCat源码分析系列 MyCat前端验证 MyCat的前端验证指的是应用连接MyCat时进行的用户验证过程,如使用MySQL客户端时,$ mysql -uroot -pr ...
- 购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(3)--Idetity,OWIN前后端验证
原文:购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(3)--Idetity,OWIN前后端验证 chsakell分享了前端使用AngularJS,后端使用ASP. ...
随机推荐
- vue3开发饿了么商城2020年新版本
带手机验证码登陆, 带全套购物车系统 带数据库 前后端分离开发 带定位用户功能 数据库代码为本地制作好了 带支付宝支付系统 带django开发服务器接口教程 地址: https://www.dua ...
- 域名和服务器绑定及https协议更换
服务器是之前已经购买了的 1.腾讯云产品中搜索域名注册(产品太多了懒得找,直接搜索来得快些) 2.进去之后可以选择各种后缀的域名,输入自己喜欢的,看看哪些后缀是没有被注册的.自己挑选一个就可以,按照指 ...
- web前端该怎么入门?web前端入门教程(非常详细)
初学编程的小伙伴经常会遇到的问题,1.没资源 2.没人带 3.不知道从何开始 ,小编也是从新手期过来的,所以很能理解萌新的难处,现在整理一些以前自己学习的一些资料送给大家,希望对广大初学小伙伴有帮助! ...
- Caused by: java.io.IOException: Type mismath in vlaue from map: excepted org.apache.hadoop.io.InaWritable,received SC
解决办法: 看map和reduce的输入是不是对应,看看map和reduce设置的参数和下面的是否一致
- 随笔之——伪类选择器:nth-child(n) 与 nth-of-type(n)的区别!!!
话不多说!直接正题!!! 一.E:nth-child(n)///选中父元素中第(n)个元素.若第n个元素为E则选中:若第n个不为E则不选中.n可以为2n(偶数).2n+1(奇数).等... 二.E:n ...
- PE文件学习(1)DOS和NT
大致结构 DOS头和NT头之间通常还有个DOS Stub DOS头 DOS头的作用是兼容MS-DOS操作系统中的可执行文件 一般没啥用 记录着PE头的位置 DOS头定义部分 typedef struc ...
- [Inno Setup] 如何读取命令行输入的参数
以 /verysilent 为例 [Code] var isVerySilent: Boolean; function InitializeSetup(): Boolean; var j: Integ ...
- 单源最短路问题--朴素Dijkstra & 堆优化Dijkstra
许久没有写博客,更新一下~ Dijkstra两种典型写法 1. 朴素Dijkstra 时间复杂度O(N^2) 适用:稠密图(点较少,分布密集) #include <cstdi ...
- 通过config文件配置动态导入模块
需求: 固定的服务中要调用不同的算法,当前服务中实现的动态导入是通过在config配置中加上参数:proto="AiProto(1,4)",在服务中from pathname im ...
- POJ1651:Multiplication Puzzle(区间dp)
Multiplication Puzzle Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 9419 Accepted: 5850 ...