微信小程序认证在.netcore9.0上的流程

一、整体流程

  1. 小游戏端:获取用户临时登录凭证 code
  2. 小游戏端:将 code 发送至 .NET Core WebAPI
  3. WebAPI:用 code 向微信服务器换取 openid/session_key
  4. WebAPI:创建/更新用户信息,生成 JWT Token
  5. 小游戏端:存储 Token 并用于后续授权请求
sequenceDiagram 微信小游戏->>+微信服务器: wx.login() 获取 code 微信小游戏->>+.NET Core API: 提交 code .NET Core API->>+微信服务器: code+appid+secret 换 openid/session_key 微信服务器-->>-.NET Core API: 返回 openid/session_key .NET Core API->>+数据库: 创建/更新用户信息 .NET Core API-->>-微信小游戏: 返回 JWT Token 微信小游戏->>+.NET Core API: 后续请求携带 Token .NET Core API-->>-微信小游戏: 返回授权数据

二、分步实现

1. 微信小游戏端(Unity/JavaScript)
// 调用微信登录接口
wx.login({
  success: (res) => {
    if (res.code) {
      // 将 code 发送到后端
      wx.request({
        url: 'https://your-api.com/auth/wechat',
        method: 'POST',
        data: { code: res.code },
        success: (response) => {
          const token = response.data.token;
          wx.setStorageSync('userToken', token); // 存储 Token
        }
      });
    }
  }
});
2. .NET Core 9 WebAPI 控制器
[ApiController]
[Route("auth")]
public class AuthController : ControllerBase
{
    private readonly IUserService _userService; // 用户服务(注册/登录逻辑)
    private readonly JwtSettings _jwtSettings; // JWT 配置

    [HttpPost("wechat")]
    public async Task<IActionResult> WeChatLogin([FromBody] WeChatLoginRequest request)
    {
        // 1. 用 code 换取 openid
        var wechatUser = await GetWeChatUserInfo(request.Code);
        
        // 2. 注册或更新用户
        var user = await _userService.CreateOrUpdateUserAsync(
            openId: wechatUser.OpenId, 
            sessionKey: wechatUser.SessionKey
        );

        // 3. 生成 JWT Token
        var token = GenerateJwtToken(user.Id);
        
        return Ok(new { Token = token });
    }

    private async Task<WeChatUserInfo> GetWeChatUserInfo(string code)
    {
        // 请求微信接口 (替换 YOUR_APPID 和 YOUR_SECRET)
        using var client = new HttpClient();
        var response = await client.GetStringAsync(
            $"https://api.weixin.qq.com/sns/jscode2session?" +
            $"appid=YOUR_APPID&secret=YOUR_SECRET&js_code={code}&grant_type=authorization_code"
        );

        var data = JsonSerializer.Deserialize<WeChatResponse>(response);
        return new WeChatUserInfo 
        { 
            OpenId = data.openid, 
            SessionKey = data.session_key 
        };
    }

    private string GenerateJwtToken(int userId)
    {
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_jwtSettings.SecretKey);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[] { new Claim("uid", userId.ToString()) }),
            Expires = DateTime.UtcNow.AddDays(7),
            SigningCredentials = new SigningCredentials(
                new SymmetricSecurityKey(key), 
                SecurityAlgorithms.HmacSha256
            )
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        return tokenHandler.WriteToken(token);
    }
}
3. 用户服务层(注册逻辑)
public class UserService : IUserService
{
    private readonly AppDbContext _dbContext;

    public async Task<User> CreateOrUpdateUserAsync(string openId, string sessionKey)
    {
        var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.WeChatOpenId == openId);
        
        if (user == null)
        {
            // 新用户注册
            user = new User 
            {
                WeChatOpenId = openId,
                SessionKey = sessionKey,
                CreatedAt = DateTime.UtcNow
            };
            _dbContext.Users.Add(user);
        }
        else
        {
            // 更新会话密钥
            user.SessionKey = sessionKey;
        }

        await _dbContext.SaveChangesAsync();
        return user;
    }
}
4. 授权过滤器(验证 Token)
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class JwtAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var token = context.HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_jwtSettings.SecretKey);

        try {
            tokenHandler.ValidateToken(token, new TokenValidationParameters {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ClockSkew = TimeSpan.Zero
            }, out _);
        }
        catch {
            context.Result = new UnauthorizedResult();
        }
    }
}

// 在控制器中使用
[HttpGet("protected-data")]
[JwtAuthorize]
public IActionResult GetProtectedData() 
{
    return Ok("授权成功!");
}

三、关键配置

  1. 微信小程序配置

    • 开放平台申请 AppID/AppSecret
    • 设置合法 API 域名(如 https://your-api.com
  2. .NET Core 配置

    // appsettings.json
    {
      "JwtSettings": {
        "SecretKey": "your-64-byte-secret-key", // 至少 64 位
        "ExpireDays": 7
      }
    }
    
  3. 数据库用户表

    字段 类型 说明
    Id int 主键
    WeChatOpenId string(128) 微信唯一标识
    SessionKey string(128) 微信会话密钥
    CreatedAt datetime 注册时间

四、安全注意事项

  1. 敏感信息保护

    • AppSecretSessionKey 永远不返回给客户端
    • 使用 HttpOnly Cookie 或客户端安全存储(如微信 Storage)保存 Token
  2. 防篡改机制

    • 微信返回的 openid/session_key 需验证签名(官方文档
  3. Token 有效期

    • JWT 过期时间建议 ≤ session_key 有效期(微信默认 30 分钟)

五、常见问题

如何获取用户头像/昵称?

  • 小游戏端调用 wx.getUserProfile() 获取加密数据
  • 后端用 session_key 解密(需实现 AES-128-CBC 算法

如何解决 code 被恶意刷取?

  • 后端增加 IP 限流(如 AspNetCoreRateLimit 包)
  • 验证 code 长度(微信固定为 32 字符)

完整代码示例GitHub Gist 链接
微信官方文档小程序登录
.NET Core JWT 库Microsoft.AspNetCore.Authentication.JwtBearer

posted @ 2025-07-21 15:05  冬天之雪  阅读(39)  评论(0)    收藏  举报