簡易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 }

     

     


免責聲明!

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



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