如何解析和存储经过身份验证的用户在 Blazor 服务器应用程序中?

huangapple go评论101阅读模式
英文:

How should I parse and store authenticated user in a Blazor server app?

问题

在Blazor服务器应用中将用户的JWT令牌存储在本地存储中,然后在名为AuthenticatedPageBase的基本页面中访问并解析用户的令牌信息似乎是一个常见的方法。但是,您想要优化这个实现以确保仅在用户首次登录时存储令牌信息,并且每个页面都能轻松访问这些信息。

一种更好的方法是使用依赖注入,将用户身份信息存储在应用程序的服务中,以便在需要时访问它。您可以创建一个服务,负责管理用户身份信息,然后在需要时将该服务注入到页面或组件中。

以下是一种示例方法:

  1. 创建一个用户身份信息服务:
public class UserService
{
    private AuthenticatedUser _user;

    public void SetUser(AuthenticatedUser user)
    {
        _user = user;
    }

    public AuthenticatedUser GetUser()
    {
        return _user;
    }
}
  1. 在Blazor应用程序的Startup.cs中将此服务注册为Scoped服务:
services.AddScoped<UserService>();
  1. 在需要访问用户身份信息的页面或组件中注入UserService:
@inject UserService UserService

<!-- 在页面或组件中使用用户身份信息 -->
@if (UserService.GetUser() != null)
{
    <p>Welcome, @UserService.GetUser().UserName!</p>
}
  1. 在用户登录时,使用UserService来设置用户身份信息:
// 在用户登录成功后
var user = AuthenticatedUser.FromIdentity((ClaimsIdentity)authState.User.Identity);
UserService.SetUser(user);

这种方法允许您在用户登录时存储令牌信息,并在整个应用程序中轻松访问它,而不必在每个页面中重复解析令牌。此外,它使用依赖注入来管理用户身份信息,使代码更具可维护性和扩展性。

英文:

I have a blazor server app where I am storing user's jwt token in localstorage. Then I have a base page called AuthenticatedPageBase, in this page, I am accessing the identify and parse the user's info out of the token like this:

 protected AuthenticatedUser _user { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        var authState = await AuthState;
        if (authState.User.Identity != null &amp;&amp; authState.User.Identity.IsAuthenticated)
        {
            _user = AuthenticatedUser.FromIdentity((ClaimsIdentity)authState.User.Identity);
            if(_user == null)
            {
                _nav.NavigateTo(&quot;/auth/logout&quot;, true);
            }
        }
        else
        {
            //the authenticated page base requires user to be authenticated, if not, redirect to 
            _nav.NavigateTo(&quot;/auth/logout&quot;, true);
        }
    }

In the FromIdentity() function, I am simply reading the claim's value and assigning them to the AuthenticatedUser class so that all the inherited pages can have access to the authenticated user.

First of all, I do not know if this is the right way or right place to do this. Because every single page in the app pretty much inherits from this base page (including component too), so this OnIntializedAsync() is called every SINGLE time.

So my question is, how should I structure my app so that every page could easily have access to the authenticated user's information? Of course, I would prefer this is done only once, maybe when user first login and somehow stores it in memory?

I know I am definitely not doing this correctly (I got this implementation from a yt video). Hopefully someone can show me the correct way to do this.

Thanks!

答案1

得分: 1

以下是翻译好的部分:

您可以按照文档中的这部分内容创建一个作用域服务来保存已验证的用户:

public class AuthenticationService
{
    public event Action<ClaimsPrincipal>? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

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

创建一个CustomAuthentication Provider依赖于AuthenticationState,并订阅AuthenticationService.UserChanged事件:

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private AuthenticationState authenticationState;

    public CustomAuthenticationStateProvider(AuthenticationService service)
    {
        authenticationState = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            NotifyAuthenticationStateChanged(
                Task.FromResult(new AuthenticationState(newUser)));
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(authenticationState);
}

注册它:

builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();

分配从令牌中读取的声明:

AuthenticationService.CurrentUser = AuthenticatedUser.FromIdentity((ClaimsIdentity)authState.User.Identity);

现在,如文档所述:

AuthenticationStateProvider是AuthorizeView组件和CascadingAuthenticationState组件使用的基础服务,用于获取用户的身份验证状态。

将代码添加到不同的Razor组件中,您会发现已经在页面之间共享了已验证的用户:

<AuthorizeView>
    <Authorized>
        <p>Hello, @context.User.Identity.Name!</p>
    </Authorized>
    <NotAuthorized>
        <p>You're not authorized.</p>
    </NotAuthorized>
</AuthorizeView>

如何解析和存储经过身份验证的用户在 Blazor 服务器应用程序中?

英文:

You could follow this part of the document Create a Scoped Service to hold the authenticated user:

public class AuthenticationService
{
    public event Action&lt;ClaimsPrincipal&gt;? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

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

Create a CustomAuthentication Provider depends on AuthenticationState and subscribe to
AuthenticationService.UserChanged event

public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private AuthenticationState authenticationState;

    public CustomAuthenticationStateProvider(AuthenticationService service)
    {
        authenticationState = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =&gt;
        {
            NotifyAuthenticationStateChanged(
                Task.FromResult(new AuthenticationState(newUser)));
        };
    }

    public override Task&lt;AuthenticationState&gt; GetAuthenticationStateAsync() =&gt;
        Task.FromResult(authenticationState);
}

Regist it:

builder.Services.AddScoped&lt;AuthenticationStateProvider, CustomAuthenticationStateProvider&gt;();

assign the claims you read from token:

AuthenticationService.CurrentUser = AuthenticatedUser.FromIdentity((ClaimsIdentity)authState.User.Identity);

Now as the doucment describes :

> AuthenticationStateProvider is the underlying service used by the
> AuthorizeView component and CascadingAuthenticationState component to
> obtain the authentication state for a user.

Add the codes to different razor componets ,You would find the authenticated user has been shared between pages:

&lt;AuthorizeView&gt;
    &lt;Authorized&gt;
        &lt;p&gt;Hello, @context.User.Identity.Name!&lt;/p&gt;
    &lt;/Authorized&gt;
    &lt;NotAuthorized&gt;
        &lt;p&gt;You&#39;re not authorized.&lt;/p&gt;
    &lt;/NotAuthorized&gt;
&lt;/AuthorizeView&gt;

如何解析和存储经过身份验证的用户在 Blazor 服务器应用程序中?

huangapple
  • 本文由 发表于 2023年5月18日 09:54:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76277245.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定