首页 微信小程序 微信开发 ASP.NET Core集成微信登录的实例图解

ASP.NET Core集成微信登录的实例图解

Apr 26, 2017 pm 02:49 PM
asp.net core 微信

这篇文章主要介绍了ASP.NET Core集成微信登录的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

工具:

Visual Studio 2015 update 3

Asp.Net Core 1.0

1 准备工作

申请微信公众平台接口测试帐号,申请网址:(mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login)。申请接口测试号无需公众帐号,可以直接体验和测试公众平台所有高级接口。

1.1 配置接口信息

1.2 修改网页授权信息

点击“修改”后在弹出页面填入你的网站域名:

2 新建网站项目

2.1 选择ASP.NET Core Web Application 模板

2.2 选择Web 应用程序,并更改身份验证为个人用户账户

3 集成微信登录功能

3.1添加引用

打开project.json文件,添加引用Microsoft.AspNetCore.Authentication.OAuth

3.2 添加代码文件

在项目中新建文件夹,命名为WeChatOAuth,并添加代码文件(本文最后附全部代码)。

3.3 注册微信登录中间件

打开Startup.cs文件,在Configure中添加代码:

app.UseWeChatAuthentication(new WeChatOptions()
{
 AppId = "******",

 AppSecret = "******"

});
登录后复制

注意该代码的插入位置必须在app.UseIdentity()下方。

4 代码

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.AspNetCore.Authentication.WeChat;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Builder
{
 /// <summary>
 /// Extension methods to add WeChat authentication capabilities to an HTTP application pipeline.
 /// </summary>
 public static class WeChatAppBuilderExtensions
 {
  /// <summary>
  /// Adds the <see cref="WeChatMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>, which enables WeChat authentication capabilities.
  /// </summary>
  /// <param name="app">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
  /// <returns>A reference to this instance after the operation has completed.</returns>
  public static IApplicationBuilder UseWeChatAuthentication(this IApplicationBuilder app)
  {
   if (app == null)
   {
    throw new ArgumentNullException(nameof(app));
   }

   return app.UseMiddleware<WeChatMiddleware>();
  }

  /// <summary>
  /// Adds the <see cref="WeChatMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>, which enables WeChat authentication capabilities.
  /// </summary>
  /// <param name="app">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
  /// <param name="options">A <see cref="WeChatOptions"/> that specifies options for the middleware.</param>
  /// <returns>A reference to this instance after the operation has completed.</returns>
  public static IApplicationBuilder UseWeChatAuthentication(this IApplicationBuilder app, WeChatOptions options)
  {
   if (app == null)
   {
    throw new ArgumentNullException(nameof(app));
   }
   if (options == null)
   {
    throw new ArgumentNullException(nameof(options));
   }

   return app.UseMiddleware<WeChatMiddleware>(Options.Create(options));
  }
 }
}
登录后复制

WeChatDefaults.cs:

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.AspNetCore.Authentication.WeChat
{
 public static class WeChatDefaults
 {
  public const string AuthenticationScheme = "WeChat";

  public static readonly string AuthorizationEndpoint = "https://open.weixin.qq.com/connect/oauth2/authorize";

  public static readonly string TokenEndpoint = "https://api.weixin.qq.com/sns/oauth2/access_token";

  public static readonly string UserInformationEndpoint = "https://api.weixin.qq.com/sns/userinfo";
 }
}
登录后复制

WeChatHandler.cs

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Authentication.WeChat
{
 internal class WeChatHandler : OAuthHandler<WeChatOptions>
 {
  public WeChatHandler(HttpClient httpClient)
   : base(httpClient)
  {
  }


  protected override async Task<AuthenticateResult> HandleRemoteAuthenticateAsync()
  {
   AuthenticationProperties properties = null;
   var query = Request.Query;

   var error = query["error"];
   if (!StringValues.IsNullOrEmpty(error))
   {
    var failureMessage = new StringBuilder();
    failureMessage.Append(error);
    var errorDescription = query["error_description"];
    if (!StringValues.IsNullOrEmpty(errorDescription))
    {
     failureMessage.Append(";Description=").Append(errorDescription);
    }
    var errorUri = query["error_uri"];
    if (!StringValues.IsNullOrEmpty(errorUri))
    {
     failureMessage.Append(";Uri=").Append(errorUri);
    }

    return AuthenticateResult.Fail(failureMessage.ToString());
   }

   var code = query["code"];
   var state = query["state"];
   var oauthState = query["oauthstate"];

   properties = Options.StateDataFormat.Unprotect(oauthState);

   if (state != Options.StateAddition || properties == null)
   {
    return AuthenticateResult.Fail("The oauth state was missing or invalid.");
   }

   // OAuth2 10.12 CSRF
   if (!ValidateCorrelationId(properties))
   {
    return AuthenticateResult.Fail("Correlation failed.");
   }

   if (StringValues.IsNullOrEmpty(code))
   {
    return AuthenticateResult.Fail("Code was not found.");
   }

   //获取tokens
   var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath));

   var identity = new ClaimsIdentity(Options.ClaimsIssuer);

   AuthenticationTicket ticket = null;

   if (Options.WeChatScope == Options.InfoScope)
   {
    //获取用户信息
    ticket = await CreateTicketAsync(identity, properties, tokens);
   }
   else
   {
    //不获取信息,只使用openid
    identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, tokens.TokenType, ClaimValueTypes.String, Options.ClaimsIssuer));
    ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), properties, Options.AuthenticationScheme);
   }

   if (ticket != null)
   {
    return AuthenticateResult.Success(ticket);
   }
   else
   {
    return AuthenticateResult.Fail("Failed to retrieve user information from remote server.");
   }
  }

  
  /// <summary>
  /// OAuth第一步,获取code
  /// </summary>
  /// <param name="properties"></param>
  /// <param name="redirectUri"></param>
  /// <returns></returns>
  protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
  {
   //加密OAuth状态
   var oauthstate = Options.StateDataFormat.Protect(properties);

   //
   redirectUri = $"{redirectUri}?{nameof(oauthstate)}={oauthstate}";

   var queryBuilder = new QueryBuilder()
   {
    { "appid", Options.ClientId },
    { "redirect_uri", redirectUri },
    { "response_type", "code" },
    { "scope", Options.WeChatScope },     
    { "state", Options.StateAddition },
   };
   return Options.AuthorizationEndpoint + queryBuilder.ToString();
  }



  /// <summary>
  /// OAuth第二步,获取token
  /// </summary>
  /// <param name="code"></param>
  /// <param name="redirectUri"></param>
  /// <returns></returns>
  protected override async Task<OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri)
  {
   var tokenRequestParameters = new Dictionary<string, string>()
   {
    { "appid", Options.ClientId },
    { "secret", Options.ClientSecret },
    { "code", code },
    { "grant_type", "authorization_code" },
   };

   var requestContent = new FormUrlEncodedContent(tokenRequestParameters);

   var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint);
   requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
   requestMessage.Content = requestContent;
   var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted);
   if (response.IsSuccessStatusCode)
   {
    var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

    string ErrCode = payload.Value<string>("errcode");
    string ErrMsg = payload.Value<string>("errmsg");

    if (!string.IsNullOrEmpty(ErrCode) | !string.IsNullOrEmpty(ErrMsg))
    {
     return OAuthTokenResponse.Failed(new Exception($"ErrCode:{ErrCode},ErrMsg:{ErrMsg}")); 
    }

    var tokens = OAuthTokenResponse.Success(payload);

    //借用TokenType属性保存openid
    tokens.TokenType = payload.Value<string>("openid");

    return tokens;
   }
   else
   {
    var error = "OAuth token endpoint failure";
    return OAuthTokenResponse.Failed(new Exception(error));
   }
  }

  /// <summary>
  /// OAuth第四步,获取用户信息
  /// </summary>
  /// <param name="identity"></param>
  /// <param name="properties"></param>
  /// <param name="tokens"></param>
  /// <returns></returns>
  protected override async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens)
  {
   var queryBuilder = new QueryBuilder()
   {
    { "access_token", tokens.AccessToken },
    { "openid", tokens.TokenType },//在第二步中,openid被存入TokenType属性
    { "lang", "zh_CN" }
   };

   var infoRequest = Options.UserInformationEndpoint + queryBuilder.ToString();

   var response = await Backchannel.GetAsync(infoRequest, Context.RequestAborted);
   if (!response.IsSuccessStatusCode)
   {
    throw new HttpRequestException($"Failed to retrieve WeChat user information ({response.StatusCode}) Please check if the authentication information is correct and the corresponding WeChat Graph API is enabled.");
   }

   var user = JObject.Parse(await response.Content.ReadAsStringAsync());
   var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), properties, Options.AuthenticationScheme);
   var context = new OAuthCreatingTicketContext(ticket, Context, Options, Backchannel, tokens, user);

   var identifier = user.Value<string>("openid");
   if (!string.IsNullOrEmpty(identifier))
   {
    identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, identifier, ClaimValueTypes.String, Options.ClaimsIssuer));
   }

   var nickname = user.Value<string>("nickname");
   if (!string.IsNullOrEmpty(nickname))
   {
    identity.AddClaim(new Claim(ClaimTypes.Name, nickname, ClaimValueTypes.String, Options.ClaimsIssuer));
   }

   var sex = user.Value<string>("sex");
   if (!string.IsNullOrEmpty(sex))
   {
    identity.AddClaim(new Claim("urn:WeChat:sex", sex, ClaimValueTypes.String, Options.ClaimsIssuer));
   }

   var country = user.Value<string>("country");
   if (!string.IsNullOrEmpty(country))
   {
    identity.AddClaim(new Claim(ClaimTypes.Country, country, ClaimValueTypes.String, Options.ClaimsIssuer));
   }

   var province = user.Value<string>("province");
   if (!string.IsNullOrEmpty(province))
   {
    identity.AddClaim(new Claim(ClaimTypes.StateOrProvince, province, ClaimValueTypes.String, Options.ClaimsIssuer));
   }

   var city = user.Value<string>("city");
   if (!string.IsNullOrEmpty(city))
   {
    identity.AddClaim(new Claim("urn:WeChat:city", city, ClaimValueTypes.String, Options.ClaimsIssuer));
   }

   var headimgurl = user.Value<string>("headimgurl");
   if (!string.IsNullOrEmpty(headimgurl))
   {
    identity.AddClaim(new Claim("urn:WeChat:headimgurl", headimgurl, ClaimValueTypes.String, Options.ClaimsIssuer));
   }

   var unionid = user.Value<string>("unionid");
   if (!string.IsNullOrEmpty(unionid))
   {
    identity.AddClaim(new Claim("urn:WeChat:unionid", unionid, ClaimValueTypes.String, Options.ClaimsIssuer));
   }

   await Options.Events.CreatingTicket(context);
   return context.Ticket;
  }
 }
}
登录后复制

WeChatMiddleware.cs

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Globalization;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Authentication.WeChat
{
 /// <summary>
 /// An ASP.NET Core middleware for authenticating users using WeChat.
 /// </summary>
 public class WeChatMiddleware : OAuthMiddleware<WeChatOptions>
 {
  /// <summary>
  /// Initializes a new <see cref="WeChatMiddleware"/>.
  /// </summary>
  /// <param name="next">The next middleware in the HTTP pipeline to invoke.</param>
  /// <param name="dataProtectionProvider"></param>
  /// <param name="loggerFactory"></param>
  /// <param name="encoder"></param>
  /// <param name="sharedOptions"></param>
  /// <param name="options">Configuration options for the middleware.</param>
  public WeChatMiddleware(
   RequestDelegate next,
   IDataProtectionProvider dataProtectionProvider,
   ILoggerFactory loggerFactory,
   UrlEncoder encoder,
   IOptions<SharedAuthenticationOptions> sharedOptions,
   IOptions<WeChatOptions> options)
   : base(next, dataProtectionProvider, loggerFactory, encoder, sharedOptions, options)
  {
   if (next == null)
   {
    throw new ArgumentNullException(nameof(next));
   }

   if (dataProtectionProvider == null)
   {
    throw new ArgumentNullException(nameof(dataProtectionProvider));
   }

   if (loggerFactory == null)
   {
    throw new ArgumentNullException(nameof(loggerFactory));
   }

   if (encoder == null)
   {
    throw new ArgumentNullException(nameof(encoder));
   }

   if (sharedOptions == null)
   {
    throw new ArgumentNullException(nameof(sharedOptions));
   }

   if (options == null)
   {
    throw new ArgumentNullException(nameof(options));
   }

   if (string.IsNullOrEmpty(Options.AppId))
   {
    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, nameof(Options.AppId)));
   }

   if (string.IsNullOrEmpty(Options.AppSecret))
   {
    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, nameof(Options.AppSecret)));
   }
  }

  /// <summary>
  /// Provides the <see cref="AuthenticationHandler{T}"/> object for processing authentication-related requests.
  /// </summary>
  /// <returns>An <see cref="AuthenticationHandler{T}"/> configured with the <see cref="WeChatOptions"/> supplied to the constructor.</returns>
  protected override AuthenticationHandler<WeChatOptions> CreateHandler()
  {
   return new WeChatHandler(Backchannel);
  }
 }
}
登录后复制

WeChatOptions.cs

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication.WeChat;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;

namespace Microsoft.AspNetCore.Builder
{
 /// <summary>
 /// Configuration options for <see cref="WeChatMiddleware"/>.
 /// </summary>
 public class WeChatOptions : OAuthOptions
 {
  /// <summary>
  /// Initializes a new <see cref="WeChatOptions"/>.
  /// </summary>
  public WeChatOptions()
  {
   AuthenticationScheme = WeChatDefaults.AuthenticationScheme;
   DisplayName = AuthenticationScheme;
   CallbackPath = new PathString("/signin-wechat");
   StateAddition = "#wechat_redirect";
   AuthorizationEndpoint = WeChatDefaults.AuthorizationEndpoint;
   TokenEndpoint = WeChatDefaults.TokenEndpoint;
   UserInformationEndpoint = WeChatDefaults.UserInformationEndpoint;
   //SaveTokens = true;   

   //BaseScope (不弹出授权页面,直接跳转,只能获取用户openid),
   //InfoScope (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
   WeChatScope = InfoScope;
  }

  // WeChat uses a non-standard term for this field.
  /// <summary>
  /// Gets or sets the WeChat-assigned appId.
  /// </summary>
  public string AppId
  {
   get { return ClientId; }
   set { ClientId = value; }
  }

  // WeChat uses a non-standard term for this field.
  /// <summary>
  /// Gets or sets the WeChat-assigned app secret.
  /// </summary>
  public string AppSecret
  {
   get { return ClientSecret; }
   set { ClientSecret = value; }
  }

  public string StateAddition { get; set; }
  public string WeChatScope { get; set; }

  public string BaseScope = "snsapi_base";

  public string InfoScope = "snsapi_userinfo";
 }
}
登录后复制

以上是ASP.NET Core集成微信登录的实例图解的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

欧易交易所app国内下载教程 欧易交易所app国内下载教程 Mar 21, 2025 pm 05:42 PM

本文提供国内安全下载欧易OKX App的详细指南。由于国内应用商店限制,建议用户通过欧易OKX官方网站下载App,或使用官网提供的二维码扫描下载。下载过程中,务必核实官网地址,检查应用权限,安装后进行安全扫描,并启用双重验证。 使用过程中,请遵守当地法律法规,使用安全网络环境,保护账户安全,警惕诈骗,理性投资。 本文仅供参考,不构成投资建议,数字资产交易风险自负。

H5和小程序与APP的区别 H5和小程序与APP的区别 Apr 06, 2025 am 10:42 AM

H5、小程序和APP的主要区别在于:技术架构:H5基于网页技术,小程序和APP为独立应用程序。体验和功能:H5轻便易用,功能受限;小程序轻量级,交互性好;APP功能强大,体验流畅。兼容性:H5跨平台兼容,小程序和APP受平台限制。开发成本:H5开发成本低,小程序中等,APP最高。适用场景:H5适合信息展示,小程序适合轻量化应用,APP适合复杂功能应用。

gateio交易所app老版本 gateio交易所app老版本下载渠道 gateio交易所app老版本 gateio交易所app老版本下载渠道 Mar 04, 2025 pm 11:36 PM

Gateio 交易所 app 老版本下载渠道,涵盖官方、第三方应用市场、论坛社区等途径,还给出下载注意事项,帮你轻松获取老版本,解决新版本使用不适或设备兼容问题。

公司安全软件与应用冲突怎么办?HUES安全软件导致常用软件无法打开如何排查? 公司安全软件与应用冲突怎么办?HUES安全软件导致常用软件无法打开如何排查? Apr 01, 2025 pm 10:48 PM

公司安全软件与应用兼容性问题及排查方法许多企业为了保障内网安全,会安装安全软件。然而,安全软件有时...

H5和小程序如何选择 H5和小程序如何选择 Apr 06, 2025 am 10:51 AM

H5和小程序的选择取决于需求。对于跨平台、快速开发和高扩展性的应用,选择H5;对于原生体验、丰富功能和平台依附性的应用,选择小程序。

H5页面制作和微信小程序有什么不同 H5页面制作和微信小程序有什么不同 Apr 05, 2025 pm 11:51 PM

H5更灵活,可定制性强,但需要娴熟的技术;小程序上手快,维护便捷,但受限于微信框架。

币安虚拟币怎么买进卖出详细教程 币安虚拟币怎么买进卖出详细教程 Mar 18, 2025 pm 01:36 PM

本文提供2025年更新的币安虚拟货币买卖简明指南,详细讲解了在币安平台上进行虚拟货币交易的操作步骤。指南涵盖了法币购买USDT、币币交易购买其他币种(如BTC)以及卖出操作,包括市价交易和限价交易两种方式。 此外,指南还特别提示了法币交易的支付安全和网络选择等关键风险,帮助用户安全、高效地进行币安交易。 通过本文,您可以快速掌握在币安平台上买卖虚拟货币的技巧,降低交易风险。

全国首个双核文旅数智人!腾讯云助力花果山景区接入DeepSeek 让'齐天大圣”更智能、更有温度 全国首个双核文旅数智人!腾讯云助力花果山景区接入DeepSeek 让'齐天大圣”更智能、更有温度 Mar 12, 2025 pm 12:57 PM

连云港花果山景区携手腾讯云,推出文旅行业首个“双核大脑”数智人——齐天大圣!3月1日,景区正式将齐天大圣接入DeepSeek平台,使其同时具备腾讯混元和DeepSeek两大AI模型能力,为游客带来更智能、更贴心的服务体验。花果山景区此前已基于腾讯混元大模型推出了数智人齐天大圣。此次腾讯云进一步利用大模型知识引擎等技术,为其接入DeepSeek,实现“双核”升级。这使得齐天大圣的互动能力更上一层楼,响应速度更快,理解能力更强,也更具温度。齐天大圣拥有强大的自然语言处理能力,能够理解游客各种提问方式

See all articles