使用Jwt令牌身份验证在Blazor Server App中自定义AuthenticationStateProvider [英] Customizing the AuthenticationStateProvider in Blazor Server App with Jwt Token Authentication

查看:273
本文介绍了使用Jwt令牌身份验证在Blazor Server App中自定义AuthenticationStateProvider的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到许多开发人员都将AuthenticationStateProvider子类化为 Blazor Server App和Blazor WebAssembly App错误,并且更错误地错误地使用了Blazor Server App和Blazor WebAssembly App. 原因.

I've noticed that many developers subclass the AuthenticationStateProvider both in Blazor Server App and Blazor WebAssembly App wrongly, and more imprtantly for the wrong reasons.

如何正确执行操作以及何时执行?

How to do it correctly and when ?

推荐答案

首先,您不会仅出于以下目的而将AuthenticationStateProvider子类化: 向ClaimPrincipal对象添加声明.一般而言,索赔是在 用户已通过身份验证,如果您需要检查这些声明并进行转换,它将 应该在其他地方完成,而不是在AuthenticationStateProvider对象中完成.顺便说一句,在 Asp.Net Core有两种方法可以实现此目的,但这有其自身的问题.

First off, you do not subclass the AuthenticationStateProvider for the sole purpose of adding claims to the ClaimPrincipal object. Generally speaking, claims are added after a user has been authenticated, and if you need to inspect those claims and tranform them, it should be done somewhere else, not in the AuthenticationStateProvider object. Incidentally, in Asp.Net Core there are two ways how you can do that, but this merits a question of its own.

我猜想

I guess that this code sample led many to believe that this is the place to add claims to the ClaimsPrincipal object.

在当前上下文中,实现Jwt令牌认证时,应添加声明 在服务器上创建Jwt令牌并在需要时在客户端上将其提取到Jwt令牌, 例如,您需要当前用户的名称.我注意到开发人员可以保存 用户名称在本地存储中,并在需要时进行检索.这是错误的. 您应该从Jwt令牌中提取用户的名称.

In the current context, implementing Jwt Token Authentication, claims should be added to the Jwt Token when it is created on the server, and extracted on the client when required, as for instance, you need the name of the current user. I've noticed that developers save the name of the user in the local storage, and retrieved it when needed. This is wrong. You should extract the name of the user from the Jwt Token.

下面的代码示例描述如何创建自定义AuthenticationStateProvider对象 其目的是从本地存储中检索新添加的Jwt令牌字符串, 解析其内容,并创建提供给有兴趣的ClaimsPrincipal对象 各方(AuthenticationStateProvider.AuthenticationStateChanged事件的订阅者) ,例如CascadingAuthenticationState对象.

The following code sample describes how to create a custom AuthenticationStateProvider object whose objective is to retrieve from the local storage a Jwt Token string that has newly added, parse its content, and create a ClaimsPrincipal object that is served to interested parties (subscribers to the AuthenticationStateProvider.AuthenticationStateChanged event) , such as the CascadingAuthenticationState object.

以下代码示例演示了如何实现自定义 正确的身份验证状态提供者,并且有充分的理由.

The following code sample demonstrates how you can implement a custom authenticationstateprovider properly, and for good reason.

public class TokenServerAuthenticationStateProvider : ServerAuthenticationStateProvider
    {
        private readonly IJSRuntime _jsRuntime;
       
        public TokenServerAuthenticationStateProvider(IJSRuntime jsRuntime)
        {
            _jsRuntime = jsRuntime;
           
           
        }

       public async Task<string> GetTokenAsync()
            => await _jsRuntime.InvokeAsync<string>("localStorage.getItem", "authToken");

        public async Task SetTokenAsync(string token)
        {
            if (token == null)
            {
                await _jsRuntime.InvokeAsync<object>("localStorage.removeItem", "authToken");
            }
            else
            {
                await _jsRuntime.InvokeAsync<object>("localStorage.setItem", "authToken", token);
            }
            
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
        }

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var token = await GetTokenAsync();
            var identity = string.IsNullOrEmpty(token)
                ? new ClaimsIdentity()
                : new ClaimsIdentity(ServiceExtensions.ParseClaimsFromJwt(token), "jwt");
            return new AuthenticationState(new ClaimsPrincipal(identity));
        }
    }

这是驻留在登录"页面的提交"按钮中的代码示例, 调用Web Api端点,在此端点上验证用户凭据,之后 将创建一个Jwt令牌并将其传递回调用代码:

And here's a code sample residing in the submit button of a Login page that calls a Web Api endpoint where the user credentials are validated, after which a Jwt Token is created and passed back to the calling code:

async Task SubmitCredentials()
{

    bool lastLoginFailed;

    var httpClient = clientFactory.CreateClient();
    httpClient.BaseAddress = new Uri("https://localhost:44371/");

    var requestJson = JsonSerializer.Serialize(credentials, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });


    var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, "api/user/login")
    {
        Content = new StringContent(requestJson, Encoding.UTF8, "application/json")
    });

    var stringContent = await response.Content.ReadAsStringAsync();

    var result = JsonSerializer.Deserialize<LoginResult>(stringContent, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });

    lastLoginFailed = result.Token == null;
    if (!lastLoginFailed)
    {
        // Success! Store token in underlying auth state service
        await TokenProvider.SetTokenAsync(result.Token);
        NavigationManager.NavigateTo(ReturnUrl);
        
    }
}

Point to note: TokenProvider is an instance of TokenServerAuthenticationStateProvider. 
Its name reflects its functionality: handling the recieved Jwt Token, and providing 
the Access Token when requested.

This line of code: TokenProvider.SetTokenAsync(result.Token); passes the Jwt Token 
to TokenServerAuthenticationStateProvider.SetTokenAsync in which the token is sored 
in the local storage, and then raises AuthenticationStateProvider.AuthenticationStateChanged
event by calling NotifyAuthenticationStateChanged, passing an AuthenticationState object
built from the data contained in the stored Jwt Token.


Note that the GetAuthenticationStateAsync method creates a new ClaimsIdentity object from 
the parsed Jwt Token. All the claims added to the newly created ClaimsIdentity object 
are retrieved from the Jwt Token. I cannot think of a use case where you have to create
a new claim object and add it to the ClaimsPrincipal object.

The following code is executed when an authenticated user is attempting to access
the FecthData page

@code 
{
   private WeatherForecast[] forecasts;


protected override async Task OnInitializedAsync()
{
    var token = await TokenProvider.GetTokenAsync();
   
    var httpClient = clientFactory.CreateClient();
    httpClient.BaseAddress = new Uri("https://localhost:44371/");
    httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"api/WeatherForecast?startDate={DateTime.Now}"));
    
    var stringContent = await response.Content.ReadAsStringAsync();

    forecasts = JsonSerializer.Deserialize<WeatherForecast[]>(stringContent, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
   
}

}

请注意,第一行代码:var token = await TokenProvider.GetTokenAsync();检索 存储在本地存储中的Jwt令牌,并将其添加到请求的Authorization标头中.

Note that the first line of code: var token = await TokenProvider.GetTokenAsync(); retrieves the Jwt Token stored in the local storage, and add it to the Authorization header of the request.

希望这对您有帮助...

Hope this helps...

这篇关于使用Jwt令牌身份验证在Blazor Server App中自定义AuthenticationStateProvider的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆