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

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))