【新增】实现支付宝小程序适配。

This commit is contained in:
jianweie
2024-04-22 23:04:58 +08:00
parent 8a4fe681cb
commit a6ad9274e1
53 changed files with 13032 additions and 107 deletions

View File

@@ -0,0 +1,193 @@
using CoreCms.Net.Auth.Policys;
using CoreCms.Net.Caching.AutoMate.RedisCache;
using CoreCms.Net.IServices;
using CoreCms.Net.Model.ViewModels.UI;
using CoreCms.Net.Utility.Extensions;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Claims;
using System.Threading.Tasks;
using System;
using System.IdentityModel.Tokens.Jwt;
using CoreCms.Net.Model.Entities;
using CoreCms.Net.Model.FromBody;
using CoreCms.Net.Configuration;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.WeDataQueryBindListResponse.Types;
namespace CoreCms.Net.Web.WebApi.Controllers.AliPayOAuth
{
/// <summary>
/// 支付宝业务交互接口
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class AliPayAuthController : ControllerBase
{
private readonly IAliPayServices _aliPayService;
private readonly PermissionRequirement _permissionRequirement;
private readonly ICoreCmsAliPayUserInfoServices _aliPayUserServices;
private readonly IRedisOperationRepository _redisOperationRepository;
private readonly ICoreCmsUserServices _userServices;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ICoreCmsUserLogServices _userLogServices;
/// <summary>
/// 构造函数
/// </summary>
public AliPayAuthController(PermissionRequirement permissionRequirement, IRedisOperationRepository redisOperationRepository, ICoreCmsAliPayUserInfoServices aliPayUserServices, IAliPayServices aliPayService, ICoreCmsUserServices userServices, IHttpContextAccessor httpContextAccessor, ICoreCmsUserLogServices userLogServices)
{
_permissionRequirement = permissionRequirement;
_redisOperationRepository = redisOperationRepository;
_aliPayUserServices = aliPayUserServices;
_aliPayService = aliPayService;
_userServices = userServices;
_httpContextAccessor = httpContextAccessor;
_userLogServices = userLogServices;
}
/// <summary>
/// 静默通过code票据返回app_auth_token相关信息
/// 前端是getAuthCode方法
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[HttpPost]
public async Task<WebApiCallBack> GetAliPayAppAuthTokenByCode([FromBody] FMStringId entity)
{
var jm = new WebApiCallBack();
var lockKey = "LOCK_OnLogin:user_" + entity.id;
var lockHolder = Guid.NewGuid().ToString("N"); //锁持有者
var redisUserLock = await _redisOperationRepository.LockTakeAsync(lockKey, lockHolder, TimeSpan.FromSeconds(10));
if (redisUserLock)
{
if (string.IsNullOrEmpty(entity.id))
{
jm.msg = "请提交code";
return jm;
}
try
{
var result = _aliPayService.GetAliPayAppAuthTokenBYCode(entity.id);
if (!result.IsError)
{
var userInfo = await _aliPayUserServices.QueryByClauseAsync(p => p.userId == result.UserId);
if (userInfo != null)
{
userInfo.reExpiresIn = result.ReExpiresIn.ObjectToInt();
userInfo.expiresIn = result.ExpiresIn.ObjectToInt();
userInfo.refreshToken = result.RefreshToken;
userInfo.accessToken = result.AccessToken;
await _aliPayUserServices.UpdateAsync(userInfo);
}
else
{
userInfo = new CoreCmsAliPayUserInfo();
//事物处理过程开始
userInfo.accessToken = result.AccessToken;
userInfo.aliPayUserInfoId = result.AlipayUserId;
userInfo.authStart = result.AuthStart;
userInfo.expiresIn = result.ExpiresIn.ObjectToInt();
userInfo.reExpiresIn = result.ReExpiresIn.ObjectToInt();
userInfo.refreshToken = result.RefreshToken;
userInfo.userId = result.UserId;
userInfo.openId = result.OpenId;
userInfo.unionId = result.UnionId;
userInfo.createTime = DateTime.Now;
userInfo.userInfoId = 0;
var resultData = await _aliPayUserServices.InsertAsync(userInfo, true);
userInfo.id = resultData;
}
if (userInfo is { userInfoId: > 0 })
{
var user = await _userServices.QueryByClauseAsync(p => p.id == userInfo.userInfoId);
if (user != null)
{
if (user.status == (int)GlobalEnumVars.UserStatus.)
{
jm.status = false;
jm.msg = "您的账号已经被禁用。";
return jm;
}
if (user.isDelete)
{
jm.status = false;
jm.msg = "您的账号已经被禁用。";
return jm;
}
var claims = new List<Claim> {
new Claim(ClaimTypes.Name, user.nickName),
new Claim(JwtRegisteredClaimNames.Jti, user.id.ToString()),
new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(_permissionRequirement.Expiration.TotalSeconds).ToString(CultureInfo.InvariantCulture)) };
//用户标识
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
identity.AddClaims(claims);
jm.status = true;
jm.data = new
{
auth = JwtToken.BuildJwtToken(claims.ToArray(), _permissionRequirement),
user
};
jm.otherData = result.UserId;
//录入登录日志
var log = new CoreCmsUserLog
{
userId = user.id,
state = (int)GlobalEnumVars.UserLogTypes.,
ip = _httpContextAccessor.HttpContext?.Connection.RemoteIpAddress != null ? _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString() : "127.0.0.1",
createTime = DateTime.Now,
parameters = GlobalEnumVars.UserLogTypes..ToString()
};
await _userLogServices.InsertAsync(log);
return jm;
}
}
jm.status = true;
jm.data = result.UserId;
jm.otherData = result.UserId;
}
else
{
jm.status = false;
jm.msg = result.SubMsg;
jm.data = result;
}
}
catch (Exception e)
{
jm.status = false;
jm.msg = e.Message;
jm.data = e;
}
finally
{
await _redisOperationRepository.LockReleaseAsync(lockKey, lockHolder);
}
}
else
{
jm.msg = "当前请求太频繁_请稍后再试";
}
return jm;
}
}
}

View File

@@ -15,8 +15,11 @@ using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Aop.Api;
using Aop.Api.Util;
using CoreCms.Net.Auth.HttpContextUser;
using CoreCms.Net.Auth.Policys;
using CoreCms.Net.Caching.AccressToken;
@@ -27,6 +30,8 @@ using CoreCms.Net.Loging;
using CoreCms.Net.Model.Entities;
using CoreCms.Net.Model.Entities.Expression;
using CoreCms.Net.Model.FromBody;
using CoreCms.Net.Model.Options;
using CoreCms.Net.Model.ViewModels.AliPay;
using CoreCms.Net.Model.ViewModels.DTO;
using CoreCms.Net.Model.ViewModels.UI;
using CoreCms.Net.Utility.Extensions;
@@ -92,6 +97,8 @@ namespace CoreCms.Net.Web.WebApi.Controllers
private readonly IWeChatApiHttpClientFactory _weChatApiHttpClientFactory;
private readonly WeChatOptions _weChatOptions;
private readonly IRedisOperationRepository _redisOperationRepository;
private readonly ICoreCmsAliPayUserInfoServices _aliPayUserInfoServices;
private readonly AliPayOptions _aliPayOptions;
/// <summary>
/// 构造函数
@@ -122,7 +129,7 @@ namespace CoreCms.Net.Web.WebApi.Controllers
, ICoreCmsSettingServices settingServices
, ICoreCmsServicesServices servicesServices
, IOptions<WeChatOptions> weChatOptions
, ICoreCmsUserServicesOrderServices userServicesOrderServices, ICoreCmsUserServicesTicketServices userServicesTicketServices, ICoreCmsStoreServices storeServices, ICoreCmsCouponServices couponServices, ICoreCmsOrderServices orderServices, IWeChatApiHttpClientFactory weChatApiHttpClientFactory, IRedisOperationRepository redisOperationRepository)
, ICoreCmsUserServicesOrderServices userServicesOrderServices, ICoreCmsUserServicesTicketServices userServicesTicketServices, ICoreCmsStoreServices storeServices, ICoreCmsCouponServices couponServices, ICoreCmsOrderServices orderServices, IWeChatApiHttpClientFactory weChatApiHttpClientFactory, IRedisOperationRepository redisOperationRepository, ICoreCmsAliPayUserInfoServices aliPayUserInfoServices, IOptions<AliPayOptions> aliPayOptions)
{
_user = user;
_userWeChatInfoServices = userWeChatInfoServices;
@@ -155,6 +162,8 @@ namespace CoreCms.Net.Web.WebApi.Controllers
_orderServices = orderServices;
_weChatApiHttpClientFactory = weChatApiHttpClientFactory;
_redisOperationRepository = redisOperationRepository;
_aliPayUserInfoServices = aliPayUserInfoServices;
_aliPayOptions = aliPayOptions.Value;
_weChatOptions = weChatOptions.Value;
}
@@ -533,7 +542,7 @@ namespace CoreCms.Net.Web.WebApi.Controllers
/// <param name="entity"></param>
/// <returns></returns>
[HttpPost]
public async Task<WebApiCallBack> SmsLogin([FromBody] FMWxAccountCreate entity)
public async Task<WebApiCallBack> SmsLogin([FromBody] FMComAccountCreate entity)
{
var jm = await _userServices.SmsLogin(entity, (int)GlobalEnumVars.LoginType.Sms, entity.platform);
return jm;
@@ -573,7 +582,7 @@ namespace CoreCms.Net.Web.WebApi.Controllers
return jm;
}
var data = new FMWxAccountCreate
var data = new FMComAccountCreate
{
mobile = phoneNumber.phoneNumber,
invitecode = entity.invitecode,
@@ -587,6 +596,124 @@ namespace CoreCms.Net.Web.WebApi.Controllers
#endregion
#region
/// <summary>
/// 支付宝小程序授权拉取手机号码
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[HttpPost]
public async Task<WebApiCallBack> DecryptPhoneNumberByAli([FromBody] FMAliLoginDecryptPhoneNumber entity)
{
var jm = new WebApiCallBack();
var userInfo = await _aliPayUserInfoServices.QueryByClauseAsync(p => p.userId == entity.sessionAuthId || p.openId == entity.sessionAuthId);
if (userInfo == null)
{
jm.status = false;
jm.msg = "支付宝用户信息获取失败";
return jm;
}
//1. 获取验签和解密所需要的参数
var content = entity.encryptedData;
// 是否为加密报文
var isDataEncrypted = !content.StartsWith("{", StringComparison.Ordinal);
var signCheckPass = false;
//2. 验签
var signContent = content;
var decryptKey = _aliPayOptions.AESKey;
// 如果是加密的报文则需要在密文的前后添加双引号
if (isDataEncrypted)
{
signContent = "\"" + signContent + "\"";
}
try
{
signCheckPass = AlipaySignature.RSACheckContent(signContent, entity.sign, _aliPayOptions.AliPublicKey, "UTF-8", "RSA2", false);
}
catch (Exception ex)
{
//验签异常, 日志
jm.status = false;
jm.msg = "验签失败";
jm.code = 500;
jm.otherData = ex;
NLogUtil.WriteAll(LogLevel.Error, LogType.Web, "小程序接口", "支付宝小程序授权拉取手机号码", ex);
return jm;
}
if (!signCheckPass)
{
//验签不通过(异常或者报文被篡改),终止流程(不需要做解密)
jm.status = false;
jm.msg = "验签失败";
jm.code = 500;
jm.otherData = "验签不通过(异常或者报文被篡改),终止流程(不需要做解密) ";
return jm;
}
//3. 解密
string plainData = null;
if (isDataEncrypted)
{
try
{
plainData = AlipayEncrypt.AesDencrypt(decryptKey, content, "UTF-8");
}
catch (Exception ex)
{
//解密异常, 记录日志
jm.status = false;
jm.msg = "解密异常";
jm.code = 500;
jm.otherData = ex;
NLogUtil.WriteAll(LogLevel.Error, LogType.Web, "小程序接口", "支付宝小程序授权拉取手机号码", ex);
return jm;
}
}
else
{
plainData = content;
}
if (!string.IsNullOrEmpty(plainData))
{
var obj = JsonConvert.DeserializeObject<AlipayEncryptDTO>(plainData);
if (obj is { code: "10000", msg: "Success" } && !string.IsNullOrEmpty(obj.mobile))
{
jm.status = true;
jm.msg = "获取手机号码成功";
jm.data = obj;
var data = new FMComAccountCreate
{
mobile = obj.mobile,
invitecode = entity.invitecode,
sessionAuthId = entity.sessionAuthId
};
jm = await _userServices.SmsLogin(data, (int)GlobalEnumVars.LoginType.AliPhoneNumber);
}
else
{
jm.status = false;
jm.msg = "数据解码失败";
jm.data = obj;
jm.otherData = plainData;
}
}
else
{
jm.status = false;
jm.msg = "获取手机号码错误,解密失败。";
jm.code = 500;
}
return jm;
}
#endregion
#region jwt token()======================================================
/// <summary>
/// 用户短信注册并返回jwt token(弃用)
@@ -595,7 +722,7 @@ namespace CoreCms.Net.Web.WebApi.Controllers
/// <returns></returns>
[Obsolete]
[HttpPost]
public async Task<WebApiCallBack> SmsLogin2([FromBody] FMWxAccountCreate entity)
public async Task<WebApiCallBack> SmsLogin2([FromBody] FMComAccountCreate entity)
{
var jm = new WebApiCallBack();
if (!CommonHelper.IsMobile(entity.mobile))

View File

@@ -50,6 +50,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AlipaySDKNet.Standard" Version="4.9.78" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />

View File

@@ -103,6 +103,24 @@
</summary>
<returns></returns>
</member>
<member name="T:CoreCms.Net.Web.WebApi.Controllers.AliPayOAuth.AliPayAuthController">
<summary>
支付宝业务交互接口
</summary>
</member>
<member name="M:CoreCms.Net.Web.WebApi.Controllers.AliPayOAuth.AliPayAuthController.#ctor(CoreCms.Net.Auth.Policys.PermissionRequirement,CoreCms.Net.Caching.AutoMate.RedisCache.IRedisOperationRepository,CoreCms.Net.IServices.ICoreCmsAliPayUserInfoServices,CoreCms.Net.IServices.IAliPayServices,CoreCms.Net.IServices.ICoreCmsUserServices,Microsoft.AspNetCore.Http.IHttpContextAccessor,CoreCms.Net.IServices.ICoreCmsUserLogServices)">
<summary>
构造函数
</summary>
</member>
<member name="M:CoreCms.Net.Web.WebApi.Controllers.AliPayOAuth.AliPayAuthController.GetAliPayAppAuthTokenByCode(CoreCms.Net.Model.FromBody.FMStringId)">
<summary>
静默通过code票据返回app_auth_token相关信息
前端是getAuthCode方法
</summary>
<param name="entity"></param>
<returns></returns>
</member>
<member name="T:CoreCms.Net.Web.WebApi.Controllers.ArticleController">
<summary>
文章api控制器

View File

@@ -72,6 +72,9 @@ builder.Services.AddWeChatPay();
//注册自定义微信接口配置文件
builder.Services.Configure<CoreCms.Net.WeChat.Service.Options.WeChatOptions>(builder.Configuration.GetSection(nameof(CoreCms.Net.WeChat.Service.Options.WeChatOptions)));
//注册自定义支付宝接口配置文件
builder.Services.Configure<CoreCms.Net.Model.Options.AliPayOptions>(builder.Configuration.GetSection(nameof(CoreCms.Net.Model.Options.AliPayOptions)));
// 注入工厂 HTTP 客户端
builder.Services.AddHttpClient();
builder.Services.AddSingleton<CoreCms.Net.WeChat.Service.HttpClients.IWeChatApiHttpClientFactory, CoreCms.Net.WeChat.Service.HttpClients.WeChatApiHttpClientFactory>();

View File

@@ -103,5 +103,27 @@
"WxOpenAppSecret": "",
"WxOpenToken": "",
"WxOpenEncodingAESKey": ""
},
"AliPayOptions": {
//appid
"AppId": "",
//应用私钥
"AppSecret": "",
//支付宝公钥
"AliPublicKey": "",
//应用公钥
"AppPublicKey": "",
//回调地址
"RedirectUrl": "",
//阿里访问令牌刷新
"AliAccessTokenRefresh": "",
//oauth2授权地址
"AppConnectUrl": "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?",
//支付宝授权类型
"AppAuthToken": "alipay.open.auth.token.app",
//支付宝通用接口
"AliPublicApi": "https://openapi.alipay.com/gateway.do",
//外部H5唤起支付宝客户端进行实名认证接口地址
"AliPayAppAuth": "alipays://platformapi/startapp?appId=20000067&url="
}
}