1. 程式人生 > >asp.net core後臺系統登入的快速構建

asp.net core後臺系統登入的快速構建

登入流程圖

示例預覽

構建步驟

當然,你也可以直接之前前往coding倉庫檢視原始碼,要是發現bug記得提醒我啊~ LoginDemo地址

1. 首先你得有一個專案

2. 然後你需要一個登入頁面

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>登入介面</title>
    <meta name="viewport" content
=
"width=device-width,initial-scale=1.0,user-scalable=no"> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <style type="text/css"> body {color:#fff;font-family:"微軟雅黑";font-size:14px;background:url('https://dn-coding-net-production-pp.qbox.me/96ec8cc7-0e5f-4217-b853-4a88c15579f3.png'
)no-repeat;}.wrap1{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;height:450px;}/*把整個螢幕真正撐開--而且能自己實現居中*/.main_content{background:url(https://dn-coding-net-production-pp.qbox.me/2ed70a05-04ad-4ccf-81d4-bc1fad2b6e41.png)repeat;margin-left:auto;margin-right:auto;text-align:left;float:none;border-radius
:
8px;}.form-group{position:relative;}.login_btn{display:block;background:#3872f6;color:#fff;font-size:15px;width:100%;line-height:50px;border-radius:3px;border:none;}.login_input{width:100%;border:1pxsolid#3872f6;border-radius:3px;line-height:40px;padding:2px5px2px30px;background:none;}.icon_font{position:absolute;top:12px;left:10px;font-size:18px;color:#3872f6;}.font16{font-size:16px;}.mg-t20{margin-top:20px;}@media(min-width:200px){.pd-xs-20{padding:20px;}}@media(min-width:768px){.pd-sm-50{padding:50px;}}#grad{background: -webkit-linear-gradient(#4990c1, #52a3d2, #6186a3);/* Safari 5.1 - 6.0 */background: -o-linear-gradient(#4990c1, #52a3d2, #6186a3);/* Opera 11.1 - 12.0 */background: -moz-linear-gradient(#4990c1, #52a3d2, #6186a3);/* Firefox 3.6 - 15 */background: linear-gradient(#4990c1, #52a3d2, #6186a3);/* 標準的語法 */}/*==jquery.validate css==*/.field-validation-error{color:#e14430!important;padding-top:5px;}.input-validation-error{border-color:#d38e99;}</style> </head> <body> <div class="container wrap1"> <h2 class="mg-b20 text-center">後臺管理系統</h2> <div class="col-sm-8 col-md-5 center-auto pd-sm-50 pd-xs-20 main_content"> <p class="text-center font16">使用者登入</p> <form asp-action="Login" method="post"> <div class="form-group mg-t20"> <i class="icon_font glyphicon glyphicon-user"></i> <input type="text" class="login_input" asp-for="UserName" placeholder="請輸入使用者名稱" autofocus/> <span asp-validation-for="UserName"></span> </div> <div class="form-group mg-t20"> <i class="icon_font glyphicon glyphicon-lock"></i> <input type="password" class="login_input" asp-for="UserPwd" placeholder="請輸入密碼"/> <span asp-validation-for="UserPwd"></span> </div> <div class="checkbox mg-b25 hide"> <label> <input type="checkbox">記住我的登入資訊 </label> </div> <button type="submit" class="login_btn">登 錄</button> </form> </div> </div> </body> </html>

控制器裡面至少擁有一個呈現登入頁的action,一個接收登入請求的action,一個退出的action
·登入· 判斷是否存在使用者,將使用者名稱或者使用者ID加密後記錄到cookie中,跳轉到管理頁
·退出· 將cookie移出掉,跳轉到登入頁
加密的方法可自行切換為其他的加密方法

    public class AccountController : Controller
    {
        private readonly IUserService _userService;
        public AccountController(IUserService userService)
        {
            _userService = userService;
        }

        public IActionResult Login()
        {
            return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Login(AccountModel model)
        {
            //驗證模型是否正確
            if (!ModelState.IsValid)
            {
                return View(model);
            }
            //呼叫服務驗證使用者名稱密碼
            if (!_userService.Login(model.UserName, model.UserPwd))
            {
                ModelState.AddModelError(nameof(model.UserPwd), "使用者名稱或密碼錯誤");
                return View();
            }
            //加密使用者名稱寫入cookie中,AdminAuthorizeAttribute特性標記取出cookie並解碼除使用者名稱
            var encryptValue = _userService.LoginEncrypt(model.UserName, ApplicationKeys.User_Cookie_Encryption_Key);
            HttpContext.Response.Cookies.Append(ApplicationKeys.User_Cookie_Key, encryptValue);
            return Redirect("/");
        }
        public IActionResult Logout()
        {
            HttpContext.Response.Cookies.Delete(ApplicationKeys.User_Cookie_Key);
            return Redirect(WebContext.LoginUrl);
        }
    }

本文只是簡單的驗證是否登入,關於更復雜的許可權驗證可參考文章:http://www.cnblogs.com/morang/p/7606843.html,以及示例專案
將此特性標記加到需要的地方即可在訪問時驗證使用者是否登入,未登入則跳轉到登入頁。

    public class AdminAuthorizeAttribute : Attribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext filterContext)
        {
            if (string.IsNullOrEmpty(WebContext.AdminName))
            {
                if (filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
                {
                    filterContext.Result = new JsonResult("未登入");
                }
                else
                {
                    filterContext.Result = new RedirectResult("Account/Login");
                }
                return;
            }
        }
    }

上面特性標記程式碼中的WebContext.AdminName是如何取到的呢?還需要結合如下程式碼

    //服務定位器
    public static class ServiceLocator
    {
        public static IServiceProvider Instance { get; set; }
        public static T GetService<T>() where T : class
        {
            return Instance.GetService<T>();
        }

    }
    //一些通用的資訊
    public static class WebContext
    {
        public static string AdminName
        {
            get
            {
                //獲取cookie
                var hasCookie = ServiceLocator.GetService<IHttpContextAccessor>()
                    .HttpContext
                    .Request.Cookies
                    .TryGetValue(ApplicationKeys.User_Cookie_Key, out string encryptValue);
                if (!hasCookie || string.IsNullOrEmpty(encryptValue))
                    return null;
                var adminName = ServiceLocator.GetService<IUserService>().LoginDecrypt(encryptValue, ApplicationKeys.User_Cookie_Encryption_Key);
                return adminName;
            }
        }
        public const string LoginUrl = "/account/login";
    }
    //全域性的一些Key值
    public class ApplicationKeys
    {
        public const string User_Cookie_Encryption_Key = "User_Cookie_Encryption_Key";

        public const string User_Cookie_Key = "User_Cookie_Key";
    }
    
    //Startup
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();//用於獲取請求上下文
        services.AddTransient<IUserService, UserService>();
        services.AddMvc();
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        //app.UseMvc()..
        //最末的時候賦值
        ServiceLocator.Instance = app.ApplicationServices;
    }

程式碼說明

  1. 首先定義了一個存放服務的靜態物件:ServiceLocator
  2. 在程式啟動後將IApplicationBuilder.ApplicationServices賦值給ServiceLocator.Instance,這樣就能夠在任何地方使用ServiceLocator.Instance獲取到注入的服務
    (為了更好的獲取例項添加了一個T GetService<T>()方法)
  3. 在WebContext中取獲取Cookie值:ServiceLocator.GetService<IHttpContextAccessor>().HttpContext.Request.Cookies
  4. 解密獲取的cookie得到使用者名稱:ServiceLocator.GetService<IUserService>().LoginDecrypt(encryptValue, ApplicationKeys.User_Cookie_Encryption_Key);
  5. 所以在後臺就能使用WebContext.AdminName獲取到當前登入使用者名稱,或者根據使用者名稱獲取登入資訊

總結

  • 自定義特性標記和過濾器之間差開一個IFilterMetadata,換言之:特性標記實現了IFilterMetadata就等於是個過濾器(個人理解)
  • asp.net core中模型繫結使用asp-for
  • asp.net core注入服務: 在 Startup.ConfigureServices方法中注入 services.AddTransient<IUserService, UserService>()
  • ASP.NET Core中提供了一個IHttpContextAccessor介面,HttpContextAccessor 預設實現了它簡化了訪問HttpContext
    它必須在程式啟動時在IServicesCollection中註冊,這樣在程式中就能獲取到HttpContextAccessor,並用來訪問HttpContext
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();