基於C#實現的單點登錄


了解或想探索單點登錄的朋友應該對單點登錄有一個大致的了解,在這里我不在過多的闡述單點登錄的概念。

單點登錄說的通俗一點,就是一處登錄(統一認證中心--Server),處處通行(Client)。

一、第一步我們先來探討探討SSO單點登錄的過程

   1.當Client第一次登錄時,查看自己有沒有局部會話,沒有的話,則重定向到統一認證中心進行進行登錄操作

   2.登錄成功后,Server會保存一個全局會話。並會產生一個token返回給Client

   3.這時又回到了Client,Client拿到Server返回的token並不是直接跳轉到正確的頁面。而是拿着這個token去Server校驗這個token是否是Server產生的,校驗通過了才進行跳轉,同時生成一個局部會話

   4.當第二個Client登錄時,同樣也會先跳轉到Server。然而此時不同的時,這是Server端已經有了一個全局的會話,此時直接重定向到Client,並帶着Server端產生的token

二、上面已經闡述了單點登錄的過程,接下來我們結合着代碼詳細的復現這一過程

  1.Client登錄時,查看自己有沒有局部會話,沒有的話,則重定向到統一認證中心進行進行登錄操作

     public override  void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var request = filterContext.HttpContext.Request;
            var response = filterContext.HttpContext.Response;

            string ReturnURL = request.Url.AbsoluteUri;                                             //請求的路徑
            string Passport = System.Configuration.ConfigurationManager.AppSettings["Server"];      //Server端的路徑
            object isLogin = filterContext.HttpContext.Session["isLogin"];    //局部會話
            
            if (isLogin != null)        //有局部會話的時候直接放行
            {
                return;     
            }
            else
            {
                //這一部分是校驗Server返回的token,第一次登錄的時候沒有token,會直接跳過這部分
            }

       //執行這步操作 filterContext.Result
= new RedirectResult(string.Format("{0}/Login/CheckLogin?redirectUrl={1}", Passport,ReturnURL)); //第一次登錄時沒有局部會話,要跳到Server端進行認證 return; }

   2.跳轉到Server端進行校驗

        public ActionResult CheckLogin(string redirectUrl) {
            object token = Session["token"];
            if (token==null)
            {
                string url = "http://localhost:52666/Login/Login?redirectUrl="+redirectUrl;   //跳轉到登錄界面,並攜着跳轉過來的路徑,以方便后續操作
                return Redirect(url);
            }
            else
            {
                return Redirect(redirectUrl+ "?tokenId=" + token.ToString());
            }
        }
        public ActionResult Login(string redirectUrl) 
        {
            ViewBag.Url = redirectUrl;          
            return View();            //返回登錄界面的視圖
        }

   3.對Server登錄界面輸入的用戶和密碼進行校驗,登錄成功就重定向到Client端

        [HttpPost]
        public ActionResult Login(string userName, string password,string RetrunURL) {

            if (userName=="admin"&&password=="admin")           //登錄成功
            {
                Session["token"] = Guid.NewGuid().ToString();       //生成一個全局會話
                MockDatabase.T_Token.Add(Session["token"].ToString());   //將產生的token寫到數據庫
                return Redirect(RetrunURL + "?tokenId=" + Session["token"].ToString());  //重定向到Client端並攜帶着這個token
            }
            else
            {
                string url = "http://localhost:52666/Login/Login?redirectUrl=" + RetrunURL;     //否則繼續返回到登錄界面
                return Redirect(url);
            }   
        }

   4.這里又跳轉到Client的過濾器中,進行對token的校驗

  public override  void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var request = filterContext.HttpContext.Request;
            var response = filterContext.HttpContext.Response;

            string ReturnURL = request.Url.AbsoluteUri;                                             //請求的路徑
            string Passport = System.Configuration.ConfigurationManager.AppSettings["Server"];      //Server端的路徑
            object isLogin = filterContext.HttpContext.Session["isLogin"];    //局部會話
            
            if (isLogin != null)        //有局部會話的時候直接放行
            {
                return;     
            }
            else
            {
          //執行這步操作
//從Server端跳轉到這里,對token進行校驗 string token = request["tokenId"]; if (!string.IsNullOrWhiteSpace(token)) { using (HttpClient http = new HttpClient()) { HttpContent httpContent = new StringContent(token); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); httpContent.Headers.ContentType.CharSet = "utf-8"; //驗證Tokend是否有效 HttpClient httpClient = new HttpClient(); string url = Passport + "Login/Verify?token="+token; //帶着token請求到Server端的Verify方法進行校驗 HttpResponseMessage res = httpClient.GetAsync(url).Result; string result = res.Content.ReadAsStringAsync().Result; if (res.StatusCode.ToString()=="OK") { if (result == "True") { filterContext.HttpContext.Session["isLogin"] = true; return; //通過校驗就放行 } } } } } filterContext.Result = new RedirectResult(string.Format("{0}/Login/CheckLogin?redirectUrl={1}", Passport,ReturnURL)); //第一次登錄時沒有局部會話,要跳到Server端進行認證 return; }

    5.Server端的Verify方法對token進行校驗(校驗token是否由Server產生的)

      public string Verify(string token) {
            return MockDatabase.T_Token.Contains(token).ToString();   //判斷數據庫中存入的token,有沒有包含傳遞過來的token
        }

    6、當第二個Client登錄時,Server端已經有了全局會話,則直接跳轉回去

       public ActionResult CheckLogin(string redirectUrl) {
            object token = Session["token"];
            if (token==null)
            {
                string url = "http://localhost:52666/Login/Login?redirectUrl="+redirectUrl;   //跳轉到登錄界面,並攜着跳轉過來的路徑,以方便后續操作
                return Redirect(url);
            }
            else
            {
         //走這段邏輯
return Redirect(redirectUrl+ "?tokenId=" + token.ToString()); //當第二個Client登錄時,此時已經有了一個全局會話。會直接跳轉過去並帶上token } }

 

三、上面一步一步的操作不是那么完整,這里給出Server端和Client端過濾器的完整代碼,希望能對研究這塊東西的小伙伴有所幫助

   1.Client端過濾器的代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Mvc;

namespace ClientO.Filter
{
    public class AuthAttribute : ActionFilterAttribute
    {
        public static List<string> Tokens = new List<string>();
        public override  void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var request = filterContext.HttpContext.Request;
            var response = filterContext.HttpContext.Response;

            string ReturnURL = request.Url.AbsoluteUri;                                             //請求的路徑
            string Passport = System.Configuration.ConfigurationManager.AppSettings["Server"];      //Server端的路徑
            object isLogin = filterContext.HttpContext.Session["isLogin"];    //局部會話
            
            if (isLogin != null)        //有局部會話的時候直接放行
            {
                return;     
            }
            else
            {
                //從Server端跳轉到這里,對token進行校驗
                string token = request["tokenId"];
                if (!string.IsNullOrWhiteSpace(token))
                {
                    using (HttpClient http = new HttpClient())
                    {
                        HttpContent httpContent = new StringContent(token);
                        httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                        httpContent.Headers.ContentType.CharSet = "utf-8";
                        //驗證Tokend是否有效
                        HttpClient httpClient = new HttpClient();
                        
                        string url = Passport + "Login/Verify?token="+token;        //帶着token請求到Server端的Verify方法進行校驗
                        HttpResponseMessage res = httpClient.GetAsync(url).Result;
                        string result = res.Content.ReadAsStringAsync().Result;
                        if (res.StatusCode.ToString()=="OK")
                        {
                            if (result == "True")
                            {
                                filterContext.HttpContext.Session["isLogin"] = true;
                                return;                       //通過校驗就放行
                            }
                        }
                    }
                }
            }

            filterContext.Result = new RedirectResult(string.Format("{0}/Login/CheckLogin?redirectUrl={1}", Passport,ReturnURL));   //第一次登錄時沒有局部會話,要跳到Server端進行認證
            return;

        }
    }
}

    2.Server端Control中的代碼

using Server.Filter;
using Server.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;

namespace Server.Controllers
{
    public class LoginController : Controller
    {
        // GET: Login
      
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult CheckLogin(string redirectUrl) {
            object token = Session["token"];
            if (token==null)
            {
                string url = "http://localhost:52666/Login/Login?redirectUrl="+redirectUrl;   //跳轉到登錄界面,並攜着跳轉過來的路徑,以方便后續操作
                return Redirect(url);
            }
            else
            {
                return Redirect(redirectUrl+ "?tokenId=" + token.ToString());           //當第二個Client登錄時,此時已經有了一個全局會話。會直接跳轉過去並帶上token
            }
        }
        public ActionResult Login(string redirectUrl) 
        {
            ViewBag.Url = redirectUrl;          
            return View();            //返回登錄界面的視圖
        }


        [HttpPost]
        public ActionResult Login(string userName, string password,string RetrunURL) {

            if (userName=="admin"&&password=="admin")           //登錄成功
            {
                Session["token"] = Guid.NewGuid().ToString();       //生成一個全局會話
                MockDatabase.T_Token.Add(Session["token"].ToString());   //將產生的token寫到數據庫
                return Redirect(RetrunURL + "?tokenId=" + Session["token"].ToString());  //重定向到Client端並攜帶着這個token
            }
            else
            {
                string url = "http://localhost:52666/Login/Login?redirectUrl=" + RetrunURL;     //否則繼續返回到登錄界面
                return Redirect(url);
            }   
        }
     public string Verify(string token) {
            return MockDatabase.T_Token.Contains(token).ToString();   //判斷數據庫中存入的token,有沒有包含傳遞過來的token
        }

    }
}

 

 

    


免責聲明!

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



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