简易Blazor 登录授权

主要参考学习张善友大神的提示,使用Blazored.LocalStorage包本地cookie方式 

本文将以Server Side的方式介绍,WASM方式仅需修改少数代码即可完成移植,不再赘述。下文介绍的是基于Token的内网用户名/密码认证,出于登录演示机制的考虑,并不保证代码在安全逻辑层面是可靠的。不要使用未加改造的本文代码,使用在生产网络中!

  1. Nuget安装Blazored.LocalStorage包。此包使用JS与客户端环境交互,保存/读取本地数据。

  2. 注册认证和授权服务。
     1 //ConfigureServices
     2 services.AddAuthentication();
     3 services.AddAuthorization();
     4 services.AddControllers();
     5 services.AddHttpClient();
     6 services.AddBlazoredLocalStorage();
     7 //Configure
     8 app.UseAuthentication();
     9 app.UseAuthorization();
    10 app.UseEndpoints(endpoints => {
    11 //...
    12     endpoints.MapControllers();
    13 //...
    14 })

      

  3. Data新建CurrentUser.cs
    using Blazored.LocalStorage;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Text.Json;
    using System.Security.Cryptography;
    using System.IO;
    using System.Text;
    
    namespace BlazorAuthoriation.Data
    {
        public class CurrentUser
        {
            static string LOCAL_STORAGE_KEY = "BlazorAuthorization";
            public static async Task<CurrentUser> Load(ILocalStorageService localStorage)
            {
                var s = await localStorage.GetItemAsync<string>(LOCAL_STORAGE_KEY);
                return FromStorageString(s);
            }
            public static CurrentUser FromStorageString(string s)
            {
                return StringToObj(s);
            }
            public static async Task Remove(Blazored.LocalStorage.ILocalStorageService localStorage)
            {
                await localStorage.RemoveItemAsync(LOCAL_STORAGE_KEY);
            }
            public static async Task Login(Blazored.LocalStorage.ILocalStorageService localStorage,string name, string pwd)
            {
                CurrentUser user = new CurrentUser() { UserName = name, PassWord = pwd,StatuTime = System.DateTime.Now };
    
                await localStorage.SetItemAsync<string>(LOCAL_STORAGE_KEY, user.ToString());
            }
            public async Task UpdateTime(Blazored.LocalStorage.ILocalStorageService localStorage)
            {
                this.StatuTime = System.DateTime.Now;
                await localStorage.SetItemAsync<string>(LOCAL_STORAGE_KEY, this.ToString());
            }
            public string UserName { set; get; }
            public string PassWord { set; get; }
            public DateTime StatuTime { set; get; }
            public override string ToString()
            {
                return DesEncrypt(JsonSerializer.Serialize(this),"this is special key");
            }
            public static CurrentUser StringToObj(string buff)
            {
                if (buff == null)
                    return null;
                CurrentUser user = JsonSerializer.Deserialize<CurrentUser>(DesDecrypt(buff, "this is special key"));
                //if (user?.StatuTime + new TimeSpan(0, 0, 10) < System.DateTime.Now)
                //    return null;
                return JsonSerializer.Deserialize<CurrentUser>(DesDecrypt(buff, "this is special key"));
            }
    
            /// <summary>
            /// 加密字符串
            /// 注意:密钥必须为8位
            /// </summary>
            /// <param name="strText">字符串</param>
            /// <param name="encryptKey">密钥</param>
            /// <param name="encryptKey">返回加密后的字符串</param>
            private static string DesEncrypt(string inputString, string encryptKey)
            {
                byte[] byKey = null;
                byte[] IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
                try
                {
                    byKey = System.Text.Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8));
                    DESCryptoServiceProvider des = new DESCryptoServiceProvider();
                    byte[] inputByteArray = Encoding.UTF8.GetBytes(inputString);
                    MemoryStream ms = new MemoryStream();
                    CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(byKey, IV), CryptoStreamMode.Write);
                    cs.Write(inputByteArray, 0, inputByteArray.Length);
                    cs.FlushFinalBlock();
                    return Convert.ToBase64String(ms.ToArray());
                }
                catch (System.Exception error)
                {
                    //return error.Message;
                    return null;
                }
            }
            /// <summary>
            /// 解密字符串
            /// </summary>
            /// <param name="this.inputString">加了密的字符串</param>
            /// <param name="decryptKey">密钥</param>
            /// <param name="decryptKey">返回解密后的字符串</param>
            private static string DesDecrypt(string inputString, string decryptKey)
            {
                byte[] byKey = null;
                byte[] IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
                try
                {
                    byte[] inputByteArray = new Byte[inputString.Length];
                    byKey = System.Text.Encoding.UTF8.GetBytes(decryptKey.Substring(0, 8));
                    DESCryptoServiceProvider des = new DESCryptoServiceProvider();
                    inputByteArray = Convert.FromBase64String(inputString);
                    MemoryStream ms = new MemoryStream();
                    CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(byKey, IV), CryptoStreamMode.Write);
                    cs.Write(inputByteArray, 0, inputByteArray.Length);
                    cs.FlushFinalBlock();
                    System.Text.Encoding encoding = new System.Text.UTF8Encoding();
                    return encoding.GetString(ms.ToArray());
                }
                catch (System.Exception error)
                {
                    //return error.Message;
                    return null;
                }
            }
        }
    }
    

      

  4. 增加Login.razor
     1 @page "/login"
     2 @using BlazorAuthoriation.Data
     3 @inject NavigationManager nav
     4 @*@inject Blazored.SessionStorage.ISessionStorageService storage*@
     5 @inject Blazored.LocalStorage.ILocalStorageService storage
     6 
     7 @if (null == currUser)
     8 {
     9     <h3>
    10         账号:<input @bind-value="user" />
    11         密码:<input @bind-value="pwd"/>
    12         <button @onclick="login">确定</button>
    13     </h3>
    14 }
    15 else
    16 {
    17     <h3>
    18         Welcome,<b> @currUser.UserName </b>!
    19         <button @onclick="logout">退出</button>
    20     </h3>
    21 }
    22 @code {
    23     [CascadingParameter]
    24     public CurrentUser currUser { get; set; }
    25 
    26     [Parameter]
    27     public EventCallback<string> LoginEvents { get; set; }
    28     [Parameter]
    29     public EventCallback LogoutEvents { get; set; }
    30 
    31     private string user;
    32     private string pwd;
    33     private async Task RaiseLoginEvent()
    34     {
    35         if (LoginEvents.HasDelegate)
    36         {
    37             await LoginEvents.InvokeAsync(null);
    38             StateHasChanged();
    39         }
    40     }
    41     private async Task RaiseLogoutEvent()
    42     {
    43         if (LogoutEvents.HasDelegate)
    44         {
    45             await LogoutEvents.InvokeAsync(null);
    46             StateHasChanged();
    47         }
    48     }
    49 
    50     private async Task logout()
    51     {
    52         await CurrentUser.Remove(storage);
    53         await RaiseLogoutEvent();
    54         //nav.NavigateTo("/");
    55     }
    56     private async void login()
    57     {
    58         await CurrentUser.Login(storage,user,pwd);
    59         await RaiseLoginEvent();
    60         //nav.NavigateTo("/");
    61     } 
    62 }

     

  5. 修改Mainlayout.razor
     1 @inherits LayoutComponentBase
     2 @inject NavigationManager nav
     3 @inject Blazored.LocalStorage.ILocalStorageService storage
     4 @using BlazorAuthoriation.Data
     5 @using BlazorAuthoriation.Pages
     6 
     7 <div class="sidebar">
     8     <NavMenu />
     9 </div>
    10 <CascadingValue Value="currUser">
    11     <div class="main">
    12         <div class="top-row px-4">
    13             @*<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>*@
    14             <Login LoginEvents="(e)=>LoginEvent(e)" LogoutEvents="(e)=>LoginEvent(e)" />
    15         </div>
    16         <div class="content px-4">
    17             @if (currUser == null)
    18             {
    19                 <h3>请登录以后再操作内容!!</h3>
    20             }
    21             else
    22                 @Body
    23         </div>
    24     </div>
    25 </CascadingValue>
    26 @code{
    27     /// <summary>
    28     /// 强制刷新标志
    29     /// </summary>
    30     private bool forceRender { get; set; } = false;
    31     private void LogoutEvent(object username)
    32     {
    33         //msg.Info($"See you later {(string)username}");
    34         //通过 LoginControl 组件回调 接收强制刷新消息
    35         //loginControl.ChildEvents.InvokeAsync("this is key");
    36         //currUser = null;
    37         forceRender = true;
    38     }
    39     private void LoginEvent(object username)
    40     {
    41         //msg.Info($"See you later {(string)username}");
    42         //通过 LoginControl 组件回调 接收强制刷新消息
    43         //loginControl.ChildEvents.InvokeAsync("this is key");
    44         //currUser = null;
    45         forceRender = true;
    46     }
    47 
    48     protected override async Task OnAfterRenderAsync(bool firstRender)
    49     {
    50         base.OnAfterRender(firstRender);
    51         //if (firstRender || forceRender)
    52         {
    53 
    54             currUser = await CurrentUser.Load(storage);
    55             if (currUser?.StatuTime < System.DateTime.Now - new TimeSpan(0, 0, 10))
    56                 currUser = null;
    57             if (null == currUser)
    58                 //nav.NavigateTo("/Login");
    59                 return;
    60             else
    61             {
    62                 if (firstRender || forceRender)
    63                 {
    64                     forceRender = false;
    65                     StateHasChanged();
    66                 }
    67                 //await currUser.UpdateTime(storage);
    68             }
    69 
    70         }
    71     }
    72     /// <summary>
    73     /// 有 CascadingValue 加持, 所有子组件可以通过 [CascadingParameter] 继承读取
    74     /// </summary>
    75     private CurrentUser currUser { get; set; }
    76 }

     

     

posted @ 2021-10-03 04:01  stweily  阅读(1823)  评论(0编辑  收藏  举报