JWT令牌未通过身份验证

时间:2019-11-20 22:37:46

标签: c# asp.net-core

我已经开发了此API,并且正在尝试向其添加JWT身份验证。 我继承了AspNetCore UserManager来执行所有身份验证。 我成功注册用户,然后对注册用户进行身份验证。一世 按预期方式获取JWT令牌,但是当我尝试访问锁定的资源时, 它拒绝访问,仅返回404(未找到)错误消息。当我 检查我的调试会话,我看到它正在重定向到通常的AspNetCore MVC Web应用程序身份验证路由,但找不到它。有什么可能 是? 这是我的startUp.cs

namespace ProjectManager.Web.API
{
    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.Configure<IdentityOptions>(options =>
            {
                options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz" +
                "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
                options.User.RequireUniqueEmail = false;

                options.Password.RequireUppercase = false;
                options.Password.RequiredUniqueChars = 0;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireDigit = false;

                options.Lockout.AllowedForNewUsers = true;
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(60);
                options.Lockout.MaxFailedAccessAttempts = 4;

                options.SignIn.RequireConfirmedEmail = false;
                options.SignIn.RequireConfirmedPhoneNumber = false;
            });
            services.AddCors();

            // configure strongly typed settings objects
            var appSettingsSection = Configuration.GetSection("AppSettings");
            services.Configure<AppSettings>(appSettingsSection);

            // configure jwt authentication
            var appSettings = appSettingsSection.Get<AppSettings>();
            var key = Encoding.ASCII.GetBytes(appSettings.Secret);
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x =>
            {
                x.Events = new JwtBearerEvents
                {
                    OnTokenValidated = context =>
                    {
                        var userService = context.HttpContext.RequestServices.GetRequiredService<SchoolAuthenticationManager>();
                        var userId = context.Principal.Identity.Name;
                        var user = userService.FindByNameAsync(userId);
                        if (user == null)
                        {
                            // return unauthorized if user no longer exists
                            context.Fail("Unauthorized");
                        }
                        return Task.CompletedTask;
                    }
                };
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,

                    ValidIssuer = (string)Configuration.GetSection("JwtIssuerOptions").GetValue(typeof(string), "Issuer"),
                    ValidAudience = (string)Configuration.GetSection("JwtIssuerOptions").GetValue(typeof(string), "Audience"),
                    TokenDecryptionKey = new SymmetricSecurityKey(key),
                    ClockSkew = TimeSpan.FromMinutes(0),
                };
            });

            services.AddIdentity<SchoolUser, IdentityRole>()
                .AddEntityFrameworkStores<ProjectDbContext>();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseCors(x => x
            .AllowAnyHeader()
            .AllowAnyMethod()
            .AllowAnyOrigin());
            app.UseSwagger();

            //{..................}

            app.UseAuthentication();
            app.UseMvc();
        }
    }
}

这是我在调试会话中看到的消息

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 66.1768ms 302 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: **Request starting HTTP/1.1 GET http://localhost:49873/Account/Login?ReturnUrl=%2Fapi%2Fvalues%2Fget**  
Microsoft.EntityFrameworkCore.Infrastructure:Information: Entity Framework Core 2.2.6-servicing-10079 initialized 'ProjectDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: Non
The thread 0x2a2c has exited with code 0 (0x0).
'iisexpress.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.6\System.Xml.ReaderWriter.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled.
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (6,238ms) [Parameters=[@__get_Item_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [e].[Id], [e].[AccessFailedCount], [e].[ConcurrencyStamp], [e].[Discriminator], [e].[Email], [e].[EmailConfirmed], [e].[LockoutEnabled], [e].[LockoutEnd], [e].[NormalizedEmail], [e].[NormalizedUserName], [e].[PasswordHash], [e].[PhoneNumber], [e].[PhoneNumberConfirmed], [e].[SecurityStamp], [e].[TwoFactorEnabled], [e].[UserName]
FROM [AspNetUsers] AS [e]
WHERE ([e].[Discriminator] = N'SchoolUser') AND ([e].[Id] = @__get_Item_0)
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (600ms) [Parameters=[@__user_Id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId]
FROM [AspNetUserClaims] AS [uc]
WHERE [uc].[UserId] = @__user_Id_0
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (175ms) [Parameters=[@__userId_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT [role].[Name]
FROM [AspNetUserRoles] AS [userRole]
INNER JOIN [AspNetRoles] AS [role] ON [userRole].[RoleId] = [role].[Id]
WHERE [userRole].[UserId] = @__userId_0
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: **Request finished in 11822.4543ms 404** 

这是我的身份验证控制器

[Route("{api}/{controller}/{action}")]
    [ApiController]
    public class AuthenticationController : ControllerBase
    {
        private readonly ProjectAuthenticationManager _authenticationManager; //Inherited from AspNetCore.Identity
        private readonly ProjectSignInManager _signInManager;//Inherited from AspNetCore.Identity
        private readonly AppSettings _appSettings;
        private readonly IMapper _mapper;
        private readonly ILogger<LoginModel> _logger;
        private readonly IConfiguration _configuration;

        public AuthenticationController(ProjectAuthenticationManager AuthenticationManager,
            ProjectSignInManager signInManager,
            ILogger<LoginModel> logger,
            IOptions<AppSettings> setting,
            IConfiguration configuration)
        {
            _authenticationManager = AuthenticationManager;
            _signInManager = signInManager;
            _logger = logger;
            _appSettings = setting.Value;
            _configuration = configuration;
        }

    [HttpPost]
        [AllowAnonymous]
        public async Task<IActionResult> AuthenticateAsync([FromBody]LoginModel loginModel)
        {
            ProjectUser user = await _authenticationManager.FindByNameAsync(loginModel.Username);
            var result = await _signInManager.PasswordSignInAsync(loginModel.Username, loginModel.Password, false, lockoutOnFailure: true);
            if (result.Succeeded)
            {
                _logger.LogInformation("User logged in.");
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new Claim[]
                    {
                        new Claim(ClaimTypes.Name, user.Id.ToString())
                    }),
                    Expires = DateTime.UtcNow.AddDays(7),
                    Audience = (string)_configuration.GetSection("JwtIssuerOptions").GetValue(typeof(string), "Audience"),
                    EncryptingCredentials = null,
                    IssuedAt = DateTime.Now,
                    Issuer = (string)_configuration.GetSection("JwtIssuerOptions").GetValue(typeof(string), "Issuer"),
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                };
                var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
                var tokenString = tokenHandler.WriteToken(token);

                // return basic user info (without password) and token to store client side
                return Ok(new
                {
                    Id = user.Id,
                    Username = user.UserName,
                    //FirstName = user.FirstName,
                    //LastName = user.LastName,
                    Token = tokenString
                });
            }
        }

        [HttpPost]
        public async Task<IActionResult> Register([FromBody]RegisterModel registerModel)
        {
            if (ModelState.IsValid)
            {
                var user = new ProjectUser { UserName = registerModel.Username, Email = registerModel.Email };
                var result = await _authenticationManager.CreateAsync(user, registerModel.Password);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User created a new account with password.");

                    //var code = await _authenticationManager.GenerateEmailConfirmationTokenAsync(user);
                    //var callbackUrl = Url.Page(
                    //    "/Account/ConfirmEmail",
                    //    pageHandler: null,
                    //    values: new { userId = user.Id, code = code },
                    //    protocol: Request.Scheme);

                    //await _emailSender.SendEmailAsync(registerModel.Email, "Confirm your email",
                    //    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                    return Created("", "User account created successfully.");
                }
            }
            // If we got this far, something failed.
            return BadRequest(new string("Something bad happened"));
        }

这是我正在尝试访问的资源

[Route("{api}/{controller}/{action}")]
    [ApiController]
    [Authorize]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public ActionResult<string> Get(int id)
        {
            return "value";
        }
}

1 个答案:

答案 0 :(得分:0)

我已经解决了一段时间了,这次我在另一个项目中创建了相同的身份验证,这一次是分别添加组件。我发现Configure方法中的这段代码引起了问题。

app.UseEndpointRouting();
app.UseEndpoint();

但这是什么意思? 注释掉后,请求成功通过身份验证,但是当我取消注释时,它会将我重定向到AspNetCore.Identity的默认身份验证路由http://localhost:49873/Account/Login