許可權管理學習 一、ASP.NET Forms身份認證

来源:http://www.cnblogs.com/zhaopei/archive/2017/08/01/authorize-1.html
-Advertisement-
Play Games

說明:本文示例使用的VS2017和MVC5。 系統無論大小、牛逼或屌絲,一般都離不開註冊、登錄。那麼接下來我們就來分析下用戶身份認證。 簡單實現登錄、註銷 以前在學習.net的時候不知道什麼Forms身份認證,直接用session實現登錄,效果也蠻好嘛。而且用戶信息存在服務端,安全。 前端代碼: 後 ...


說明:本文示例使用的VS2017和MVC5。
系統無論大小、牛逼或屌絲,一般都離不開註冊、登錄。那麼接下來我們就來分析下用戶身份認證。

簡單實現登錄、註銷

以前在學習.net的時候不知道什麼Forms身份認證,直接用session實現登錄,效果也蠻好嘛。而且用戶信息存在服務端,安全。
前端代碼:

@if (string.IsNullOrWhiteSpace(ViewBag.UserName))
{
    <form action="/home/login1">
        <input type="text" name="userName" />
        <input type="submit" value="登錄" />
    </form>
}
else
{
    <form action="/home/logout1">
        <div>當前用戶已登錄,登錄名:@ViewBag.UserName</div>
        <input type="submit" value="退出" />
    </form>
}

後臺代碼:

public ActionResult Index()
{
    ViewBag.UserName = Session["userName"]?.ToString();           
    return View();
}       

public void Login1(string userName)
{
    if (!string.IsNullOrWhiteSpace(userName))  //為了方便演示,就不做真的驗證了     
        Session["userName"] = userName;
    else
        Session["userName"] = null;
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原來頁面
}

public void Logout1()
{
    Session["userName"] = null;
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原來頁面
}

是不是,簡單明瞭。想要自己擴展或是定製什麼功能都非常好用。不過我們需要維護session。比如系統重新發佈,或者iis被自動重啟。就會出現session丟失的情況。也就是用戶會莫名其妙提升需要重新登錄。體驗非常不好。(這裡先不討論session服務和資料庫的情況)。既然微軟有一套成熟的許可權管理我們為什麼不用呢?

Forms認證登錄、註銷

首先在web.config里開啟Forms身份認證:

<system.web>
  <authentication mode="Forms"></authentication>

後臺代碼:

public void Login2(string userName)
{
    if (!string.IsNullOrWhiteSpace(userName))  //為了方便演示,就不做真的驗證了
        FormsAuthentication.SetAuthCookie(userName, true); //登錄
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原來頁面
}

public void Logout2()
{
    FormsAuthentication.SignOut();//登出
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原來頁面
}

前臺代碼:

@if (!Request.IsAuthenticated)
{
    <form action="/home/login2">
        <input type="text" name="userName" />
        <input type="submit" value="登錄" />
    </form>
}
else
{
    <form action="/home/logout2">
        <div>當前用戶已登錄,登錄名:@Context.User.Identity.Name</div>
        <input type="submit" value="退出" />
    </form>
}

如此幾句代碼就實現了我們的登錄和註銷。和我們自己用session管理登錄不同。Forms身份認證是直接把信息存cookie到瀏覽器的。通過SetAuthCookie這個方法名也可以看出來。不過Cookie信息經過了加密。
這裡有必要說明session和cookie的關係。當我們利用session來維持用戶狀態的時候,其實也用到了cookie。

然而Forms身份認證僅僅只是把信息存了cookie,而沒有在服務端維護一個對應的session。
不信你可以測試。可以用兩種方式都登錄,然後清除session就可以測出來了。(怎麼清session?重啟iis,或者修改下後臺代碼在重新編譯訪問)
【說明】用戶認證為什麼要存cookie?因為HTTP是一個無狀態的協議。對於伺服器來說,每次請求都是一樣的。所以,只能通過每次請求帶的cookie來識別用戶了。(暫時不考慮其他方式)

自定義的身份認證標識

上面使用的登錄很簡單,但實際情況往往很複雜。明顯正常業務需要存的用戶信息會要更多。那麼我們是否可以擴展身份標識呢?答案是肯定的。
後臺代碼:

public void Login3(string userName)
{
    if (!string.IsNullOrWhiteSpace(userName))  //為了方便演示,就不做真的驗證了     
    {
        UserInfo user = new UserInfo()
        {
            Name = userName,
            LoginTime = DateTime.Now
        };
        //1、序列化要保存的用戶信息
        var data = JsonConvert.SerializeObject(user);

        //2、創建一個FormsAuthenticationTicket,它包含登錄名以及額外的用戶數據。
        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(2, userName, DateTime.Now, DateTime.Now.AddDays(1), true, data);

        //3、加密保存
        string cookieValue = FormsAuthentication.Encrypt(ticket);

        // 4. 根據加密結果創建登錄Cookie
        HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue);
        cookie.HttpOnly = true;
        cookie.Secure = FormsAuthentication.RequireSSL;
        cookie.Domain = FormsAuthentication.CookieDomain;
        cookie.Path = FormsAuthentication.FormsCookiePath;

        // 5. 寫登錄Cookie
        Response.Cookies.Remove(cookie.Name);
        Response.Cookies.Add(cookie);
    }
    Response.Redirect(Request.UrlReferrer.LocalPath);//重定向到原來頁面
}

然後在Global.asax的Application_AuthenticateRequest方法:

protected void Application_AuthenticateRequest()
{
    GetUserInfo();
}

//通過coolie解密 讀取用戶信息到 HttpContext.Current.User
public void GetUserInfo()
{
    // 1. 讀登錄Cookie
    HttpCookie cookie = Request.Cookies[FormsAuthentication.FormsCookieName];

    try
    {
        UserInfo userData = null;
        // 2. 解密Cookie值,獲取FormsAuthenticationTicket對象
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);

        if (ticket != null && string.IsNullOrEmpty(ticket.UserData) == false)
            // 3. 還原用戶數據
            userData = JsonConvert.DeserializeObject<UserInfo>(ticket.UserData);

        if (ticket != null && userData != null)
            // 4. 構造我們的MyFormsPrincipal實例,重新給context.User賦值。
            HttpContext.Current.User = new MyFormsPrincipal<UserInfo>(ticket, userData);
    }
    catch { /* 有異常也不要拋出,防止攻擊者試探。 */ }
}

前端代碼:

@{
    MyFormsPrincipal<UserInfo> user = Context.User as MyFormsPrincipal<UserInfo>;
    if (user == null)
    {
        <form action="/home/login3">
            <input type="text" name="userName" />
            <input type="submit" value="登錄" />
        </form>
    }
    else
    {

        <form action="/home/logout2">
            <div>當前用戶已登錄,登錄名:@Context.User.Identity.Name</div>
            <div>當前用戶已登錄,登錄時間:@user.UserData.LoginTime</div>
            <input type="submit" value="退出" />
        </form>
    }
}

其實整個過程和FormsAuthentication.SetAuthCookie(userName, true); //登錄是等效的。只是我們通過擴展,存了我們想要存儲的數據。
過程也比較簡單:

  • 構造要存儲的數據
  • 序列化
  • 把序列化信息放入FormsAuthenticationTicket對象
  • 通過FormsAuthentication.Encrypt加密對象
  • 發送cookie到瀏覽器

這裡稍微複雜點的地方就是解密然後給User賦值HttpContext.Current.User = new MyFormsPrincipal<UserInfo>(ticket, userData);
MyFormsPrincipal需要實現介面MyFormsPrincipal

public class MyFormsPrincipal<TUserData> : IPrincipal where TUserData : class, new()
{
    private IIdentity _identity;
    private TUserData _userData;

    public MyFormsPrincipal(FormsAuthenticationTicket ticket, TUserData userData)
    {
        if (ticket == null)
            throw new ArgumentNullException("ticket");
        if (userData == null)
            throw new ArgumentNullException("userData");

        _identity = new FormsIdentity(ticket);
        _userData = userData;
    }

    public TUserData UserData
    {
        get { return _userData; }
    }

    public IIdentity Identity
    {
        get { return _identity; }
    }

    public bool IsInRole(string role)//這裡暫時不實現
    {
        return false;
    }
}

倒也沒有什麼特別,就是實例化的時候傳入票據和自定義數據就好了。

授權

有了登錄一般都離不開授權。微軟的東西好就好在,一般都是成套成套的。

[Authorize]
public ActionResult LoginOk()
{
    return View();
}

直接給Action添加一個Authorize特性就好了,這人就會自動檢查是否登錄。如果沒有登錄自動跳轉到登錄頁面。登錄頁面的設置還是在web.config裡面

<system.web>
  <authentication mode="Forms" >
    <forms loginUrl="/home/index"></forms>

這種簡單的授權驗證明顯是不夠的。很多時候某些頁面只有某些人才能訪問。比如VIP。那麼我們又要擴展了。

//繼承 AuthorizeAttribute
public class MyAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.User.Identity.Name != "農碼一生")
        {
            filterContext.HttpContext.Response.Write("您不是vip用戶,不能訪問機密數據");
            filterContext.HttpContext.Response.End();
            return;
        }
        base.OnAuthorization(filterContext);
    }
}
[MyAuthorize]
public ActionResult LoginVIP()
{
    return View();
}

是的,就是這麼簡單。說了這麼多,來張效果圖吧:

 

推薦閱讀:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 詳細的介紹了獨立硬碟冗餘陣列,並演示了Linux下的軟RAID的實現過程。 ...
  • 本教程根據慕課網<Django入門與實踐>編寫 基礎知識 什麼是django? Django是一個基於Python的高級Web開發框架, 特點:高效,快速,高度集成(不用自己弄底層),免費,開源 上網的流程 輸入URL→向目標url發送http請求→伺服器把頁面響應給瀏覽器(通過後臺編寫的代碼處理請 ...
  • 因為個人興趣愛好所致,最近在學習模擬電路方面的知識。在電容、電感串聯電路學習時費了很長時間,特此記錄一下學習心得,幫助自己總結也幫助同我一樣的初學者。在此特別感謝對我進行幫助的各位熱心網友:無敵小河馬、老洪電子、麻辣香鍋等朋友。 ...
  • 以前成功將Office Web Server 2013部署在了本地伺服器上,此次是將Office Web Server 2013部署在阿裡雲伺服器Windows Server 2008和2012上,中途遇到一些坑,寫此文章,幫助後來人,同時做個記錄。 因為Office Web Server 2013 ...
  • 1. 作用 動態查看進程的變化,常用來查找最耗CPU資源的進程。 2. 用法 top 【參數】 參數: -d:後接秒數,就是整個進程界面更新的秒數。預設是5秒; -b:以批次的方式執行top,通常會搭配數據流重定向來將批處理的結果輸出成為文件; -n:後面接整數數字,與-b搭配使用,表示需要進行幾次 ...
  • Linux 操作系統的網卡設備的傳統命名方式是 eth0、eth1、eth2等,而 CentOS7 提供了不同的命名規則,預設是基於固件、拓撲、位置信息來分配。這樣做的優點是命名全自動的、可預知的,缺點是比 eth0、wlan0 更難讀,比如 ens33 。 一、命名規則策略 規則1: 對於板載設備 ...
  • VIm配置及使用筆記 ============================= 安裝 apt get install vim y 配置說明 vim /etc/vim/vimrc 在配置文件後加入這些配置項 使用說明 編輯模式 一般模式 + 跳轉行 + 移動游標 + 撤銷與恢復 + 複製與粘貼 命令 ...
  • 一.環境:發行版本:ubuntu 14.04 64bit 二.獲取要指定的用戶及組id 使用id命令 (筆者獲取的uid和gid都為1000) 三.獲取識別的硬碟路徑 sudo fdisk -l (筆者獲取的路徑是/dev/sdb1) 四.掛載 sudo mount /dev/sdb1 /media ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...