当前位置:网站首页>微信公众号OAuth2.0授权登录并显示用户信息
微信公众号OAuth2.0授权登录并显示用户信息
2022-07-07 17:32:00 【阿升】
OAuth是Open Authorization的缩写,即开放授权。OAuth协议为用户资源的授权提供了一个安全的、开放而又简单的标准,即在第三方无需使用用户名和密码的情况下就可以申请到该用户的资源。常见的OAuth2.0授权场景有:QQ、微信、今日头条、抖音、微博、GitHub等。比如,使用QQ授权登录第三方网站的场景,这样就免去了用户注册账号的过程,提高了用户访问体验。
一.OAuth2.0原理和网页授权
1.OAuth2.0原理
根据授权码模式,第一步获取code,第二步获取access_token,第三步携带access_token获取资源。每一步都是通过API实现的: OAuth相关方法都在Senparc.Weixin.MP.AdvancedAPIs命名空间下的OAuthApi类中:
2.微信公众号网页授权
网页授权流程分为四步:
(1)获取code:引导用户进入授权页面同意授权
(2)获取access_token:通过code换取网页授权access_token
(3)刷新access_token:如果需要,开发者刷新网页授权来避免过期
(4)请求资源:通过网页授权access_token和openid获取用户基本信息
3.对UnionID的理解
当开发者拥有多个移动应用、网站应用和公众帐号时,用户账号是不同的。微信官方是通过微信开放平台来解决这个问题的,对同一个微信开放平台下的不同应用[比如,移动应用、网站应用和公众帐号等],UnionID是相同的,即是同一个账号。
说明:自己想要的另外一种方式是通过手机号码来识别同一个用户账号。
二.用Senparc实现OAuth2.0的过程
1.snsapi_userinfo和snsapi_base
snsapi_userinfo需要用户确认同意的,而snsapi_base是静态获取,用户无感知。 需要说明的是特殊场景下的静默授权:对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使scope为snsapi_userinfo,也是静默授权,用户无感知。
2.登录入口
登录入口位于WeiXinMPSDK/Samples/MP/Senparc.Weixin.Sample.MP/Controllers/OAuth2Controller.cs文件中的Index()方法:
// 用户尝试进入的需要登录的页面
public ActionResult Index(string returnUrl)
{
ViewData["returnUrl"] = returnUrl;
//此页面引导用户点击授权
ViewData["UrlUserInfo"] = OAuthApi.GetAuthorizeUrl(appId, "http://fengling.nat300.top/oauth2/UserInfoCallback?returnUrl=" + returnUrl.UrlEncode(), null, OAuthScope.snsapi_userinfo);//snsapi_userinfo方式回调地址
ViewData["UrlBase"] = OAuthApi.GetAuthorizeUrl(appId, "http://fengling.nat300.top/oauth2/BaseCallback?returnUrl=" + returnUrl.UrlEncode(), null, OAuthScope.snsapi_base);//snsapi_base方式回调地址
return View();
}
其中,OAuthApi.GetAuthorizeUrl()方法的具体实现如下所示:
/// <summary>
/// 获取验证地址
/// </summary>
/// <param name="appId">公众号的唯一标识</param>
/// <param name="redirectUrl">授权后重定向的回调链接地址,请使用urlencode对链接进行处理</param>
/// <param name="state">重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节</param>
/// <param name="scope">应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)</param>
/// <param name="responseType">返回类型,请填写code(或保留默认)</param>
/// <param name="addConnectRedirect">加上后可以解决40029-invalid code的问题(测试中)</param>
/// <returns></returns>
public static string GetAuthorizeUrl(string appId, string redirectUrl, string state, OAuthScope scope, string responseType = "code", bool addConnectRedirect = true)
{
var url = string.Format("https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type={2}&scope={3}&state={4}{5}#wechat_redirect",
appId.AsUrlData(), redirectUrl.AsUrlData(), responseType.AsUrlData(), scope.ToString("g").AsUrlData(), state.AsUrlData(), addConnectRedirect ? "&connect_redirect=1" : "");
/* 这一步发送之后,客户会得到授权页面,无论同意或拒绝,都会返回redirectUrl页面。
* 如果用户同意授权,页面将跳转至redirect_uri/?code=CODE&state=STATE。这里的code用于换取access_token(和通用接口的access_token不通用)
* 若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE
*/
return url;
}
微信开发者工具中打开的页面:
3.OAuthScope.snsapi_userinfo方式回调
该方式位于WeiXinMPSDK/Samples/MP/Senparc.Weixin.Sample.MP/Controllers/OAuth2Controller.cs文件中的UserInfoCallback()方法:
/// <summary>
/// OAuthScope.snsapi_userinfo方式回调
/// </summary>
/// <param name="code"></param>
/// <param name="returnUrl">用户最初尝试进入的页面</param>
/// <returns></returns>
public ActionResult UserInfoCallback(string code, string returnUrl)
{
if (string.IsNullOrEmpty(code)) { return Content("您拒绝了授权!"); }
OAuthAccessTokenResult result = null;
//通过,用code换取access_token
try
{
result = OAuthApi.GetAccessToken(appId, appSecret, code);
}
catch (Exception ex)
{
return Content(ex.Message);
}
if (result.errcode != ReturnCode.请求成功)
{
return Content("错误:" + result.errmsg);
}
//下面2个数据也可以自己封装成一个类,储存在数据库中(建议结合缓存)
//如果可以确保安全,可以将access_token存入用户的cookie中,每一个人的access_token是不一样的
// HttpContext.Session.SetString("OAuthAccessTokenStartTime", SystemTime.Now.ToString());
// HttpContext.Session.SetString("OAuthAccessToken", result.ToJson());
//因为第一步选择的是OAuthScope.snsapi_userinfo,这里可以进一步获取用户详细信息
try
{
if (!string.IsNullOrEmpty(returnUrl)) { return Redirect(returnUrl); }
OAuthUserInfo userInfo = OAuthApi.GetUserInfo(result.access_token, result.openid);
return View(userInfo);
}
catch (ErrorJsonResultException ex)
{
return Content(ex.Message);
}
}
首先是获取到access_token,然后如果returnUrl为空,那么返回用户信息页面。获取access_token代码为OAuthApi.GetAccessToken(appId, appSecret, code);
:
/// <summary>
/// 获取AccessToken(OAuth专用)
/// </summary>
/// <param name="appId">公众号的唯一标识</param>
/// <param name="secret">公众号的appsecret</param>
/// <param name="code">code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。</param>
/// <param name="grantType">填写为authorization_code(请保持默认参数)</param>
/// <returns></returns>
public static OAuthAccessTokenResult GetAccessToken(string appId, string secret, string code, string grantType = "authorization_code")
{
var url = string.Format(Config.ApiMpHost + "/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type={3}", appId.AsUrlData(), secret.AsUrlData(), code.AsUrlData(), grantType.AsUrlData());
return CommonJsonSend.Send<OAuthAccessTokenResult>(null, url, null, CommonJsonSendType.GET);
}
返回用户信息页面代码为OAuthApi.GetUserInfo(result.access_token, result.openid)
:
/// <summary>
/// 获取用户基本信息
/// </summary>
/// <param name="oauthAccessToken">调用接口凭证(OAuth专用)</param>
/// <param name="openId">普通用户的标识,对当前公众号唯一</param>
/// <param name="lang">返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语</param>
/// <returns></returns>
public static OAuthUserInfo GetUserInfo(string oauthAccessToken, string openId, Language lang = Language.zh_CN)
{
var url = string.Format(Config.ApiMpHost + "/sns/userinfo?access_token={0}&openid={1}&lang={2}", oauthAccessToken.AsUrlData(), openId.AsUrlData(), lang.ToString("g").AsUrlData());
return CommonJsonSend.Send<OAuthUserInfo>(null, url, null, CommonJsonSendType.GET);
}
当点击"点击这里测试snsapi_userinfo"链接时: 这时显示弹出授权页面,点击"同意"按钮:
4.OAuthScope.snsapi_base方式回调
该方式位于WeiXinMPSDK/Samples/MP/Senparc.Weixin.Sample.MP/Controllers/OAuth2Controller.cs文件中的BaseCallback()方法:
/// <summary>
/// OAuthScope.snsapi_base方式回调
/// </summary>
/// <param name="code"></param>
/// <param name="returnUrl">用户最初尝试进入的页面</param>
/// <returns></returns>
public ActionResult BaseCallback(string code, string returnUrl)
{
try
{
if (string.IsNullOrEmpty(code)) { return Content("您拒绝了授权!"); }
// 通过,用code换取access_token
var result = OAuthApi.GetAccessToken(appId, appSecret, code);
if (result.errcode != ReturnCode.请求成功) { return Content("错误:" + result.errmsg); }
//下面2个数据也可以自己封装成一个类,储存在数据库中(建议结合缓存)
//如果可以确保安全,可以将access_token存入用户的cookie中,每一个人的access_token是不一样的
// HttpContext.Session.SetString("OAuthAccessTokenStartTime", SystemTime.Now.ToString());
// HttpContext.Session.SetString("OAuthAccessToken", result.ToJson());
//因为这里还不确定用户是否关注本微信,所以只能试探性地获取一下
OAuthUserInfo userInfo = null;
try
{
//已关注,可以得到详细信息
userInfo = OAuthApi.GetUserInfo(result.access_token, result.openid);
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
ViewData["ByBase"] = true;
return View("UserInfoCallback", userInfo);
}
catch (ErrorJsonResultException ex)
{
//未关注,只能授权,无法得到详细信息
//这里的ex.JsonResult可能为:"{\"errcode\":40003,\"errmsg\":\"invalid openid\"}"
return Content("用户已授权,授权Token:" + result, "text/html", Encoding.UTF8);
}
}
catch (Exception ex)
{
WeixinTrace.SendCustomLog("BaseCallback发生错误", ex.ToString());
return Content("发生错误:" + ex.ToString());
}
}
获取access_token和用户信息的方法与OAuthScope.snsapi_userinfo方式回调完全相同,不再介绍。当点击"点击这里测试snsapi_base"链接时: 可见OAuthScope.snsapi_base方式回调是静默授权,并不需要显示弹出授权页面。
参考文献:
[1]微信网页授权并获取用户信息的方法:https://juejin.cn/post/6844903648061816840
[2]wangfengyuan/wxAuthorize:https://github.com/wangfengyuan/wxAuthorize
[3]微信公众号OAuth2.0网页授权:https://www.bilibili.com/video/BV1Vb411t7XR?p=1
[4]网页授权:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
[5]微信公众平台开发--微信授权登录(OAuth2.0):https://www.cnblogs.com/0201zcr/p/5131602.html
人工智能干货推荐 专注于人工智能领域的技术分享
游戏元宇宙 专注于游戏领域的技术分享
边栏推荐
- 让这个 CRMEB 单商户微信商城系统火起来,太好用了!
- L1-019 who falls first (Lua)
- “本真”是什么意思
- MySQL、sqlserver oracle数据库连接方式
- [Verilog advanced challenge of Niuke network question brushing series] ~ multi bit MUX synchronizer
- 【Confluence】JVM内存调整
- Longest common prefix (leetcode question 14)
- Pasqal首席技术官:模拟量子计算率先为工业带来量子优势
- how to prove compiler‘s correctness
- 杰理之关于 TWS 配对方式配置【篇】
猜你喜欢
9 atomic operation class 18 Rohan enhancement
The strength index of specialized and new software development enterprises was released, and Kirin Xin'an was honored on the list
多个kubernetes集群如何实现共享同一个存储
8 CAS
Jerry's headphones with the same channel are not allowed to pair [article]
Business experience in virtual digital human
小试牛刀之NunJucks模板引擎
关于ssh登录时卡顿30s左右的问题调试处理
让这个 CRMEB 单商户微信商城系统火起来,太好用了!
Le PGR est - il utile au travail? Comment choisir une plate - forme fiable pour économiser le cœur et la main - d'œuvre lors de la préparation de l'examen!!!
随机推荐
POJ 1182: food chain (parallel search) [easy to understand]
谷歌seo外链Backlinks研究工具推荐
Pasqal首席技术官:模拟量子计算率先为工业带来量子优势
指定opencv非标准安装的版本
How to buy stocks on your mobile phone and open an account? Is it safe to open an account
Business experience in virtual digital human
Introduction to bit operation
Make insurance more "safe"! Kirin Xin'an one cloud multi-core cloud desktop won the bid of China Life Insurance, helping the innovation and development of financial and insurance information technolog
How to share the same storage among multiple kubernetes clusters
[confluence] JVM memory adjustment
R language ggplot2 visualization: use the ggviolin function of ggpubr package to visualize the violin diagram, set the palette parameter to customize the filling color of violin diagrams at different
解决远程rviz报错问题
杰理之测试盒配置声道【篇】
2022年投资哪个理财产品收益高?
L1-023 output gplt (Lua)
[Verilog advanced challenge of Niuke network question brushing series] ~ multi bit MUX synchronizer
LeetCode1051(C#)
Unable to link the remote redis server (solution 100%
L1-027 rental (Lua)
Notes...