首页 > 其他分享 >Blazor WebAssembly使用 AuthenticationStateProvider 自定义身份认证

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

时间:2024-05-12 11:57:06浏览次数:8  
标签:WebAssembly service 自定义 user using new Blazor userDetail public

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

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

安装依赖Microsoft.AspNetCore.Components.Authorization、Blazored.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.CurrentUser = au.GetUserClaimsPrincipal(userDetail);
        au.LoginIn("jwerqwert", userDetail);
        message.Success("登录成功!");
    }
}

  

简单的登录认证就完成了

用户详情代码

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

  

 

标签:WebAssembly,service,自定义,user,using,new,Blazor,userDetail,public
From: https://www.cnblogs.com/Zeng02/p/18187645

相关文章

  • dotnet 使用自定义特性
    namespaceTETTD.Common{///<summary>///导入excel特性标记字段映射的列///</summary>[AttributeUsage(AttributeTargets.Property|AttributeTargets.Field,AllowMultiple=false)]publicclassReadAttribute:Attribute{p......
  • 三分钟分享自定义表单系统开源的优势
    在数字化转型浪潮下,利用低代码技术平台、自定义表单系统开源的优势特点,可以助力企业实现高效办公,降低人工成本,从而进入流程化办公新时代。为了帮助大家了解相关信息,流辰信息与大家分享自定义表单系统开源的相关特点和优势,希望能帮助大家解决心中的疑问,更好地利用企业内部数据资源,......
  • MDT 的 Bootstrap.ini 中设置随机计算机名称,你可以使用预定义的 Task Sequence 变量和
    MDT的Bootstrap.ini中设置随机计算机名称,你可以使用预定义的TaskSequence变量和自定义脚本来实现。以下是一个示例:创建PowerShell脚本:powershellCopyCodefunctionGenerate-RandomComputerName{$prefix="PC"#可以是你希望的计算机名称前缀$rand......
  • Elasticsearch 自定义评分
    一.概述在前几章中,讲到了如何分词,以及分词的种类。分词后在进行全文检索时,返回结果如何确定用户真正想看到的, 那数据结果如何排序呢?比如在电商中:搜索一个商品关键词,默认是综合排序,商品如何顺序是经过一定的算法策略,也是为了提高用户的体验。Elasticsearch使用评分算......
  • NET6 自定义授权中间件
    AuthorizationWithCustMiddleware///<summary>///自定义授权中间件类:使用身份验证中间件存储的身份信息来进行权限验证///一定要先启用身份验证中间件(app.UseAuthentication()),它会验证请求中的身份信息,并将身份信息存储在HttpContext.User属性中///如果没有启用身份......
  • 自定义鼠标设置-中键设置为后退
    这个软件能够修改鼠标的很多设置公司配置的鼠标没有侧边按键导致没有后退按键可以使用,使阅读代码不够流畅使用这款软件就能将中间设置为后退,使阅读代码更加流畅下载网站为https://www.highrez.co.uk/downloads/XMouseButtonControl.htm汉化语言包链接为https://www.highr......
  • Laravel 实现自定义资源路由
    Laravel如何实现自定义资源路由最近在开发过程中,发现总有一些路由需要重复定义,比如切换状态,导出,回收站啊之类的。如果使用Laravel自带的资源路由方法,还不足以满足重复劳动得过程。所以是否有方法可以自定义项目得资源路由呢?在Laravel中,资源路由一般有两种服务端渲染Route:......
  • 自定义表单工作流的优势介绍
    当前,应用低代码技术平台可以助力企业提高效率,降低开发成本,实现个性化场景定制,因而越来越得到了客户的信赖与喜爱。很多客户朋友询问自定义表单工作流的优势和特点,为了帮助大家解决这个疑问,今天,就跟大家一起分享低代码技术平台以及自定义表单工作流的相关知识,相信从这些字里行间中......
  • Ant Design Blazor Table 组件的 自定义分页样式, 显示全部记录数,ShowTotal
    在AntDesignBlazor中,Table 组件的 ShowTotal 属性是一个泛型属性,它可以是两种类型之一:Func<PaginationTotalContext,string> 或 RenderFragment<PaginationTotalContext>。这个属性用于定义如何显示表格数据的总条数。OneOf<T1,T2> 是一个特殊的类型,它表示这个......
  • JDK源码阅读-------自学笔记(二十六)(java.util.Map 自定义讲解)
    一、简介Map就是用来存储“键(key)-值(value)”对的.通过键寻找value,所以键不能重复.数组的本质也是一种键值对,区别就是索引一般是数字,而Map的Key可以是任意对象(字符串,数字),相当于把数组的索引范围扩的更大,使用更方便.实际开发中较为常用.二、Map的常用方法实例(1......