香蕉视频app

Keep on going never give up.

Let's Go

Asp.Net MVC 接入第三方登錄(QQ、新浪等)

Asp.NetLonely2019-09-03 13:43:40250次6條

Asp.Net MVC接入第三方登錄,目前比較廣泛使用的有QQ登錄、新浪登錄、微信登錄、百度登錄,下面我們來說說怎么實現網站第三方登錄功能。

一開始沒開始動手做之前,我反復看了官方文檔十幾遍都沒搞清楚到底咋回事,我該如何實現?用戶登錄成功后我能拿到的數據有哪些?文檔看不明白,那怎么辦?找個Demo看一下唄,百度了一圈之后發現網上的Demo都比較舊或者比較簡陋,有些還是WebForm的,最后在CSDN花幣下了兩個Demo配合官網文檔一看,頓然醒悟,不過有些Demo比較過分啊,就拼了個鏈接字符串跳轉到登錄授權頁后面就沒了,浪費幣#aru_39#

官方文檔傳送

Q Q登錄文檔:

香蕉视频app 新浪登錄文檔:

微信登錄文檔:

香蕉视频app 百度登錄文檔:

前期準備工作

基本上接入第三方登錄就那幾個步驟,有個別平臺流程不太一樣但都大同小異,弄清楚了流程舉一反三,有了思路實現起來代碼就好寫了。以QQ登錄為例,流程為:

香蕉视频app (1)、申請AppId和AppKey,有些平臺叫法不一樣如新浪的叫AppKey、AppSecret但都是同一個東西。

香蕉视频app (2)、獲取Authorization Code。

(3)、通過Authorization Code獲取授權Access Token。

(4)、使用Access Token來獲取用戶的OpenID。(這一步新浪沒有,新浪的叫Uid,在上一步直接和Token等信息一起返回了)

香蕉视频app (5)、使用Access Token以及OpenID獲取登錄用戶信息。到這一步拿到用戶信息基本上就已經算結束了啊,注冊用戶綁定那些按照自己的業務流程來就行了。

在申請AppKey、AppSecret之前,作為開發者,你需要準備以下資料:

1、基本信息:主要為網站名稱、網站類別、網站簡介、網站Logo。

香蕉视频app 2、平臺信息:主要為網站地址、網站回調域、主板單位名稱、網站備案號(沒備案其實也可以的)。

香蕉视频app 在這里就不得不說騰訊有點惡心了,要認證開發者身份還必須手持身份證上傳,認證開發者身份完之后才能創建應用申請Key。我就特別討厭要一堆個人信息的網站,這點還是新浪比較友好,申請Key新浪這邊門檻就低很多了,直接有一個新浪賬號就能申請到Key了,只不過是沒通過審核的,能測試用不就好了。

另外這些Key,某寶上可以買到,30RMB左右,提供網站信息,回調域就可以了。微信這邊個人好像申請不了,也不廢話這么多了,這一步略過,在有AppKey、AppSecret的情況下,我們來到第二步開始編碼。

開始編寫代碼

本文節選代碼,完整Deom文章底部提供下載地址。

香蕉视频app C#是一門面向對象語言,既然是面向對象就得用上面向對象思想啊,還記得面向對象三大特性是什么嗎?對沒錯就是封裝、繼承、多態,必須章口就萊#newtieba_10#

image.png

(1)、新建ClassLibrary(類庫)取名為OAuth,把各種平臺的登錄寫在這里。

香蕉视频app (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>
        /// 請求的基本網址
        /// 如:http://api.weibo.com
        /// </summary>
        string BaseUrl { get; }

        /// <summary>
        /// AppKey
        /// </summary>
        string AppKey { get; }

        /// <summary>
        /// AppSecret
        /// </summary>
        string AppSecret { get; }

        /// <summary>
        /// 回調域名
        /// </summary>
        string Domain { get; }
    }
}

香蕉视频app (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; }
    }
}

香蕉视频app (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所以就懶得寫了,有興趣的同學自行完善其他平臺的接入。

完整Deom戳這里 --> 傳送門

暗錨,解決錨點偏移

文章評論

    嘿,來試試登錄吧!