Asp.Net Core 2.0 項目實戰(10) 基於cookie登錄授權認證並實現前台會員、后台管理員同時登錄


Asp.Net Core 2.0 項目實戰(1) NCMVC開源下載了

Asp.Net Core 2.0 項目實戰(2)NCMVC一個基於Net Core2.0搭建的角色權限管理開發框架

Asp.Net Core 2.0 項目實戰(3)NCMVC角色權限管理前端UI預覽及下載

Asp.Net Core 2.0 項目實戰(4)ADO.NET操作數據庫封裝、 EF Core操作及實例

Asp.Net Core 2.0 項目實戰(5)Memcached踩坑,基於EnyimMemcachedCore整理MemcachedHelper幫助類。

Asp.Net Core 2.0 項目實戰(6)Redis配置、封裝幫助類RedisHelper及使用實例

Asp.Net Core 2.0 項目實戰(7)MD5加密、AES&DES對稱加解密

Asp.Net Core 2.0 項目實戰(8)Core下緩存操作、序列化操作、JSON操作等Helper集合類

Asp.Net Core 2.0 項目實戰(9) 日志記錄,基於Nlog或Microsoft.Extensions.Logging的實現及調用實例

Asp.Net Core 2.0 項目實戰(10) 基於cookie登錄授權認證並實現前台會員、后台管理員同時登錄

Asp.Net Core 2.0 項目實戰(11) 基於OnActionExecuting全局過濾器,頁面操作權限過濾控制到按鈕級

1.登錄的實現

  登錄功能實現起來有哪些常用的方式,大家首先想到的肯定是cookie或session或cookie+session,當然還有其他模式,今天主要探討一下在Asp.net core 2.0下實現以cookie登錄授權,與net freamwork框架下常用的開發方式有所不同的是以前開發不管是webform還是mvc模式,大多數開發者會封裝成第三方操作類,方便項目全局調用;在net core 2.0 下的登錄方式發生了點變化,大概流程是先通過依賴注入相關配置,再通過Action登錄授權,然后Authentication相關屬性認證,具體怎么實現讓我們一起一步步操作一下。

2.Cookie開發回顧

  進行net core 2.0 cookie編程前,首先回顧一下原來做asp.net項目開發常用的操作cookie封裝的一些封裝。封裝好的cookiehelper類庫,可以很方便的在項目中調用,如寫入cookie的時候直接調用CookieHelper.WriteCookie(名稱,值)這樣key/value形式寫入cookie,讀取cookie的時候直接使用CookieHelper.GetCookie(名稱)就可以獲取到cookie值。

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
using System.Configuration;
using System.Web;
using System.Security.Cryptography;

namespace ZZ.Common
{
    public class CookieHelper
    {
/// <summary>
        /// 寫cookie值
        /// </summary>
        /// <param name="strName">名稱</param>
        /// <param name="strValue"></param>
        public static void WriteCookie(string strName, string strValue)
        {
            HttpCookie cookie = HttpContext.Current.Request.Cookies[strName];
            if (cookie == null)
            {
                cookie = new HttpCookie(strName);
            }
            cookie.Value = UrlEncode(strValue);
            HttpContext.Current.Response.AppendCookie(cookie);
        }

        /// <summary>
        /// 寫cookie值
        /// </summary>
        /// <param name="strName">名稱</param>
        /// <param name="strValue"></param>
        public static void WriteCookie(string strName, string key, string strValue)
        {
            HttpCookie cookie = HttpContext.Current.Request.Cookies[strName];
            if (cookie == null)
            {
                cookie = new HttpCookie(strName);
            }
            cookie[key] = UrlEncode(strValue);
            HttpContext.Current.Response.AppendCookie(cookie);
        }

        /// <summary>
        /// 寫cookie值
        /// </summary>
        /// <param name="strName">名稱</param>
        /// <param name="strValue"></param>
        public static void WriteCookie(string strName, string key, string strValue, int expires)
        {
            HttpCookie cookie = HttpContext.Current.Request.Cookies[strName];
            if (cookie == null)
            {
                cookie = new HttpCookie(strName);
            }
            cookie[key] = UrlEncode(strValue);
            cookie.Expires = DateTime.Now.AddMinutes(expires);
            HttpContext.Current.Response.AppendCookie(cookie);
        }

        /// <summary>
        /// 寫cookie值
        /// </summary>
        /// <param name="strName">名稱</param>
        /// <param name="strValue"></param>
        /// <param name="strValue">過期時間(分鍾)</param>
        public static void WriteCookie(string strName, string strValue, int expires)
        {
            HttpCookie cookie = HttpContext.Current.Request.Cookies[strName];
            if (cookie == null)
            {
                cookie = new HttpCookie(strName);
            }
            cookie.Value = UrlEncode(strValue);
            cookie.Expires = DateTime.Now.AddMinutes(expires);
            HttpContext.Current.Response.AppendCookie(cookie);
        }

        /// <summary>
        /// 讀cookie值
        /// </summary>
        /// <param name="strName">名稱</param>
        /// <returns>cookie值</returns>
        public static string GetCookie(string strName)
        {
            if (HttpContext.Current.Request.Cookies != null && HttpContext.Current.Request.Cookies[strName] != null)
                return UrlDecode(HttpContext.Current.Request.Cookies[strName].Value.ToString());
            return "";
        }

        /// <summary>
        /// 讀cookie值
        /// </summary>
        /// <param name="strName">名稱</param>
        /// <returns>cookie值</returns>
        public static string GetCookie(string strName, string key)
        {
            if (HttpContext.Current.Request.Cookies != null && HttpContext.Current.Request.Cookies[strName] != null && HttpContext.Current.Request.Cookies[strName][key] != null)
                return UrlDecode(HttpContext.Current.Request.Cookies[strName][key].ToString());

            return "";
        }
    }
}
CookieHelper

3.NET Core2.0 下Cookie的使用

  3.1添加Nuget相關依賴

  

  

  我這里使用 Microsoft.AspNetCore.All大而全的,逐項引用這里不做過多探討,適合自己的就是最好的。

  參照搜狐網上看到的一段話:

  Microsoft.AspNetCore.All包,它是一個元數據包,包含了大量的東西,其中包括:Authorization, Authentication, Identity, CORS, Localization, Logging, Razor, Kestrel 等,除了這些它還附加了 EntityFramework, SqlServer, Sqlite 等包。有些同學可能會覺得這樣會引用了很多項目中使用不到的程序集,導致發布后的程序變得很龐大,不過我要告訴你不必擔心,發布后的程序集不但不會變得很大,反而會小很多,因為 Microsoft 把所有的這些依賴全部都集成到了sdk中,也就是說當你安裝sdk的之后,MVC相關的包就已經安裝到了你的系統上。

3.2配置

  3.2.1首先在Startup.cs中ConfigureServices添加Cookie中間件,使用自定義Scheme

 
        
    //Cookie(1)添加 Cookie 服務
    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            //后台管理員cookie服務
            .AddCookie(AdminAuthorizeAttribute.AdminAuthenticationScheme, options =>
            {
                options.LoginPath = "/admin/Login/Index";//登錄路徑
                options.LogoutPath = "/admin/Login/LogOff";//退出路徑
                options.AccessDeniedPath = new PathString("/Error/Forbidden");//拒絕訪問頁面
                options.Cookie.Path = "/";
            })
            //前台用戶cookie服務
            .AddCookie(UserAuthorizeAttribute.UserAuthenticationScheme, options =>
            {
                options.LoginPath = "/Login/Index";
                options.LogoutPath = "/Login/LogOff";
                options.AccessDeniedPath = new PathString("/Error/Forbidden");//拒絕訪問頁面
                options.Cookie.Path = "/";
            });
在ConfigureServices方法中添加授權支持,並添加使用Cookie的方式,配置登錄頁面、登出頁面和沒有權限時的跳轉頁面。AdminAuthorizeAttribute、UserAuthorizeAttribute為自定義AuthorizeAttribute類,兩個登錄方案,同時類中實現虛方法OnAuthorization過濾,如果系統中只有一個登錄授權全部使用默認即可。
      //Cookie(1)添加 Cookie 服務
      services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
//后台管理員cookie服務 .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.LoginPath = "/admin/Login/Index";//登錄路徑 options.LogoutPath = "/admin/Login/LogOff";//退出路徑 options.AccessDeniedPath = new PathString("/Error/Forbidden");//拒絕訪問頁面 options.Cookie.Path = "/"; });

 

  3.2.2在Configure使用Cookie中間件

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

{

            //Cookie(2)使用Cookie的中間件

            app.UseAuthentication();

}

  3.3[自定義AuthorizeAttribute]特性

    添加一個登錄方案(Scheme)

  CookieAuthenticationDefaults.AuthenticationScheme,這是系統已經定義好的一個默認的登錄方案,添加一個新的來實現一個不同的身份登錄。代碼如下:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NC.MVC
{
    /// <summary>
    /// 跳過檢查屬性
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class SkipUserAuthorizeAttribute : Attribute, IFilterMetadata
    {
    }
    /// <summary>
    /// 前台登錄驗證
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class UserAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
    {
        public const string UserAuthenticationScheme = "UserAuthenticationScheme";//自定義一個默認的登錄方案
        public UserAuthorizeAttribute()
        {
            this.AuthenticationSchemes = UserAuthenticationScheme;
        }
        public virtual void OnAuthorization(AuthorizationFilterContext filterContext)
        {
            //獲取對應Scheme方案的登錄用戶呢?使用HttpContext.AuthenticateAsync
            var authenticate = filterContext.HttpContext.AuthenticateAsync(UserAuthorizeAttribute.UserAuthenticationScheme);
            //authenticate.Wait();
            if (authenticate.Result.Succeeded || this.SkipUserAuthorize(filterContext.ActionDescriptor))
            {
                return;
            }
            //如果是默認Scheme可以使用 
            //if (filterContext.HttpContext.User.Identity.IsAuthenticated || this.SkipUserAuthorize(filterContext.ActionDescriptor))
            //{
            //    return;
            //}

              HttpRequest httpRequest = filterContext.HttpContext.Request;
            string url = filterContext.HttpContext.Content("~/login");
                url = string.Concat(url, "?returnUrl=", httpRequest.Path);

                RedirectResult redirectResult = new RedirectResult(url);
                filterContext.Result = redirectResult;
                return;
        }

        protected virtual bool SkipUserAuthorize(ActionDescriptor actionDescriptor)
        {
            bool skipAuthorize = actionDescriptor.FilterDescriptors.Where(a => a.Filter is SkipUserAuthorizeAttribute).Any();
            if (skipAuthorize)
            {
                return true;
            }

            return false;
        }
    }
}

 

  這里有一個點需要注意登錄多個用戶,filterContext.HttpContext.User.Identity這里會默認AddAuthentication(Scheme)中的Scheme, 網上看到“如果你的Controller或者Action上有使用AuthorizeAttribute,那這個Attribute使用的登錄方案是哪個,則這個HttpContext.User對應的就是那個方案的登錄用戶。如果沒有使用,則AddAuthentication()方法默認指它的方案(Scheme)所登錄的用戶,就是這個HttpContext.User”這里跟我實際測試的有所不同,大家可以多測試一下。

  所以獲取對應方案的登錄用戶,這里用的是

    var authenticate = filterContext.HttpContext.AuthenticateAsync(UserAuthorizeAttribute.UserAuthenticationScheme);

    if (authenticate.Result.Succeeded){return;}

  如果您有更好的方案請留言告知!

3.4登錄授權實現

  配置文件中處理完成之后,接下來就是在登錄Action中實現登錄。添加一個Controller,如LoginController,再添加一個Action,如 Login,所配置的路由,要與上面的配置對應,不然跳轉登錄時會跳錯頁面。

  下面展示前台會員登錄類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;

namespace NC.MVC.Controllers
{
    public class LoginController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
        /// <summary>
        /// 用戶登錄
        /// </summary>
        /// <param name="userName">用戶ID</param>
        /// <param name="passWord">用戶密碼</param>
        /// <param name="rememberMe">是否記住密碼</param>
        /// <returns></returns>
        [HttpPost]
        public IActionResult Login(string userName, string passWord, string rememberMe, string txtCode)
        {
            var user = new ClaimsPrincipal(
             new ClaimsIdentity(new[]
             {
                        new Claim("UserName","UserNameValue"),
                        new Claim("UserPwd","UserPwdValue"),
             }, UserAuthorizeAttribute.UserAuthenticationScheme)
            );
            HttpContext.SignInAsync(UserAuthorizeAttribute.UserAuthenticationScheme, user, new AuthenticationProperties
            {
                ExpiresUtc = DateTime.UtcNow.AddMinutes(60),// 有效時間
                //ExpiresUtc = DateTimeOffset.Now.Add(TimeSpan.FromDays(7)), // 有效時間
                IsPersistent = true,
                AllowRefresh = false
            });
            return new RedirectResult("~/Home/Index");//登錄成功
        }
    }
}

 


  這里要注意的是HttpContext.SignInAsync(AuthenticationType,…) 所設置的Scheme一定要與前面的配置一樣,這樣對應的登錄授權才會生效。

3.5認證驗證

  登錄完成后,進入需要授權才能進入的管理頁面,在做限制的Controller上加上[自定義AuthorizeAttribute]特性來做限制。這里我們自定義了兩個AuthorizeAttribute;UserAuthorizeAttribute和AdminAuthorizeAttribute,結合3.3的實現,我們使用UserAuthorizeAttribute在Contoller或Action上加特性。這里我們先在HomeController上加上[UserAuthorize]

using Microsoft.AspNetCore.Mvc;
using System.Data;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;

namespace NC.MVC.Controllers
{
    [UserAuthorize]
    public class HomeController : Controller
  {
        public IActionResult Index()
        {
           return View();
     }
  }
}

 

 
        

3.6退出登錄

  如果是多用戶登錄的系統,退出的時候需要指定方案退出。
public async Task Logout(string returnurl)
{
    await HttpContext.SignOutAsync(UserAuthorizeAttribute.UserAuthenticationScheme);
    return Redirect(returnurl ?? "~/");
}

 

4.總結

  以上就是我對net core 2.0 cookie授權登錄實際編程測試遇到的坑點以及編程過程中用到的部分代碼,后端管理員登錄相關代碼以及AdminAuthorizeAttribute和前端會員UserAuthorizeAttribute代碼基本一致,這里限於時間及篇幅不做過多處理,自己動手是學習技術最快的方式,沒有之一。

  在進行netcore2.0編程或者學習的時候,一定要拋卻固有的一些編程形式、思維。新的事物總會對舊有規則,舊有思維產生一定的沖擊,一定要學會適應,提醒大家同時警醒自己。Net core 開源跨平台總的來說是屬於是良性的變化,變得更方便,更容易擴展,NET陣營需要我們大家共同去努力。

  感謝ace chloe core開源、zkea cms core開源,在學習Asp.Net Core 2.0開發的時候網上資料相對較少,找了很多資料,有很多大牛的文章代碼對我幫助很大,寫博的時候很多創意、文章忘記出處,以后會留意。整理此篇備忘,希望對你有些許幫助。



 


免責聲明!

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



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