一篇對OAuth2.0開發實例的介紹


今天看到了博友對SSO的文章,SSO單點登錄的講解突然想寫一篇關於OAuth2.0用戶授權的介紹。

應用場景:在APP或者網頁接入一些第三方應用時,時長會需要用戶登錄另一個合作平台,比如QQ,微博,微信的授權登錄。

使用好處:這樣可以免去用戶同步的麻煩,同時也增加了用戶信息的安全。

交互模型

1.接口需要經過“1 次認證+1 次授權+1 次審核” 即可獲得 accesstoken,請求業 務模式說明:
1.1 來源認證: 用戶訪問您的移動應用(網頁),請根據確認合作名稱(例如: 金融網)作為 來源認證,服務端驗證無誤后會返回一個臨時令牌( 有效期 30 分鍾)。開發授權的話這一步就可以忽略來源認證
1.2 用戶授權: 接收到臨時令牌加入本次請求回調地址( 回調地址格式: https:// 或 http://外網可訪問網址), 用戶登錄授權通過后將會在回調地址中返回授權通 過碼( 參數名: code)。

1.3 令牌審核: 第一步訪問得到臨時令牌,第二次請求得到授權通過碼, 授權通過碼加臨時令牌通過后將會返回 AccessToken( 注意臨時令牌有效時間 30 分鍾)。

臨時令牌的時間可以根據實際情況設置

第一步獲取臨時令牌

 //
        // GET: /API2/UserAuth/
        /// <summary>
        /// 獲取臨時令牌
        /// </summary>
        /// <param name="source">請求來源</param>
        /// <returns>
        /// tem_token:xxxxx,//臨時令牌(有效時間30分鍾)
        /// </returns>
        public ActionResult GetTemToken(string source)
        {
            if (source.Equals("XX來源"))
            {
                var tem_token = "jxbtem_" + Tools.GetRandomString(11);//獲取十一位唯一碼
                //改用Session保存
                System.Web.HttpContext.Current.Application[tem_token] = source;
             //寫入有效期
                System.Web.HttpContext.Current.Application["temtokenLimit"] = DateTime.Now.AddMinutes(30);
                return Tools.GetResult(new { tem_token });
            }
            else
            {
                return Tools.GetResult("未知請求來源", null);
            }
        }

第二步使用臨時令牌用戶授權

 /// <summary>
        /// 使用臨時令牌用戶授權
        /// </summary>
        /// <param name="callbackurl">回調地址</param>
        /// <param name="temtoken">臨時令牌</param>
        /// <returns>
        /// 驗證通過跳轉用戶授權,驗證失敗顯示失敗原因
        /// </returns>
        public ActionResult UserAuthorization(string callbackurl, string temtoken)
        {
            //校驗參數合法性
            if (string.IsNullOrEmpty(callbackurl) || string.IsNullOrEmpty(temtoken))
            {
                return Tools.GetResult("非法請求");
            }
            if (callbackurl.Trim().Substring(0, 4).ToLower().IndexOf("http") == -1)
            {
                return Tools.GetResult("回調地址不合法");
            }
            //傳遞重要參數
            object session = System.Web.HttpContext.Current.Application[temtoken];
            object temtokenLimit = System.Web.HttpContext.Current.Application["temtokenLimit"];
            if (session != null && temtokenLimit != null)
            {
                Session["callbackurl"] = callbackurl;
                Session["temtokenLimit"] = temtokenLimit;
                Session["temtoken"] = temtoken;
                Session["source"] = session;
                System.Web.HttpContext.Current.Application.Remove(temtoken);
                System.Web.HttpContext.Current.Application.Remove("temtokenLimit");
                return RedirectToAction("GotoUserAut");
            }
            else
            {
                System.Web.HttpContext.Current.Application.Remove(temtoken);
                System.Web.HttpContext.Current.Application.Remove("temtokenLimit");
                return Tools.GetResult("令牌驗證失敗");
            }
        }

第三步跳轉用戶授權頁面

 /// <summary>
        /// 跳轉用戶授權頁面
        /// </summary>
        /// <returns></returns>
        public ActionResult GotoUserAut(string MSG)
        {
            //驗證授權信息
            if (!string.IsNullOrEmpty(MSG))
            {
                ViewBag.MSG = MSG;
                return View();
            }
            //獲取用戶信息
            string uname = Request["uname"];
            string pwd =Request["pwd"];
            string md5 = Request["md5"];
            ViewBag.MSG = "";
            var source = Session["source"];
            ViewBag.Source = source;
            if (string.IsNullOrEmpty(uname) || string.IsNullOrEmpty(pwd))
            {
                ViewBag.MSG = "請輸入用戶名密碼";
                return View();
            }
            if (string.IsNullOrEmpty(md5) || md5.Equals("0"))
            {
                pwd = ToolKit.EncryptMd5(pwd);
            }
            //驗證用戶信息
            if (!string.IsNullOrEmpty(uname) && !string.IsNullOrEmpty(pwd))
            {
                var basma = DBHelper.BASMA.FirstOrDefault(ma => ma.MA001.Equals(uname) && ma.MA002.Equals(pwd));
                if (basma != null)
                {
                    return View(basma);
                }
                else
                {
                    ViewBag.MSG = "用戶名或密碼錯誤";
                    return View();
                }
            }
            return View();
        }

第四步確認授權登錄

 /// <summary>
        /// 確認授權登錄
        /// </summary>
        /// <param name="UserId">用戶ID</param>
        /// <returns>
        /// 跳轉第三方回調頁面
        /// </returns>
        public ActionResult StartUserAuthorization(int UserId)
        {
            BASMA UserInfo = null;
            if (UserExist(UserId, out UserInfo))
            {
                var source = Session["source"];
                var callbackurl = Session["callbackurl"];
                var temtoken = Session["temtoken"];
                var temtokenLimit = Session["temtokenLimit"];
                if (source==null|| callbackurl==null || temtoken==null || temtokenLimit==null)
                {
                    return RedirectToAction("GotoUserAut", new { MSG = "授權信息驗證失敗" });
                }
                var accesstoken = "jxb_" + Tools.GetRandomString(14);//獲取十四位唯一碼
                //驗證是否已授權
                var basau = DBHelper.BASAU.FirstOrDefault(au => au.AU001.Equals(UserId) && au.AU002.Equals(source.ToString()));
                if (basau == null)
                {
                    BASAU newbasau = new BASAU();
                    newbasau.AU001 = UserId;
                    newbasau.AU002 = source.ToString();
                    newbasau.AU003 = temtoken.ToString();
                    newbasau.AU004 = DateTime.Parse(temtokenLimit.ToString());
                    newbasau.AU005 = accesstoken;
                    newbasau.AU006 = DateTime.Now;
                    DBHelper.BASAU.InsertOnSubmit(newbasau);
                    DBHelper.SubmitChanges();
                }
                else
                {
                    basau.AU003 = temtoken.ToString();
                    basau.AU004 = DateTime.Parse(temtokenLimit.ToString());
                    basau.AU005 = accesstoken;
                    basau.AU006 = DateTime.Now;
                    DBHelper.SubmitChanges();
                }
                
                //計算回調返回code
                TimeSpan ts = (basau.AU006??DateTime.Now) - new DateTime(1970, 1, 1, 0, 0, 0, 0);//計算時間戳
                string TimeSpan = Convert.ToInt64(ts.TotalMilliseconds).ToString(); //獲得時間戳

                return Redirect(callbackurl + "?code=" + TimeSpan);//回調返回授權碼
            }
            else
            {
                return RedirectToAction("GotoUserAut", new { MSG = "指定授權用戶不存在" });
            }
        }

第五步獲取應用授權令牌

 /// <summary>
        /// 獲取應用授權令牌
        /// </summary>
        /// <param name="code">授權成功返回碼</param>
        /// <param name="temtoken">請求臨時令牌</param>
        /// <returns>
        /// accesstoken:xxxxx,//授權碼
        /// </returns>
        public ActionResult GetAccessToken(string code, string temtoken)
        {
            if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(temtoken))
            {
                return Tools.GetResult("請求參數不能為空", null);
            }
            DateTime dtBase = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            DateTime convertTime = dtBase.Add(new TimeSpan(long.Parse(code) * TimeSpan.TicksPerMillisecond));
            var BASAU = DBHelper.BASAU.FirstOrDefault(au => au.AU006.Equals(convertTime) && au.AU003.Equals(temtoken)&&au.AU004.Value>=DateTime.Now);
            if (BASAU != null)
            {
                return Tools.GetResult(new { accesstoken = BASAU.AU005 });
            }
            else
            {
                return Tools.GetResult("令牌驗證失敗", null);
            }
        }

第六步獲取用戶唯一標識

/// <summary>
        /// 獲取用戶唯一標識
        /// </summary>
        /// <param name="accesstoken">授權令牌</param>
        /// <returns>
        /// MA099:xxxxx,//用戶唯一標識
        /// MA010:xxxxx,//用戶昵稱
        /// </returns>
        public ActionResult GetUserInfo(string accesstoken)
        {
            if (!string.IsNullOrEmpty(accesstoken))
            {
                var basau = DBHelper.BASAU.FirstOrDefault(ua => ua.AU005.Equals(accesstoken));
                if (basau != null)
                {
                    var basma = DBHelper.BASMA.FirstOrDefault(ma => ma.ID.Equals(basau.AU001));
                    if (basma != null)
                    {
                        return Tools.GetResult(new { basma.MA099, basma.MA010 });
                    }
                    else
                    {
                        return Tools.GetResult("用戶信息拉取失敗", null);
                    }
                }
                else
                {
                    return Tools.GetResult("令牌驗證失敗", null);
                }
            }
            else
            {
                return Tools.GetResult("請求參數非法",null);
            }
        }

授權是需要用戶登錄才能授權,如果在自己的應用內或者可以提供用戶標識就可以直接通過。

附上完整的授權頁面代碼:

@model Ecio_Admin.Models.BASMA
@{
    ViewBag.Title = "GotoUserAut";
    Layout = null;
    //獲取用戶ID
    var UserId = "";
    if (Model != null)
    {
        UserId = Model.ID.ToString();
    }
}
<html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <meta charset="UTF-8">
    <title>應用授權</title>
    <link href="~/Content/css/UserAut.css" rel="stylesheet" />
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script type="text/javascript">
        $(function () {
            //授權
            $("#userauth").click(
                function () {
                    location.href = "/API2/UserAuth/StartUserAuthorization?UserId=" + $("#UserId").val();
                });
        });
    </script>
</head>
<body>
    <div class="box">
        <input type="hidden" id="UserId" value="@UserId" />
        <img class="logo" src="~/UploadFiles/IMG/OAuth.png" alt="LOGO" />
        @{
            if (Model != null)
            {
                <h1 class="title">登錄后該應用將獲得以下授權:</h1>
                    <h2 class="title2"><input type="checkbox" checked readonly="readonly" disabled="disabled">將獲取您的基本信息(昵稱、頭像)</h2>
                <form class="form-group" action="#" method="post">
                    <input class="form-btn" style="cursor:pointer;" type="button" id="userauth" value="確定授權" />
                </form>
            }
            else
            {
                <h1 class="title">授權<span>@ViewBag.Source</span>訪問你的XXX賬號</h1>
        <form class="form-group" action="/API2/UserAuth/GotoUserAut" method="post">
            <input class="form-import" type="text" placeholder="請輸入您的賬號" id="uname" name="uname" required autocomplete="off" />
            <input class="form-import" type="password" placeholder="請輸入您的密碼" id="pwd" name="pwd" required autocomplete="off" />
            <div style="color:red;">@ViewBag.MSG</div>
        <input class="form-btn" style="cursor:pointer;" type="submit" id="Login" value="登錄" />
    </form>
            }
}
    </div>
</body>
</html>

代碼其實都是多余的,在編寫時可以按照這種安全機制,去書寫自己的授權邏輯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM