首页 > 解决方案 > 即使包含不记名令牌,Netcore API 也会返回 401

问题描述

我想保护我的 API 端点,以便它只能通过身份验证进行访问,我最终得到了这个错误。我使用 register 方法注册用户并获取令牌。然后我在请求标头中使用那个长令牌来访问受保护的区域。但我不断收到授权 401 错误。到底发生了什么错误!

http Get http://localhost:5000/Account/Protected 'authorization:Bearer       eyJhb....fx0IM'
HTTP/1.1 401 Unauthorized
Content-Length: 0
Date: Fri, 27 Jul 2018 12:36:46 GMT
Server: Kestrel
WWW-Authenticate: Bearer error="invalid_token", error_description="The token as no expiration"

我有这个控制器配置用于Account Controller. Register 方法运行良好并注册此人,现在如果我想使用受保护的控制器添加测试 api。我收到 401 错误。

namespace Lemon.Auth.Controllers
{
[Route("[controller]/[action]")]
public class AccountController : ControllerBase
{

    private readonly SignInManager<IdentityUser> _signInManager;
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IConfiguration _configuration;

    public AccountController(
        UserManager<IdentityUser> userManager,
        SignInManager<IdentityUser> signInManager,
        IConfiguration configuration
    )
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _configuration = configuration;
    }

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)
    [HttpGet]
    public async Task<object> Protected()
    {
        return "Protected area";
    }

    // Handlers
    [HttpPost]
    public async Task<object> Login([FromBody] LoginDto model)
    {

        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, false, false);

        if(result.Succeeded)
        {
            var appUser = _userManager.Users.SingleOrDefault(r => r.Email == model.Email);
            return await GenerateJwtToken(model.Email, appUser);
        }

        throw new ApplicationException("Invalid Login Attempt");
    }

    // Handler :Register:
    public async Task<object> Register([FromBody] RegisterDto model)
    {
        var user = new IdentityUser
        {
            UserName = model.Email,
            Email = model.Email
        };

        // debuggi
        try
        {
            var result = await _userManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                await _signInManager.SignInAsync(user, false);
                return await GenerateJwtToken(model.Email, user);
            }
        }
        catch (System.Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }

        throw new ApplicationException("Unknown Error");
    }

    private async Task<object> GenerateJwtToken(string email, IdentityUser user)
    {
        var claims = new List<Claim>
        {
            new Claim(JwtRegisteredClaimNames.Sub, email),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
            new Claim(ClaimTypes.NameIdentifier, user.Id)
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        // var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpiresDays"]));
        Console.WriteLine("hello");

        var token = new JwtSecurityToken(
            _configuration["JwtIssuer"],
            _configuration["JwtIssuer"],
            claims,
            signingCredentials: creds
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}
}

这是我的 Startup.cs

        public void ConfigureServices(IServiceCollection services)
    {
        // Db and context
        services.AddEntityFrameworkNpgsql().AddDbContext<ApplicationDbContext>(options =>
            {
                options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"));
            }
        );

        // add Identity
        services.AddIdentity<IdentityUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        // add jwt
        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // clear default behaviour
        services.AddAuthentication(options => 
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(cfg => 
        {
            cfg.RequireHttpsMetadata = false;
            cfg.SaveToken = true;
            cfg.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidIssuer = Configuration["JwtIssuer"],
                    ValidAudience = Configuration["JwtIssuer"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
                    ClockSkew = TimeSpan.Zero // remove delay of token when expire
};
            });

        // add mvc
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ApplicationDbContext dbContext)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        // app.UseHttpsRedirection();
        app.UseMvc();
        app.UseAuthentication();

        // ensure tables are created
        dbContext.Database.EnsureCreated();
    }

我想要实现的只是保护 API。我查阅了本教程https://medium.com/@ozgurgul/asp-net-core-2-0-webapi-jwt-authentication-with-identity-mysql-3698eeba6ff8

标签: c#asp.net-core

解决方案


Edit-2:我刚刚看到您正在使用的教程。它已经这样做了。您可以尝试为令牌添加到期日期吗?错误消息说令牌没有过期。

private async Task<object> GenerateJwtToken(string email, IdentityUser user)
{
    var claims = new List<Claim>
    {
        new Claim(JwtRegisteredClaimNames.Sub, email),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(ClaimTypes.NameIdentifier, user.Id)
    };

    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    // var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpiresDays"]));
    Console.WriteLine("hello");

    var token = new JwtSecurityToken(
        _configuration["JwtIssuer"],
        _configuration["JwtIssuer"],
        claims,
        expires: DatimeTime.UtcNow.AddHours(1), // or smth else
        signingCredentials: creds
    );

编辑:我的第一个答案也是问题的部分原因,但还没有。实际问题是services.AddIdentity<,>添加了 cookie 身份验证,如此处所示。如果您坚持使用 asp.net-identity,则必须进行一些更改。一个例子可以在这里找到。

旧:您的身份验证不起作用,因为您在 mvc 之后添加了身份验证。随便翻一下

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ApplicationDbContext dbContext)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    // app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseMvc();

    // ensure tables are created
    dbContext.Database.EnsureCreated();
}

推荐阅读