Asp.Net MVC接入第三方登录,目前比较广泛使用的有QQ登录、新浪登录、微信登录、百度登录,下面我们来说说怎么实现网站第三方登录功能。
一开始没开始动手做之前,我反复看了官方文档十几遍都没搞清楚到底咋回事,我该如何实现?用户登录成功后我能拿到的数据有哪些?文档看不明白,那怎么办?找个Demo看一下呗,百度了一圈之后发现网上的Demo都比较旧或者比较简陋,有些还是WebForm的,最后在CSDN花币下了两个Demo配合官网文档一看,顿然醒悟,不过有些Demo比较过分啊,就拼了个链接字符串跳转到登录授权页后面就没了,浪费币#aru_39#
Q Q登录文档:传送门
新浪登录文档:传送门
微信登录文档:传送门
百度登录文档:传送门
基本上接入第三方登录就那几个步骤,有个别平台流程不太一样但都大同小异,弄清楚了流程举一反三,有了思路实现起来代码就好写了。以QQ登录为例,流程为:
(1)、申请AppId和AppKey,有些平台叫法不一样如新浪的叫AppKey、AppSecret但都是同一个东西。
(2)、获取Authorization Code。
(3)、通过Authorization Code获取授权Access Token。
(4)、使用Access Token来获取用户的OpenID。(这一步新浪没有,新浪的叫Uid,在上一步直接和Token等信息一起返回了)
(5)、使用Access Token以及OpenID获取登录用户信息。到这一步拿到用户信息基本上就已经算结束了啊,注册用户绑定那些按照自己的业务流程来就行了。
在申请AppKey、AppSecret之前,作为开发者,你需要准备以下资料:
1、基本信息:主要为网站名称、网站类别、网站简介、网站Logo。
2、平台信息:主要为网站地址、网站回调域、主板单位名称、网站备案号(没备案其实也可以的)。
在这里就不得不说腾讯有点恶心了,要认证开发者身份还必须手持身份证上传,认证开发者身份完之后才能创建应用申请Key。我就特别讨厌要一堆个人信息的网站,这点还是新浪比较友好,申请Key新浪这边门槛就低很多了,直接有一个新浪账号就能申请到Key了,只不过是没通过审核的,能测试用不就好了。
另外这些Key,某宝上可以买到,30RMB左右,提供网站信息,回调域就可以了。微信这边个人好像申请不了,也不废话这么多了,这一步略过,在有AppKey、AppSecret的情况下,我们来到第二步开始编码。
本文节选代码,完整Deom文章底部提供下载地址。
C#是一门面向对象语言,既然是面向对象就得用上面向对象思想啊,还记得面向对象三大特性是什么吗?对没错就是封装、继承、多态,必须章口就莱#newtieba_10#
(1)、新建ClassLibrary(类库)取名为OAuth,把各种平台的登录写在这里。
(2)、新建一个接口,取名为IOAuthConfig.cs,代码如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OAuth { public interface IOAuthConfig { /// <summary> /// 请求的基本网址 /// 如:https://api.weibo.com /// </summary> string BaseUrl { get; } /// <summary> /// AppKey /// </summary> string AppKey { get; } /// <summary> /// AppSecret /// </summary> string AppSecret { get; } /// <summary> /// 回调域名 /// </summary> string Domain { get; } } }
(3)、新建一个抽象类,取名为BaseOAuthConfig.cs,代码如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OAuth { internal abstract class BaseOAuthConfig : IOAuthConfig { private Dictionary<string, string> dicConfig = null; /// <summary> /// 构造函数 /// </summary> protected BaseOAuthConfig() { dicConfig = GetConfig(); } protected abstract Dictionary<string, string> GetConfig(); #region 实现 IOAuthConfig 接口成员 public string BaseUrl => dicConfig["BaseUrl"]; public string AppKey => dicConfig["AppKey"]; public string AppSecret => dicConfig["AppSecret"]; public string Domain => dicConfig["Domain"]; #endregion } }
(4)、新建一个密封类QQOAuthConfig.cs继承于BaseOAuthConfig,并重写抽象方法GetConfig()。这里的Config.GetValue(),Config是封装的一个类,用于读取和设置配置文件的信息,配置信息存进数据库也可以,因为这些数据不会经常改动,所以我把它存到配置文件,我想读取配置文件应该要比查数据库速度要快吧。
using Common.Util; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OAuth.QQ { internal sealed class QQOAuthConfig : BaseOAuthConfig { /// <summary> /// 获取QQ登录API配置 /// </summary> /// <returns>返回配置信息</returns> protected override Dictionary<string, string> GetConfig() { Dictionary<string, string> dic = new Dictionary<string, string>(4); dic.Add("BaseUrl", Config.GetValue("QQBaseUrl")); dic.Add("AppKey", Config.GetValue("QQAppKey")); dic.Add("AppSecret", Config.GetValue("QQAppSecret")); dic.Add("Domain", Config.GetValue("CallBackDomain")); return dic; } } }
(5)、建立Model,代码如下所示:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OAuth.QQ { public class QQModel { /// <summary> /// 接口调用凭证 /// </summary> public string Access_token { get; set; } /// <summary> /// access_token接口调用凭证超时时间,单位(秒) /// </summary> public string Expires_in { get; set; } /// <summary> /// 用户刷新access_token /// </summary> public string Refresh_token { get; set; } } public class QQUser { /// <summary> /// OpenID是此网站上或应用中唯一对应用户身份的标识,网站或应用可将此ID进行存储,便于用户下次登录时辨识其身份,或将其与用户在网站上或应用中的原有账号进行绑定。 /// </summary> public string Openid { get; set; } } public class QQUserInfo { /// <summary> /// 返回码,0: 正确返回,其它: 失败。 /// </summary> public int Ret { get; set; } /// <summary> /// 如果ret小于0,会有相应的错误信息提示,返回数据全部用UTF-8编码。 /// </summary> public string Msg { get; set; } /// <summary> /// 用户在QQ空间的昵称。 /// </summary> public string Nickname { get; set; } /// <summary> /// 大小为30×30像素的QQ空间头像URL。 /// </summary> public string Figureurl { get; set; } /// <summary> /// 大小为50×50像素的QQ空间头像URL。 /// </summary> public string Figureurl_1 { get; set; } /// <summary> /// 大小为100×100像素的QQ空间头像URL。 /// </summary> public string Figureurl_2 { get; set; } /// <summary> /// 大小为40×40像素的QQ头像URL。 /// </summary> public string Figureurl_qq_1 { get; set; } /// <summary> /// 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。 /// </summary> public string Figureurl_qq_2 { get; set; } /// <summary> /// 性别,如果获取不到则默认返回"男"。 /// </summary> public string Gender { get; set; } } }
(6)、把登录流程封装成一个类,在需要的地方调用就可以了,代码如下所示:
using Common.Util; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; namespace OAuth.QQ { public class QQ { private IOAuthConfig config = new QQOAuthConfig(); //获取配置信息 /// <summary> /// 获取Authorization Code请求地址,请求方法:Get /// </summary> /// <param name="callback">回调函数名称</param> /// <param name="state">client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。请务必严格按照流程检查用户与state参数状态的绑定。</param> /// <returns>返回Authorization Code请求地址</returns> public string GetAuthCodeUrl(string callback, out string state) { string api = "/oauth2.0/authorize"; string callbackUrl = HttpUtility.UrlEncode(config.Domain + callback); state = RandomHelper.GetRandomString(16); string url = string.Format("{0}{1}?response_type=code&client_id={2}&redirect_uri={3}&state={4}", config.BaseUrl, api, config.AppKey, callbackUrl, state); return url; } /// <summary> /// 获取Access Token请求地址,请求方法:Get /// </summary> /// <param name="code">Authorization Code</param> /// <param name="callback">回调函数名称</param> /// <returns>返回Access Token请求地址</returns> public string GetAccessTokenUrl(string code, string callback) { string api = "/oauth2.0/token"; string callbackUrl = HttpUtility.UrlEncode(config.Domain + callback); string url = string.Format("{0}{1}?grant_type=authorization_code&client_id={2}&client_secret={3}&code={4}&redirect_uri={5}", config.BaseUrl, api, config.AppKey, config.AppSecret, code, callbackUrl); return url; } /// <summary> /// 获取Access Token /// </summary> /// <param name="url">Access Token请求地址</param> /// <returns>返回access_token</returns> public string GetAccessToken(string url) { try { string data = RequestHelper.HttpGet(url, Encoding.UTF8); //成功返回数据内容:access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14s //失败情况和其他需求,自行根据文档返回码完善 string[] arr = data.Split('&'); string[] temp = arr[0].Split('='); return temp[1]; } catch { throw; } } /// <summary> /// 获取用户OpenID /// </summary> /// <param name="access_token">获取到的access token</param> /// <returns>返回OpenID</returns> public string GetOpenID(string access_token) { try { string api = "/oauth2.0/me"; string url = string.Format("{0}{1}?access_token={2}", config.BaseUrl, api, access_token); string data = RequestHelper.HttpGet(url, Encoding.UTF8); // 返回数据内容:callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} ); int startIndex = data.IndexOf("(") + 1; int endIndex = data.IndexOf(")"); int length = endIndex - startIndex; QQUser qqUser = data.Substring(startIndex, length).Trim().ToObject<QQUser>(); return qqUser.Openid; } catch { throw; } } /// <summary> /// 获取用户信息 /// </summary> /// <param name="access_token">获取到的access token</param> /// <param name="openid">用户的ID,与QQ号码一一对应。 </param> /// <returns>返回QQUserInfo</returns> public QQUserInfo GetQQUserInfo(string access_token, string openid) { try { string api = "/user/get_user_info"; string url = string.Format("{0}{1}?access_token={2}&oauth_consumer_key={3}&openid={4}&format=json", config.BaseUrl, api, access_token, config.AppKey, openid); string data = RequestHelper.HttpGet(url, Encoding.UTF8); return data.ToObject<QQUserInfo>(); } catch { throw; } } } }
(7)、流程已经写好了,下面在登录的控制器调用就好了,代码如下所示:
[HttpGet] public ActionResult QQLogin() { QQ qqRequest = new QQ(); string salt; string url = qqRequest.GetAuthCodeUrl("Test/QQCallback", out salt); Session["QQLoginSalt"] = salt; return Redirect(url); } [HttpGet] public ActionResult QQCallback() { //图演示方便直接写这了 string backSalt = Request.QueryString["state"]; string code = Request.QueryString["code"]; //此code会在10分钟内过期 //判断防止跨站请求伪造(CSRF)攻击等…… QQ qqRequest = new QQ(); string url = qqRequest.GetAccessTokenUrl(code, "Test/QQCallback"); string access_token = qqRequest.GetAccessToken(url); string openid = qqRequest.GetOpenID(access_token); QQUserInfo qqUserInfo = qqRequest.GetQQUserInfo(access_token, openid); //根据你的业务逻辑自己完善 if (qqUserInfo.Ret == 0 && string.IsNullOrWhiteSpace(qqUserInfo.Msg)) //成功 { return Content("登录成功!欢迎您:"+ qqUserInfo.Nickname); } else //失败 { return Content("登录失败!返回码:" + qqUserInfo.Ret); } }
在这里登录成功以后就能拿到用户的信息了,自己再根据业务需要完成其他的一些操作例如注册绑定账号等等,搞清楚以后很容易举一反三将别的平台的接入方法也写进来,这里我只写了QQ登录和新浪登录,因为我搞不到微信登录的Key所以就懒得写了,有兴趣的同学自行完善其他平台的接入。
文章评论