代码之家  ›  专栏  ›  技术社区  ›  Westerlund.io

JWT不能用Blazor存储在ASP.NET内核中

  •  4
  • Westerlund.io  · 技术社区  · 6 年前

    我遵循了以下教程: https://medium.com/@st.mas29/microsoft-blazor-web-api-with-jwt-authentication-part-1-f33a44abab9d

    我下载了示例: https://github.com/StuwiiDev/DotnetCoreJwtAuthentication/tree/Part2

    SampleDataController ,具有 Authorize 标记,则返回401。

    当使用Postman调用和添加令牌时,它可以工作。

    Microsoft.AspNetCore.Authentication.JwtBearer 处理客户端部分(存储令牌)?

    3 回复  |  直到 6 年前
        1
  •  12
  •   itminus    5 年前

    对我的用户进行身份验证缺少什么?Microsoft.AspNetCore.Authentication.JwtBearer不处理客户端部分(存储令牌)吗?

    JwtBearer 在服务器端运行,只验证请求的授权头,即 Authorization: Bearer your_access_token ,并且不关心WebAssembly代码如何运行。所以您需要使用jwt accessToken发送请求。因为教程建议您使用 localStorage ,让我们存储 accessToken .

    WebAssembly 无法访问 BOM helper.js JwtAuthentication.Client/wwwroot/js/ :

    var wasmHelper = {};
    
    wasmHelper.ACCESS_TOKEN_KEY ="__access_token__";
    
    wasmHelper.saveAccessToken = function (tokenStr) {
        localStorage.setItem(wasmHelper.ACCESS_TOKEN_KEY,tokenStr);
    };
    
    wasmHelper.getAccessToken = function () {
        return localStorage.getItem(wasmHelper.ACCESS_TOKEN_KEY);
    };
    

    在你的书里引用剧本 JwtAuthentication.Client/wwwroot/index.html

    <body>
        <app>Loading...</app>
        <script src="js/helper.js"></script>
        <script src="_framework/blazor.webassembly.js"></script>
    </body>
    

    现在,让我们将javascript代码包装到C#。创建新文件 Client/Services/TokenService.cs :

    public class TokenService
    {
        public Task SaveAccessToken(string accessToken) {
            return JSRuntime.Current.InvokeAsync<object>("wasmHelper.saveAccessToken",accessToken);
        }
        public Task<string> GetAccessToken() {
            return JSRuntime.Current.InvokeAsync<string>("wasmHelper.getAccessToken");
        }
    }
    

    // file: Startup.cs 
    services.AddSingleton<TokenService>(myTokenService);
    

    现在我们可以注射 TokenService Login.cshtml

    @using JwtAuthentication.Client.Services
    // ...
    @page "/login"
    // ...
    @inject TokenService tokenService
    
    // ...
    
    @functions {
        public string Email { get; set; } = "";
        public string Password { get; set; } = "";
        public string Token { get; set; } = "";
    
    
        /// <summary>
        /// response from server
        /// </summary>
        private class TokenResponse{
            public string Token;
        }
    
        private async Task SubmitForm()
        {
            var vm = new TokenViewModel
            {
                Email = Email,
                Password = Password
            };
    
            var response = await Http.PostJsonAsync<TokenResponse>("http://localhost:57778/api/Token", vm);
            await tokenService.SaveAccessToken(response.Token);
        }
    }
    

    假设您希望在 FetchData.cshtml

    @functions {
        WeatherForecast[] forecasts;
    
    
        protected override async Task OnInitAsync()
        {
            var token = await tokenService.GetAccessToken();
            Http.DefaultRequestHeaders.Add("Authorization",String.Format("Bearer {0} ",token));
            forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts");
        }
    }
    

    结果是:

    enter image description here

        2
  •  3
  •   Oliver    6 年前

    如果它能帮助其他同样在Blazor应用程序中寻找JWT解决方案的人,我发现@itminus answer非常有用,但它也为我指明了另一个方向。

    我发现的一个问题是 FetchData.cshtml Authorization 第二次头球。

    SubmitForm 在里面 Login.cshtml

        protected async Task SubmitForm()
        {
            // Remove any existing Authorization headers
            Http.DefaultRequestHeaders.Remove("Authorization");
    
            TokenViewModel vm = new TokenViewModel()
            {
                Email = Email,
                Password = Password
            };
    
            TokenResponse response = await Http.PostJsonAsync<TokenResponse>("api/Token/Login", vm);
    
            // Now add the token to the Http singleton
            Http.DefaultRequestHeaders.Add("Authorization", string.Format("Bearer {0} ", response.Token));
        }
    

    然后我意识到,我正在构建一个SPA,所以我根本不需要在请求之间持久化令牌—它只是连接到HttpClient。

        3
  •  2
  •   enet    6 年前

    下面的类处理客户机上的登录过程,将JWT令牌存储在 local 储存。注意:开发人员负责存储JWT令牌,并将其传递给服务器。客户机(Blazor、Angular等)不会自动为他这样做。

    public class SignInManager
        {
            // Receive 'http' instance from DI
            private readonly HttpClient http;
            public SignInManager(HttpClient http)
            {
                this.http = http;
            }
    
            [Inject]
            protected LocalStorage localStorage;
    
    
            public bool IsAuthenticated()
            {
                var token = localStorage.GetItem<string>("token");
    
                return (token != null); 
            }
    
            public string getToken()
            {
                return localStorage.GetItem<string>("token");
            }
    
            public void Clear()
            {
                localStorage.Clear();
            }
    
    
            // model.Email, model.Password, model.RememberMe, lockoutOnFailure: false
            public async Task<bool> PasswordSignInAsync(LoginViewModel model)
            {
                SearchInProgress = true;
                NotifyStateChanged();
    
                var result = await http.PostJsonAsync<Object>("/api/Account", model);
    
                if (result)// result.Succeeded
               {
                  _logger.LogInformation("User logged in.");
    
                  // Save the JWT token in the LocalStorage
                  // https://github.com/BlazorExtensions/Storage
                  await localStorage.SetItem<Object>("token", result);
    
    
                  // Returns true to indicate the user has been logged in and the JWT token 
                  // is saved on the user browser
                 return true;
    
               }
    
            }
        }
    

    public async Task<IList<Profile>> GetProfiles()
            {   
                SearchInProgress = true;
                NotifyStateChanged();
    
                var token = signInManager.getToken();
                if (token == null) {
                    throw new ArgumentNullException(nameof(AppState)); //"No token";
                }
    
                this.http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    
                // .set('Content-Type', 'application/json')
                // this.http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    
                Profiles = await this.http.GetJsonAsync<Profile[]>("/api/Profiles");
    
    
                SearchInProgress = false;
                NotifyStateChanged();
            } 
    

    //您还必须在客户机上设置Startup类,如下所示:

    public void ConfigureServices(IServiceCollection services)
        {
            // Add Blazor.Extensions.Storage
           // Both SessionStorage and LocalStorage are registered
           // https://github.com/BlazorExtensions/Storage
           **services.AddStorage();**
    
          ...
        }
    

    //一般来说,这是你必须对客户做的事在服务器上,您必须有一个方法,比如在Account controller中,它的功能是生成JWT令牌,您必须配置JWT中间件,用必要的属性注释控制器,例如:

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]  
    

    等等。。。

    希望这有帮助。。。

    推荐文章