【问题标题】:.NET core JWT authorization failed?.NET 核心 JWT 授权失败?
【发布时间】:2020-02-16 12:09:21
【问题描述】:

我正在尝试让 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");
                }
            });


        }

【问题讨论】:

    标签: c# angular authentication .net-core jwt


    【解决方案1】:

    由于 Angular 的 HttpHeaders 是 immutable,所以不能只使用 httpOptions.headers.set('Authorization', localStorage.getItem('auth_token'));,因为它对原始对象没有影响。

    首先是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,所以我只是将标头传递给 HttpClient。试试这种方式,如果它仍然不起作用,请告诉我们。

    【讨论】:

    • 哦哦,谢谢
    【解决方案2】:

    在 ASP.NET 核心令牌认证的 HTTP 请求中添加授权头的默认方法是在令牌前添加 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,而不是使用函数 WriteToken,而是使用 CreateToken。我曾以这种方式使用过 JWT,它确实有效。

    【讨论】:

    • 这样做后我仍然遇到同样的未经授权的错误。但很高兴知道它必须在那里。
    【解决方案3】:

    在您创建使用密钥“**********”的令牌的登录端点和您使用密钥“superSecretKey@345”的设置类中,这就是问题所在,身份验证中间件正在尝试使用与用于发布令牌的密钥不同的密钥来验证传入的 JWT 令牌,您必须使用相同的密钥来发布和验证,还将密钥放在“appsettings”等其他地方以避免这种冲突

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

    【讨论】:

    • 我对不一致的地方不好,但为了保密,我忘了给第二个打上星号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-13
    • 2022-10-13
    • 2019-08-04
    • 2021-07-23
    • 2017-06-26
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多