JWT-запрос не может пройти через политику авторизации - PullRequest
0 голосов
/ 15 ноября 2018

Я внедряю авторизацию JWT в Asp.Net, но у меня возникла проблема. Когда я пытаюсь получить доступ к методу Dashboard / Home, я всегда получаю ответ «401 Unauthorized». По моим исследованиям могу сказать, что запрос не может пройти

[Authorize(Policy = "ApiUser")]

Но, комментируя это утверждение, я получаю здесь ошибку

var userId = _caller.Claims.Single(c => c.Type == "id");

В котором говорится, что последовательность не содержит соответствующих элементов, и если я напечатаю

_httpContextAccessor.HttpContext.User.Claims

Я в основном получаю '[]'. Также я хочу, чтобы вы заметили, что токен правильный
Я тестирую приложение с помощью почтальон
Я наложил эту ошибку на два дня и очень рассчитываю на вашу помощь. Скажите, нужен ли какой-нибудь дополнительный код.
Код:
Контроллер панели приборов:

 [Authorize(Policy = "ApiUser")]
        [Route("api/[controller]/[action]")]
        public class DashboardController : Controller
        {
            private readonly ClaimsPrincipal _caller;
            private readonly BackendContext _appDbContext;
            private readonly IHttpContextAccessor _httpContextAccessor;

            public DashboardController(UserManager<AppUser> userManager, BackendContext appDbContext, IHttpContextAccessor httpContextAccessor)
            {
                _caller = httpContextAccessor.HttpContext.User;
                _appDbContext = appDbContext;
                _httpContextAccessor = httpContextAccessor;
            }

            // GET api/dashboard/home

            [HttpGet]
            public async Task<IActionResult> Home()
            {
                // retrieve the user info
                //HttpContext.User
                //return new OkObjectResult(_httpContextAccessor.HttpContext.User.Claims);
                    var userId = _caller.Claims.Single(c => c.Type == "id");

                var customer = await _appDbContext.Customers.Include(c => c.Identity).SingleAsync(c => c.Identity.Id == userId.Value);

                return new OkObjectResult(new
                {
                    Message = "This is secure API and user data!",
                    customer.Identity.FirstName,
                    customer.Identity.LastName,
                    customer.Identity.PictureUrl,
                    customer.Identity.FacebookId,
                    customer.Location,
                    customer.Locale,
                    customer.Gender
                });
            }
        }

Запуск:

public class Startup
    {
        private const string SecretKey = "iNivDmHLpUA223sqsfhqGbMRdRj1PVkH"; // todo: get this from somewhere secure
        private readonly SymmetricSecurityKey _signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));

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

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddIdentity<IdentityUser, IdentityRole>()
                .AddEntityFrameworkStores<BackendContext>()
                .AddDefaultTokenProviders();

            services.AddDbContext<BackendContext>(options =>
              options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                  b => b.MigrationsAssembly("Backend")));

            services.AddSingleton<IJwtFactory, JwtFactory>();
            var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));

            // Configure JwtIssuerOptions
            services.Configure<JwtIssuerOptions>(options =>
            {
                options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
                options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
                options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
            });

            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],

                ValidateAudience = true,
                ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],

                ValidateIssuerSigningKey = true,
                IssuerSigningKey = _signingKey,

                RequireExpirationTime = false,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero
            };

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

            }).AddJwtBearer(configureOptions =>
            {
                configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
                configureOptions.TokenValidationParameters = tokenValidationParameters;
                configureOptions.SaveToken = true;
            });

            // api user claim policy
            services.AddAuthorization(options =>
            {
                options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
            });

            // add identity
            var builder = services.AddIdentityCore<AppUser>(o =>
            {
                // configure identity options
                o.Password.RequireDigit = false;
                o.Password.RequireLowercase = false;
                o.Password.RequireUppercase = false;
                o.Password.RequireNonAlphanumeric = false;
                o.Password.RequiredLength = 6;
            });
            builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
            builder.AddEntityFrameworkStores<BackendContext>().AddDefaultTokenProviders();

            services.AddAutoMapper();

            services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
            services.AddDbContext<BackendContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
                b => b.MigrationsAssembly("Backend")));
            services.AddTransient<IStoreService, StoreService>();
            services.AddMvc();//.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseAuthentication();
            app.UseDefaultFiles();
            app.UseStaticFiles();

            app.UseMvc();
        }
    }

Константы (используются при запуске):

 public static class Constants
        {
            public static class Strings
            {
                public static class JwtClaimIdentifiers
                {
                    public const string Rol = "rol", Id = "id";
                }

                public static class JwtClaims
                {
                    public const string ApiAccess = "api_access";
                }
            }
        }  

Ответы [ 2 ]

0 голосов
/ 08 апреля 2019

Прошло около 4 месяцев с тех пор, как этот вопрос был задан, но я надеюсь, что это поможет будущим посетителям, у меня была такая же проблема, и это было, потому что я пропустил добавление app.UseAuthentication(); к моему Configure методу :

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

Также я пропустил добавление JwtIssuerOptions к моим appsettings.json и appsettings.Development.json:

"JwtIssuerOptions": {
"Issuer": "webApi",
"Audience": "http://localhost:5000/"
},
0 голосов
/ 16 ноября 2018

Ваша проблема вызвана несоответствием конфигурации между генерацией токена и проверкой для Issuer и Audience.

В Startup.cs вы настраиваете ValidateIssuer = true и ValidateAudience = true, но для предоставленного токена нет iss и aud.

Вот два способа попробовать:

  • Отключить проверку Issuer и Audience

        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = false,
            ValidIssuer = "Issuer",
    
            ValidateAudience = false,
            ValidAudience = "Audience",
    
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = _signingKey,
    
            RequireExpirationTime = false,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };
    
  • Или укажите Issuer и Audience при создании токена.

        public IActionResult Login()
    {
        var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("iNivDmHLpUA223sqsfhqGbMRdRj1PVkH"));
        var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
    
        var tokeOptions = new JwtSecurityToken(
            issuer: "Issuer",
            audience: "Audience",
            claims: new List<Claim>() { new Claim("rol", "api_access") },
            expires: DateTime.Now.AddMinutes(25),
            signingCredentials: signinCredentials
        );
    
        var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
        return Ok(new { Token = tokenString });
    }
    

Вот рабочая демоверсия CoreJwt .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...