简体   繁体   中英

Blazor: Redirect to Login page when there is no session / JWT token?

I am trying to create a new application in Blazor and am working on authentication. I am using a JWT tokens that is stored in Local storage. When the application loads i need to check if there is a token in the storage. If so, add it to the HTTP headers for all API requests, if not then redirect to the login page before the page loads....... where do i do that? There are multiple pages that need this check so this needs to be done in a single location to cover all. Should this be done in the Startup?

Searching did not provide a solution and since i am new to Blazor finding the right search terms is a bit difficult :)

Create a component and name it RedirectToLogin.razor :

@inject NavigationManager Navigation
@code {
    [CascadingParameter]
    private Task<AuthenticationState> AuthenticationStateTask { get; set; }

    protected override async Task OnInitializedAsync()
    {
        var authenticationState = await AuthenticationStateTask;

        if (authenticationState?.User?.Identity is null || !authenticationState.User.Identity.IsAuthenticated)
        {
            var returnUrl = Navigation.ToBaseRelativePath(Navigation.Uri);

            if (string.IsNullOrWhiteSpace(returnUrl))
                Navigation.NavigateTo("YourLoginPath", true);
            else
                Navigation.NavigateTo($"{YourLoginPath}?returnUrl={returnUrl}", true);
        }
    }
}

Modify App.razor :

<Found Context="routeData">
    <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(HostLayout)">
        <NotAuthorized>
            <RedirectToLogin></RedirectToLogin>
        </NotAuthorized>
    </AuthorizeRouteView>
</Found>

Credits go to: this article

What I did was to write my own http service to do this.

It is called Backend and has a property for the token like this:

    public string ApiToken
    {
        get
        {
            if (!string.IsNullOrEmpty(apitoken))
                return apitoken;

            apitoken = Browser.ReadStorage("apitoken");
            return apitoken;
        }

        set
        {
            Browser.WriteStorage("apitoken", value);
        }
    }

And then in this class implement all the methods that the http class has, while injecting your token. for instance:

    public async Task<T> GetJsonAsync<T>(string uri)
    {
            this.InProgress = true;
            var result = await http.GetJsonAsync<ServerResult<T>>(uri, this.ApiToken);
            this.InProgress = false;

            if (!result.Success)
            {
                BlazorExtensions.Browser.Alert($"Error: {result.ErrorMessage}");
                return default(T);
            }

            return result.ValueObject;

    }

And everywhere use the backend service to do your requests. Also handy for your api url.

EDIT For the redirection part: For brevity I left out this part, but this is the full method:

    public async Task<T> GetJsonAsync<T>(string uri)
    {
        try
        {
            this.InProgress = true;
            var result = await http.GetJsonAsync<ServerResult<T>>(uri, this.ApiToken);
            this.InProgress = false;

            if (!result.Success)
            {
                BlazorExtensions.Browser.Alert($"Error: {result.ErrorMessage}");
                return default(T);
            }

            return result.ValueObject;
        }
        catch (UnauthorizedAccessException)
        {
            this.InProgress = false;
            uriHelper.NavigateTo("/bzr/Logon");
            return default(T);
        }
        catch (Exception e)
        {
            BlazorExtensions.Browser.Alert($"Fout bij http fetch: {e.Message}");
            this.InProgress = false;
            return default(T);
        }
    }

The uriHelper is simply injected in the constructor:

    public IUriHelper uriHelper { get; private set; }

    public Backend(HttpClient httpInstance, IUriHelper uriHelper)
    {
        http = httpInstance;
        this.uriHelper = uriHelper;

Guess that's what you need?

What do you mean Startup ? The Startup class, right ?

Right now the Razor Components framework does not have application event cycles. Be patient...it is coming... Meanwhile, for learning's sake use Components event cycles. Generally, you should store yourJWT in the local storage, including data about the user, whether he has been authenticated and such like, and a service which retrieves this data finding out if the user is authenticated. You can call this service whenever authentication (or authorization) is necessary.

Incidentally, you don't mean to stop the process of ... by saying " before the page loads ". You just mean that you don't want the "New Profile Page", as for instance, in online dating website, to be displayed, if the user is not authenticated, right ? Well, in that case, call the authenticating local service (which retrieves data from the local storage), from the page (Component) OnInit or OnInitAsync methods.

Hope this helps...

A solution (for now, not the best i think?) is to create a base class for all component pages to inherit from and override the OnInit method to do the checks and redirects. This also works when navigating to a link after the application has loaded.

A check in the app.cshtml component only works for the initial startup, not when navigating within the application. This would be nice for other checks, but not to protect routes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM