14 changed files with 16921 additions and 4 deletions
-
40src/Shentun.Peis.Application.Contracts/ThirdUsers/ThirdLoginDto.cs
-
38src/Shentun.Peis.Application.Contracts/ThirdUsers/WechatSession.cs
-
1src/Shentun.Peis.Application/Shentun.Peis.Application.csproj
-
39src/Shentun.Peis.Application/ThirdUsers/ThirdUserAppService.cs
-
80src/Shentun.Peis.Application/ThirdUsers/WeChatHelper.cs
-
57src/Shentun.Peis.Domain/ThirdUsers/ThirdUser.cs
-
28src/Shentun.Peis.EntityFrameworkCore/DbMapping/ThirdUsers/ThirdUserDbMapping.cs
-
9src/Shentun.Peis.EntityFrameworkCore/EntityFrameworkCore/PeisDbContext.cs
-
16375src/Shentun.Peis.EntityFrameworkCore/Migrations/20250430072513_insert_third_user.Designer.cs
-
47src/Shentun.Peis.EntityFrameworkCore/Migrations/20250430072513_insert_third_user.cs
-
71src/Shentun.Peis.EntityFrameworkCore/Migrations/PeisDbContextModelSnapshot.cs
-
129src/Shentun.Peis.HttpApi.Host/Controllers/MiniProgramTokenController.cs
-
7src/Shentun.Peis.HttpApi.Host/PeisHttpApiHostModule.cs
-
4src/Shentun.Peis.HttpApi.Host/appsettings.json
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace Shentun.Peis.ThirdUsers |
|||
{ |
|||
public class ThirdLoginDto |
|||
{ |
|||
/// <summary>
|
|||
/// 是否有token
|
|||
/// </summary>
|
|||
public string IsToken { get; set; } = "Y"; |
|||
/// <summary>
|
|||
/// 微信OpenId
|
|||
/// </summary>
|
|||
public string OpenId { get; set; } |
|||
/// <summary>
|
|||
/// 访问TOKEN
|
|||
/// </summary>
|
|||
public string AccessToken { get; set; } |
|||
/// <summary>
|
|||
/// 刷新Token
|
|||
/// </summary>
|
|||
public string RefreshToken { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 过期时间(单位秒)
|
|||
/// </summary>
|
|||
public int ExpiresIn { get; set; } |
|||
/// <summary>
|
|||
/// 会话键
|
|||
/// </summary>
|
|||
public string SessionKey { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 会话键值
|
|||
/// </summary>
|
|||
public string SessionKeyValue { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace Shentun.Peis.ThirdUsers |
|||
{ |
|||
public class WechatSession |
|||
{ |
|||
/// <summary>
|
|||
/// 用户唯一标识
|
|||
/// </summary>
|
|||
public string OpenId { get; set; } |
|||
/// <summary>
|
|||
/// 会话密钥
|
|||
/// </summary>
|
|||
public string Session_Key { get; set; } |
|||
/// <summary>
|
|||
/// 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见UnionID 机制说明。
|
|||
/// </summary>
|
|||
public string Unionid { get; set; } |
|||
/// <summary>
|
|||
/// 错误码
|
|||
/// </summary>
|
|||
public int ErrCode { get; set; } |
|||
/// <summary>
|
|||
/// 错误信息
|
|||
/// </summary>
|
|||
public string ErrMsg { get; set; } |
|||
/// <summary>
|
|||
/// Redis里面OpenId值所对应的键OpenIdKey
|
|||
/// </summary>
|
|||
public string OpenIdKey { get; set; } |
|||
/// <summary>
|
|||
/// Redis里面Session_Key值所对应的键SessionKey
|
|||
/// </summary>
|
|||
public string SessionKey { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using Microsoft.AspNetCore.Authentication; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.AspNetCore.Identity; |
|||
using OpenIddict.Abstractions; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Identity; |
|||
|
|||
namespace Shentun.Peis.ThirdUsers |
|||
{ |
|||
public class ThirdUserAppService : ApplicationService |
|||
{ |
|||
//private readonly IOpenIddictScopeManager _scopeManager;
|
|||
//private readonly SignInManager<IdentityUser> _signInManager;
|
|||
//private readonly IRepository<IdentityUser,Guid> _identityUserRepository;
|
|||
//private readonly IdentityUserManager _identityUserManager;
|
|||
|
|||
//public ThirdUserAppService(
|
|||
// IOpenIddictScopeManager scopeManager,
|
|||
// SignInManager<IdentityUser> signInManager,
|
|||
// IRepository<IdentityUser, Guid> identityUserRepository,
|
|||
// IdentityUserManager identityUserManager)
|
|||
//{
|
|||
// _scopeManager = scopeManager;
|
|||
// _signInManager = signInManager;
|
|||
// _identityUserRepository = identityUserRepository;
|
|||
// _identityUserManager = identityUserManager;
|
|||
//}
|
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using Microsoft.Extensions.Configuration; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Net; |
|||
using System.Text; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Shentun.Peis.ThirdUsers |
|||
{ |
|||
public class WeChatHelper |
|||
{ |
|||
public static async Task<WechatSession> GetWechatSession(IConfiguration configSection1, string jsCode) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(jsCode)) |
|||
{ |
|||
throw new Exception("jsCode不能为空"); |
|||
} |
|||
IConfigurationSection configSection = configSection1.GetSection("WeChat"); |
|||
if (configSection == null) |
|||
{ |
|||
throw new Exception("微信配置不能为空"); |
|||
} |
|||
string url = configSection.GetValue("SessionUrl", "https://api.weixin.qq.com/sns/jscode2session?"); |
|||
string appId = configSection.GetValue("AppID", ""); |
|||
string appSecret = configSection.GetValue("AppSecret", ""); |
|||
if (string.IsNullOrWhiteSpace(url)) |
|||
{ |
|||
throw new Exception("微信url不能为空"); |
|||
} |
|||
if (string.IsNullOrWhiteSpace(appId)) |
|||
{ |
|||
throw new Exception("微信appId不能为空"); |
|||
} |
|||
if (string.IsNullOrWhiteSpace(appSecret)) |
|||
{ |
|||
throw new Exception("微信appSecret不能为空"); |
|||
} |
|||
|
|||
url = url + "appid=" + appId + "&secret=" + appSecret + "&js_code=" + jsCode + "&grant_type=authorization_code"; |
|||
|
|||
var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip }; |
|||
|
|||
using (var httpClient = new HttpClient(handler)) |
|||
{ |
|||
//await异步等待回应
|
|||
|
|||
var responseResult = await httpClient.GetAsync(url); |
|||
//确保HTTP成功状态值
|
|||
if (!responseResult.IsSuccessStatusCode) |
|||
{ |
|||
throw new Exception("获取微信小程序数据失败," + responseResult.StatusCode); |
|||
} |
|||
|
|||
//await异步读取最后的JSON(注意此时gzip已经被自动解压缩了,因为上面的AutomaticDecompression = DecompressionMethods.GZip)
|
|||
string responseContent = await responseResult.Content.ReadAsStringAsync(); |
|||
var wechatSession = JsonSerializer.Deserialize<WechatSession>(responseContent, |
|||
new JsonSerializerOptions() { PropertyNameCaseInsensitive = true } |
|||
); |
|||
if (wechatSession == null) |
|||
{ |
|||
throw new Exception("获取微信小程序wechatSession为空"); |
|||
} |
|||
if (wechatSession.ErrCode != 0) |
|||
{ |
|||
//wechatSession.OpenId = "obZGv5RhSNxxpkDwT0Xaf9Fzn8NM";
|
|||
//wechatSession.SessionKey = "O+VJ49EHaPLR5+oOjsG0xA==";
|
|||
throw new Exception("微信账户登陆失败" + wechatSession.ErrMsg); |
|||
} |
|||
if (string.IsNullOrWhiteSpace(wechatSession.OpenId)) |
|||
{ |
|||
throw new Exception("微信账户OpenId不能为空"); |
|||
} |
|||
return wechatSession; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Entities.Auditing; |
|||
using Volo.Abp.Domain.Entities; |
|||
using System.ComponentModel.DataAnnotations.Schema; |
|||
using Microsoft.EntityFrameworkCore; |
|||
|
|||
namespace Shentun.Peis.Models |
|||
{ |
|||
/// <summary>
|
|||
/// 第三方用户用户表
|
|||
/// </summary>
|
|||
[Table("third_user")] |
|||
[Index(nameof(MobilePhone), Name = "ix_third_user_mobile_phone_index", IsUnique = true)] |
|||
public class ThirdUser : AuditedEntity<Guid>, IHasConcurrencyStamp |
|||
{ |
|||
/// <summary>
|
|||
/// 手机号码
|
|||
/// </summary>
|
|||
[Column("mobile_phone")] |
|||
public string MobilePhone { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// abp用户表Id
|
|||
/// </summary>
|
|||
[Column("abp_user_id")] |
|||
public Guid? AbpUserId { get; set; } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 是否启用
|
|||
/// </summary>
|
|||
[Column("is_active")] |
|||
public char IsActive { get; set; } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 微信openid
|
|||
/// </summary>
|
|||
[Column("wechat_openid")] |
|||
public string? WechatOpenId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 用户注册方式(0.小程序注册 1.后台添加) 默认0
|
|||
/// </summary>
|
|||
[Column("user_register_flag")] |
|||
public char UserRegisterFlag { get; set; } |
|||
|
|||
|
|||
[Column("concurrency_stamp")] |
|||
public string ConcurrencyStamp { get; set; } |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Shentun.Peis.Models; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Shentun.Peis.EntityFrameworkCore; |
|||
|
|||
namespace Shentun.Peis.DbMapping |
|||
{ |
|||
internal class ThirdUserDbMapping : IEntityTypeConfiguration<ThirdUser> |
|||
{ |
|||
public void Configure(EntityTypeBuilder<ThirdUser> entity) |
|||
{ |
|||
entity.HasComment("第三方用户表"); |
|||
entity.Property(t => t.AbpUserId).HasComment("abpuser表id"); |
|||
entity.Property(t => t.WechatOpenId).HasComment("微信openid"); |
|||
entity.Property(t => t.MobilePhone).HasComment("手机号码"); |
|||
entity.Property(t => t.IsActive).HasComment("是否启用").IsRequired().HasDefaultValueSql("'Y'"); |
|||
entity.Property(t => t.UserRegisterFlag).HasComment("用户注册方式(0.小程序注册 1.后台添加) 默认0").IsRequired().HasDefaultValueSql("'0'"); |
|||
entity.Property(e => e.Id).IsFixedLength().IsRequired(); |
|||
|
|||
entity.ConfigureByConvention(); |
|||
} |
|||
} |
|||
} |
|||
16375
src/Shentun.Peis.EntityFrameworkCore/Migrations/20250430072513_insert_third_user.Designer.cs
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
#nullable disable |
|||
|
|||
namespace Shentun.Peis.Migrations |
|||
{ |
|||
public partial class insert_third_user : Migration |
|||
{ |
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.CreateTable( |
|||
name: "third_user", |
|||
columns: table => new |
|||
{ |
|||
id = table.Column<Guid>(type: "uuid", fixedLength: true, nullable: false), |
|||
mobile_phone = table.Column<string>(type: "text", nullable: true, comment: "手机号码"), |
|||
abp_user_id = table.Column<Guid>(type: "uuid", nullable: true, comment: "abpuser表id"), |
|||
is_active = table.Column<char>(type: "character(1)", nullable: false, defaultValueSql: "'Y'", comment: "是否启用"), |
|||
wechat_openid = table.Column<string>(type: "text", nullable: true, comment: "微信openid"), |
|||
user_register_flag = table.Column<char>(type: "character(1)", nullable: false, defaultValueSql: "'0'", comment: "用户注册方式(0.小程序注册 1.后台添加) 默认0"), |
|||
concurrency_stamp = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: true), |
|||
creation_time = table.Column<DateTime>(type: "timestamp without time zone", nullable: false), |
|||
creator_id = table.Column<Guid>(type: "uuid", nullable: false), |
|||
last_modification_time = table.Column<DateTime>(type: "timestamp without time zone", nullable: false), |
|||
last_modifier_id = table.Column<Guid>(type: "uuid", nullable: false) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_third_user", x => x.id); |
|||
}, |
|||
comment: "第三方用户表"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "ix_third_user_mobile_phone_index", |
|||
table: "third_user", |
|||
column: "mobile_phone", |
|||
unique: true); |
|||
} |
|||
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropTable( |
|||
name: "third_user"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.Extensions.Configuration; |
|||
using OpenIddict.Abstractions; |
|||
using OpenIddict.Server.AspNetCore; |
|||
using Shentun.Peis.Models; |
|||
using Shentun.Peis.ThirdUsers; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.OpenIddict.Controllers; |
|||
|
|||
namespace Shentun.Peis.Controllers |
|||
{ |
|||
|
|||
/// <summary>
|
|||
/// 小程序登录
|
|||
/// </summary>
|
|||
[Route("/connect/token", Order = -1)] |
|||
public class MiniProgramTokenController : TokenController |
|||
{ |
|||
|
|||
private readonly IConfiguration _configuration; |
|||
private readonly IRepository<ThirdUser, Guid> _thirdUserRepository; |
|||
private readonly SignInManager<Volo.Abp.Identity.IdentityUser> _signInManager; |
|||
private readonly IRepository<Volo.Abp.Identity.IdentityUser, Guid> _identityUserRepository; |
|||
|
|||
|
|||
public MiniProgramTokenController( |
|||
IConfiguration configuration, |
|||
IRepository<ThirdUser, Guid> thirdUserRepository, |
|||
SignInManager<Volo.Abp.Identity.IdentityUser> signInManager, |
|||
IRepository<Volo.Abp.Identity.IdentityUser, Guid> identityUserRepository) |
|||
{ |
|||
_configuration = configuration; |
|||
_thirdUserRepository = thirdUserRepository; |
|||
_signInManager = signInManager; |
|||
_identityUserRepository = identityUserRepository; |
|||
} |
|||
|
|||
|
|||
|
|||
public override async Task<IActionResult> HandleAsync() |
|||
{ |
|||
//MiniProgram
|
|||
var request = await GetOpenIddictServerRequestAsync(HttpContext); |
|||
|
|||
string grantType = request.GrantType; |
|||
|
|||
if (grantType == "mini_program") |
|||
{ |
|||
//小程序登录
|
|||
|
|||
// 获取小程序 code 并换取 openid
|
|||
var jsCode = request.GetParameter("jsCode").ToString(); |
|||
|
|||
var wechatSession = await WeChatHelper.GetWechatSession(_configuration, jsCode); |
|||
|
|||
var thirdUserEnt = await _thirdUserRepository.FirstOrDefaultAsync(f => f.WechatOpenId == wechatSession.OpenId && f.IsActive == 'Y' && f.AbpUserId != null); |
|||
if (thirdUserEnt == null) |
|||
{ |
|||
throw new UserFriendlyException("用户未授权"); |
|||
} |
|||
// 查询关联的 AbpUser
|
|||
var abpUser = await _identityUserRepository.FirstOrDefaultAsync(f => f.Id == thirdUserEnt.AbpUserId); |
|||
if (abpUser == null) |
|||
{ |
|||
throw new UserFriendlyException("用户未关联权限"); |
|||
} |
|||
|
|||
// 生成声明主体
|
|||
var principal = await _signInManager.CreateUserPrincipalAsync(abpUser); |
|||
|
|||
var scopes = request.GetScopes(); |
|||
var resources = await GetResourcesAsync(scopes); |
|||
principal.SetScopes(scopes); |
|||
principal.SetResources(resources); |
|||
|
|||
await SetClaimsDestinationsAsync(principal); |
|||
|
|||
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|||
} |
|||
else if (grantType == "phone_verify") |
|||
{ |
|||
//手机号+验证码登录认证
|
|||
|
|||
var mobilePhone = request.GetParameter("mobilePhone").ToString(); |
|||
var verifyCode = request.GetParameter("verifyCode").ToString(); |
|||
|
|||
|
|||
|
|||
var thirdUserEnt = await _thirdUserRepository.FirstOrDefaultAsync(f => f.MobilePhone == mobilePhone && f.IsActive == 'Y' && f.AbpUserId != null); |
|||
if (thirdUserEnt == null) |
|||
{ |
|||
throw new UserFriendlyException("用户未授权"); |
|||
} |
|||
// 查询关联的 AbpUser
|
|||
var abpUser = await _identityUserRepository.FirstOrDefaultAsync(f => f.Id == thirdUserEnt.AbpUserId); |
|||
if (abpUser == null) |
|||
{ |
|||
throw new UserFriendlyException("用户未关联权限"); |
|||
} |
|||
|
|||
// 生成声明主体
|
|||
var principal = await _signInManager.CreateUserPrincipalAsync(abpUser); |
|||
|
|||
var scopes = request.GetScopes(); |
|||
var resources = await GetResourcesAsync(scopes); |
|||
principal.SetScopes(scopes); |
|||
principal.SetResources(resources); |
|||
|
|||
await SetClaimsDestinationsAsync(principal); |
|||
|
|||
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|||
} |
|||
else |
|||
{ |
|||
return await base.HandleAsync(); |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
} |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue