• net core体系-web应用程序-4asp.net core2.0 项目实战(1)-12基于cookie登录授权认证并实现前台会员、后台管理员同时登录


    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 "";
            }
        }
    }
    复制代码

    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开发的时候网上资料相对较少,找了很多资料,有很多大牛的文章代码对我帮助很大,写博的时候很多创意、文章忘记出处,以后会留意。整理此篇备忘,希望对你有些许帮助。

    IT黑马
  • 相关阅读:
    【Java】:判断数据类型
    【Shell编程】:多命令处理
    正则表达式
    26、DIEN(DIN的延伸)
    25、深层用户兴趣网络DIN(阿里)
    强化学习(7)---动态规划
    强化学习(6)---马尔可夫过程
    ubuntu出现终端和浏览器输入法不能显示中文,但是ubuntu software可以显示(fcitx)
    ASP.NET实现企业微信接入应用实现身份认证
    DevOps
  • 原文地址:https://www.cnblogs.com/hmit/p/10769618.html
Copyright © 2020-2023  润新知