Blazor WebAssembly使用 AuthenticationStateProvider 自定义身份认证

本文章以客户端基础,实现类似后台系统,进入后台控制台页面需要经过登录身份验证才可访问情况

简单来时就是实现前后端分离,前端通过 token和用户信息进行身份认证,或者在 AuthenticationStateProvider  实现方法 GetAuthenticationStateAsync 中调用后台接口进行身份验证

安装依赖Microsoft.AspNetCore.Components.AuthorizationBlazored.LocalStorage

自定义AuthenticationStateProvider

using Blazored.LocalStorage;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Model.Entity;
using System.Net.Http.Headers;
using System.Security.Claims;

namespace font.Authorization
{
    public class HAuthenticationStateProvider : AuthenticationStateProvider 
    {   
        public HttpClient? _httpClient { set; get; }

        private AuthenticationService service;

        public HAuthenticationStateProvider(AuthenticationService service,  HttpClient _httpClient)
        {     
            this._httpClient = _httpClient;          

            this.service = service;
            service.UserChanged += (newUser) =>
            {
                NotifyAuthenticationStateChanged(
                    Task.FromResult(new AuthenticationState(newUser)));
            };
            service.LoginInState += MarkUserAsAuthenticated;
            service.LogoutState += MarkUserAsLoggedOut;
        }


        /// <summary>
        /// 身份认证提供
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public override Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var savedToken = service.GetAccessToken();

            if (string.IsNullOrWhiteSpace(savedToken))
            {
                return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
            }
            // 已认证过请求时带上token
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(service._bearer, savedToken);

            var user = service.GetUserDetail();
            if (user == null)
            {
                var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
                return Task.FromResult(new AuthenticationState(anonymousUser));

            }
            return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(
                new ClaimsIdentity( new[]{ new Claim(ClaimTypes.Name, user.UserName),  new Claim("userId", user.UserId.ToString())  }, service._authentication))));

        }

        /// <summary>
        /// 辅助登录后刷新认证状态
        /// </summary>
        /// <param name="token"></param>
        public void MarkUserAsAuthenticated(string token, UserDetail user)
        {
            var authenticatedUser = new AuthenticationState(
                new ClaimsPrincipal(new ClaimsIdentity(
                    new[] { new Claim(ClaimTypes.Name, user.UserName), new Claim("userId", user.Id.ToString()) }, service._authentication)));           
            var authState = Task.FromResult(authenticatedUser);
            NotifyAuthenticationStateChanged(authState);
        }

        /// <summary>
        /// 退出登录后刷新状态
        /// </summary>
        public void MarkUserAsLoggedOut()
        {
            var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
            var authState = Task.FromResult(new AuthenticationState(anonymousUser));
            NotifyAuthenticationStateChanged(authState);
        }
    }
}

 

自定义 AuthenticationService 管理登录和注销操作

using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components;
using Model.Entity;
using System.Security.Claims;

namespace font.Authorization
{
    public class AuthenticationService
    {
        public readonly string _token = "accessToken";
        public readonly string _bearer = "bearer";
        public readonly string _userDetail = "userDetail";
        public readonly string _authentication = "User Authentication";
        public event Action<ClaimsPrincipal>? UserChanged;
        public event Action<string, UserDetail>? LoginInState;
        public event Action? LogoutState;
        public readonly ISyncLocalStorageService localStorageService;  
        private ClaimsPrincipal? currentUser;
        public ClaimsPrincipal CurrentUser
        {
            get { return currentUser ?? new(); }
            set
            {
                currentUser = value;

                if (UserChanged is not null)
                {
                    UserChanged(currentUser);
                }
            }
        }

        public AuthenticationService(ISyncLocalStorageService _syncLocalStorageService)
        {
            this.localStorageService = _syncLocalStorageService;
        }

        public void LoginIn(string accessToken, UserDetail userDetail)
        {
            SetAccessToken(accessToken);
            SetUserDetail(userDetail);
            LoginInState(accessToken, userDetail);
        }
        public void Logout()
        {
            RemoveAccessToken();
            RemoveUserDetail();
            LogoutState();
        }

        public ClaimsPrincipal GetUserClaimsPrincipal(UserDetail user)
        {
            var identity = new ClaimsIdentity(
           new[]
           {
                new Claim(ClaimTypes.Name, user.UserName),
                new Claim("Id", user.Id.ToString()),
                new Claim("userId", user.UserId.ToString()),
           },
           _authentication);
            return new ClaimsPrincipal(identity);
        }
        
        public void RemoveAccessToken()
        {
            localStorageService.RemoveItem(_token);
        }
    
        public string? GetAccessToken()
        {
           return localStorageService.GetItemAsString(_token);
        }
     
        public void SetAccessToken(string accessToken)
        {
            localStorageService.SetItemAsString(_token, accessToken);
        }
        public UserDetail? GetUserDetail()
        {
            return localStorageService.GetItem<UserDetail>(_userDetail);
        }
        public void RemoveUserDetail()
        {
            localStorageService.RemoveItem(_userDetail);
        }
        public void SetUserDetail(UserDetail userDetail)
        {
            localStorageService.SetItem<UserDetail>(_userDetail, userDetail);
        }

    }
}

  

Program.cs

builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationService>();
builder.Services.AddScoped<AuthenticationStateProvider, HAuthenticationStateProvider>();

在布局组件或者具体组件内使用

<CascadingAuthenticationState>
    <AuthorizeView>
        <Authorized>
            <Layout>
                <Sider class="main-container" @bind-Collapsed=@collapsed NoTrigger OnCollapse="OnCollapse">
                     <div class="logo1" />
                     <NavMenu collapsed="@collapsed" />
                 </Sider>
                 <Layout class="site-layout">
                     <Header class="site-layout-background" Style="padding: 0;">
                         @if (collapsed)
                        {
                            <Icon Type="menu-unfold" Theme="outline" class="trigger" OnClick="toggle" Style=" font-size: 30px;margin-left: 10px;" />
                        }
                        else
                        {
                            <Icon Type="menu-fold" Theme="outline" class="trigger" OnClick="toggle" Style=" font-size: 30px;margin-left: 10px;" />
                        }
                    </Header>
                    <Content class="site-layout-background content-container" Style="margin: 24px 16px;padding: 24px;min-height: 280px;">
                        @Body
                    </Content>
                </Layout>
            </Layout>
        </Authorized>
        <NotAuthorized>
            <Login />
        </NotAuthorized>
    </AuthorizeView>
</CascadingAuthenticationState>

 

自定义登录组件和授权后的页面,注入  进行登录和注销测试

 

 再刷新页面测试 和注销测试

 

已授权首页代码

@page "/"
@inject IMessageService _message
@inject AuthenticationService serivce

<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.

<Button Type="primary">Primary</Button>
<Button Type="primary">Primary</Button>
<br />
<Button Type="primary" OnClick="@OnClick">
    Display normal message
</Button>

@code{
    private void OnClick()
    {
        Console.WriteLine("onclick");
        _message.Info("退出登录成功!");
        serivce.Logout();
    }
}

  

登录组件代码

@layout EmptyLayout
@page "/login"
@inject AuthenticationService au
@inject IMessageService message

<h3>Login</h3>
<Button Type="@ButtonType.Primary" OnClick="@login"  Size="30" Shape="@ButtonShape.Round" >登录</Button>

@code {
    void login()
    {
        UserDetail userDetail = new UserDetail() { Id = 1, UserId = 12, UserName = "hygge" };     
        au.LoginIn("jwerqwert", userDetail);
        message.Success("登录成功!");
    }
}

  

简单的登录认证就完成了

用户详情代码

  public class UserDetail
  {
      public int Id { get; set; }
      public int UserId { get; set; }
      public string UserName { get; set; }
  
  }

  

 

posted @ 2024-05-12 11:58  漫步花海下的oldman  阅读(376)  评论(0编辑  收藏  举报