工具:

Visual Studio 2015 update 3

Asp.Net Core 1.0

1 准备工作

申请微信公众平台接口测试帐号,申请网址:(http://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));
}
}
}

WeChatAppBuilderExtensions.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";
}
}

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. 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;
}
}
}

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

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.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";
}
}

WeChatOptions.cs

ASP.NET Core集成微信登录的更多相关文章

  1. [转]ASP.NET Core集成微信登录

    本文转自:http://www.cnblogs.com/early-moon/p/5819760.html 工具: Visual Studio 2015 update 3 Asp.Net Core 1 ...

  2. asp.net core集成MongoDB

    0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.前言及MongoDB的介绍 最近在整合自己的框架,顺便把MongoDBD的最简单CRUD重构一下作为组件化集成到asp.net ...

  3. asp.net core集成CAP(分布式事务总线)

    一.前言 感谢杨晓东大佬为社区贡献的CAP开源项目,传送门在此:.NET Core 事件总线,分布式事务解决方案:CAP 以及 如何在你的项目中集成 CAP[手把手视频教程],之前也在工作中遇到分布式 ...

  4. asp.net core 集成JWT(一)

    [什么是JWT] JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io/ 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT ...

  5. asp.net core 集成JWT(二)token的强制失效,基于策略模式细化api权限

    [前言] 上一篇我们介绍了什么是JWT,以及如何在asp.net core api项目中集成JWT权限认证.传送门:https://www.cnblogs.com/7tiny/p/11012035.h ...

  6. php的laravel框架快速集成微信登录

    最终的解决方案是:https://github.com/liuyunzhuge/php_weixin_provider,详细的介绍请往下阅读. 本文面向的是php语言laravel框架的用户,介绍的是 ...

  7. ABP官方文档翻译 6.2.1 ASP.NET Core集成

    ASP.NET Core 介绍 迁移到ASP.NET Core? 启动模板 配置 启动类 模块配置 控制器 应用服务作为控制器 过滤器 授权过滤器 审计Action过滤器 校验过滤器 工作单元Acti ...

  8. asp.net core 集成 log4net 日志框架

    asp.net core 集成 log4net 日志框架 Intro 在 asp.net core 中有些日志我们可能想输出到数据库或文件或elasticsearch等,如果不自己去实现一个 Logg ...

  9. [Abp 源码分析]十七、ASP.NET Core 集成

    0. 简介 整个 Abp 框架最为核心的除了 Abp 库之外,其次就是 Abp.AspNetCore 库了.虽然 Abp 本身是可以用于控制台程序的,不过那样的话 Abp 就基本没什么用,还是需要集合 ...

随机推荐

  1. css之布局

    布局一直是页面制作很重要的部分,有个良好的布局不仅在页面上呈现很好的效果,还对后续功能扩展有重要的作用.本文主要讨论一下几种布局: 水平居中布局 垂直居中布局 多列布局 自适应布局 stracky-f ...

  2. CSS如何计算优先级?如何计算权重?

    (1) 优先级就近原则,同权重以最近者为准 载入样式以最后载入的样式为准: 同权重下:内联样式表(标签内部) > 嵌入样式表(当前文件) > 外部样式表(外部文件) !import > ...

  3. Assembly.GetManifestResourceNames()获取不到资源文件

    Assembly.GetManifestResourceNames()获取到的是嵌入的资源文件 右键资源文件属性 将生成操作改为嵌入的资源就OK咯

  4. 笔试题: 数据库 已看1 一些关键的sql语句练习 和选择题 有用 sql语句练习 挺好

    一.     选择题 1.SQL语言是( C )语言. A.层次数据库 B.网络数据库 C.关系数据库 D.非数据库     redis 是 3.如果在where子句中有两个条件要同时满足,应该用哪个 ...

  5. hadoop自动安装脚本

    还不能实现完全自动安装,只能算半自动的. 进行交互主要障碍有两点: 1. ssh-keygen的时候需要点击回车. 2. passwd 需要设置密码  如果谁能解决以上两点,欢迎email给我. 另外 ...

  6. c/c++转义字符大全【转自互联网】

    将转义字符收集如下:转义字符 意义 ASCII码值(十进制) \a 响铃(BEL) 007 \b 退格(BS) 008 \f 换页(FF) 012 \n 换行(LF) 010 \r 回车(CR) 01 ...

  7. 第六课 ROS的空间描述和变换

    1.空间描述与变换 有两个坐标系A和B,B坐标系中有一个点P,如何把B坐标系中的P映射到A坐标系呢,这就涉及到空间描述与变换, 先看一下旋转矩阵: 上面中间的行向量中的元素表示在B坐标系当中的元素用A ...

  8. es6基础系列四--字符串的拓展

    1 for...of 字符串的遍历接口 for(let i of "abc"){ console.log(i); } // a // b // c 2 includes 是否包含某 ...

  9. 【转】solr源码导入eclipse

     http://blog.csdn.net/vltic/article/details/19917377   (1)相应的开发环境准备          (1)jdk1.6+的安装和环境变量配置(命令 ...

  10. Note: ENDBOX: Scalable Middlebox Functions Using Client-Side Trusted Execution

    ENDBOX enable secure networking by client-Side trusted execution. What ENDBOX is a scalable middlebo ...