簡體   English   中英

.NET 核心 JWT 授權失敗?

[英].NET core JWT authorization failed?

我試圖讓 JWT 在這里工作,登錄后我的客戶端成功接收到令牌,但是當我在 /info 路由請求用戶信息時,授權失敗。 任何幫助將不勝感激,在此先感謝。

我得到錯誤:

Route matched with {action = "GetInfo", controller = "Accounts", page = ""}. Executing controller action with signature System.Threading.Tasks.Task`1[ProjectConker.Controllers.AccountsInfo] GetInfo() on controller ProjectConker.Controllers.AccountsController (ProjectConker).
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.

這是發行令牌的地方。

        [HttpPost("login")]

        public async Task<IActionResult> Post([FromBody]LoginInfo credentials)
        {
            if (credentials == null)
            {
                return BadRequest("Invalid client request");
            }

            var user = await UserManager.FindByNameAsync(credentials.Username);
            await SignInManager.SignInAsync(user, isPersistent: false);

            var result = await SignInManager.PasswordSignInAsync(user, 
            credentials.Password, isPersistent: false, lockoutOnFailure: false);

            if (result.Succeeded)
            {
                var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("**********"));
                var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);


                var tokeOptions = new JwtSecurityToken(
                    issuer: "http://localhost:5000",
                    audience: "http://localhost:5000",

                    claims: new List<Claim>(){
                        new Claim("username", credentials.Username)
                     },

                    expires: DateTime.Now.AddMinutes(5),
                    signingCredentials: signinCredentials
                );

                var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
                return Ok(new { Token = tokenString, UserName = user.UserName });
            }
            else
            {
                return Unauthorized();
            }
        }

將令牌保存到本地存儲

    public Login(loginForm : ILoginForm) : Observable<ILoginForm>
    {
        return this.http.post<ILoginForm>(this.accountsUrl + "/login", loginForm, httpOptions)
        .pipe(map<any, any>((data, index) => {
            localStorage.setItem("auth_token", data.token);
            this.username = data.username;
            this.loggedIn = true;

            console.log(data);
            return data;
        }));
    }

獲取用戶信息

    public GetAccountInfo() : Observable<any>
    {
        httpOptions.headers.set('Authorization', localStorage.getItem('auth_token'));
        return this.http.get(this.accountsUrl + "/info", httpOptions);
    }

返回用戶信息,但此處授權失敗

    [HttpGet]
    [Route("info")]
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

    public async Task<AccountsInfo> GetInfo()
    {

         var usernameClaim = User.Claims.SingleOrDefault(c => c.Type == "username");
         Console.WriteLine(usernameClaim.Value, ConsoleColor.Red);

         var user = await UserManager.FindByNameAsync(usernameClaim.Value);


         return new AccountsInfo{ DisplayName = user.UserName };
     }

我的啟動.cs

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
                services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,

                        ValidIssuer = "http://localhost:5000",
                        ValidAudience = "http://localhost:5000",
                        IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("superSecretKey@345"))
                    };
                });

            //services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            // In production, the Angular files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/dist";
            });

            services.AddHttpClient();

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            services.AddCors(options => options.AddPolicy("CorsPolicy", 
            builder => 
            {
                builder.AllowAnyMethod().AllowAnyHeader()
                       .WithOrigins("*")
                       .AllowCredentials();
            }));

            services.AddSignalR();

            services.AddEntityFrameworkSqlServer();

            services.AddDbContext<ConkerDbContext>(
    options => options.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll));

            services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<ConkerDbContext>();

            services.AddScoped<SearchEngine>();
            services.AddTransient<RoadmapService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            // app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseSpaStaticFiles();

            app.UseAuthentication();

            app.UseSignalR(routes =>
            {
                routes.MapHub<ChatHub>("/api/chat");
            });

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action=Index}/{id?}");
            });

            app.UseSpa(spa =>
            {
                // To learn more about options for serving an Angular SPA from ASP.NET Core,
                // see https://go.microsoft.com/fwlink/?linkid=864501

                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                {
                    spa.UseAngularCliServer(npmScript: "start");
                }
            });


        }

在 ASP.NET 核心令牌認證的 HTTP 請求中添加授權 header 的默認方法是在令牌之前添加Bearer 所以代碼應該是這樣的-

httpOptions.headers.set('Authorization', "Bearer " + localStorage.getItem('auth_token'));

您可以覆蓋默認行為以消除對Bearer的需要。 請閱讀以下帖子以幫助了解在令牌之前使用不記名的原因。 https://www.quora.com/Why-is-Bearer-required-before-the-token-in-Authorization-header-in-a-HTTP-request

也試試這個,

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(claims),
                Expires = DateTime.Now.AddDays(1),
                SigningCredentials = creds,
                IssuedAt = DateTime.Now,
                NotBefore = DateTime.Now
            };

            var tokenHandler = new JwtSecurityTokenHandler();

            var token = tokenHandler.CreateToken(tokenDescriptor);

我的意思是不是創建一個新的 JWTSecurityToken,而是創建一個 SecuritTokenDescriptor,而不是使用 function WriteToken,而是使用 CreateToken。 我以這種方式使用了 JWT 並且它有效。

由於 Angular 的 HttpHeaders 是不可變的,你不能只使用httpOptions.headers.set('Authorization', localStorage.getItem('auth_token')); , 因為它對原來的 object 沒有影響。

首先是 header 無效,使用 Ashique 提供的 Bearer 方法,您的 GetAccountInfo 調用將如下所示:

public GetAccountInfo() : Observable<any> {
    const headers = new HttpHeaders({'Authorization': 'Bearer ' + localStorage.getItem('auth_token')});
    return this.http.get(this.accountsUrl + "/info", {headers});
}

這里我假設您沒有設置其他 HttpOptions,所以我只是將 header 傳遞給 HttpClient。 以這種方式嘗試,讓我們知道它是否仍然無法正常工作。

在您創建令牌的登錄端點中,您正在使用密鑰“**********”,在設置 class 中,您正在使用密鑰“superSecretKey@345”,這是問題所在,身份驗證中間件是嘗試驗證傳入的 JWT 令牌,其密鑰與用於頒發令牌的密鑰不同,您必須使用相同的密鑰進行頒發和驗證,還將密鑰放在“appsettings”等其他地方以避免這種沖突

var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("**********"));
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("superSecretKey@345"))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM