JWT Auth работает с атрибутом Authorize, но не с [Authorize (Policy = "Administrator")] - PullRequest
0 голосов
/ 21 марта 2019

У меня есть приложение .NetCore 2.2, использующее веб-токены Json для аутентификации и авторизации пользователей.

Когда я добавляю атрибут [Authorize] к своим контроллерам, я могу добавить токен на любой запрос к этим контроллерам и взаимодействовать с данными.

Когда я изменяю атрибут Auth для включения роли, например, [Authorize (Policy = "Administrator")] запросы всегда возвращают 403.

Модель User.cs содержит перечисление Role со значениями User / Administrator.

В Startup.cs я добавил RequireRole / RequireAuthenticatedUser.

См. Startup.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.AddCors();

        services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
            .AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; });

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

        #region JWT
        // Configure AppSettings and add to DI  
        var appSettingsSection = Configuration.GetSection("AppSettings");
        services.Configure<AppSettings>(appSettingsSection);

        // Configure jwt authentication
        var appSettings = appSettingsSection.Get<AppSettings>();
        var key = Encoding.ASCII.GetBytes(appSettings.Secret);

        // Add Jwt Authentication Service
        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });
        #endregion

        #region Add Transient DI
        services.AddTransient<IPlayerService, PlayerService>();
        #endregion

        #region Add Authorization
        services.AddAuthorization(options =>
        {
            options.AddPolicy("Administrator",
                p => p.RequireAuthenticatedUser().RequireRole(Role.Administrator.ToString())
            );
            options.AddPolicy("User",
                p => p.RequireAuthenticatedUser().RequireRole(
                    new[] { Role.User.ToString(), Role.User.ToString() }
                )
            );
        });
        #endregion

        #region Cookies
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(options => {
        options.AccessDeniedPath = "/User/ErrorNotAuthorised";
        options.LoginPath = "/User/ErrorNotAuthenticated";
    });
        #endregion
    }

    // 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();
            // seeder recreates and seeds database on each execution
            new DataSeeder(new PlayerService(), new ClubService(), new TeamService(), new TeamPlayerService(), new UserService()).Seed();
        }
        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.UseCookiePolicy();

        app.UseCors(x => x
        .AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader());

        app.UseAuthentication();

        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");
            }
        });
    }
}

Пример метода контроллера:

    // POST: api/Player
    [Authorize(Policy="Administrator")]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public void Post([FromBody] Player player)
    {
        _service.AddPlayer(player);
    }

Этот метод контроллера возвращает неавторизованный запрос 403 из всех взаимодействий. Я думаю, что мой токен JWT не содержит значение Role, но я не уверен, как его проверить или как его включить.

Любая помощь приветствуется.

EDIT:

Смотреть на пользователей

Класс пользователей

    public enum Role
{
    Administrator,
    User
}

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public Team Team { get; set; }
    public Role Role { get; set; }
    public string Token { get; set; }
}

РЕДАКТИРОВАТЬ 2:

Таким образом, все, что действительно необходимо для того, чтобы JWT использовал роли в качестве формы аутентификации, включено в функцию ConfigureServices функции Startup.cs ниже. Я пропустил класс JWT, и также включил это ниже.

Я изменил атрибут auth на контроллерах, чтобы искать Roles = "Administrator" вместо Policies.

Startup.cs

            public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors();

        // Configure AppSettings and add to DI
        var appSettingsSection = Configuration.GetSection("AppSettings");
        services.Configure<AppSettings>(appSettingsSection);

        // Configure jwt authentication
        var appSettings = appSettingsSection.Get<AppSettings>();
        var key = Encoding.ASCII.GetBytes(appSettings.Secret);

        // Add Jwt Authentication Service
        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false
            };
        });

Класс JWT Helper, который я не понял ранее:

    {       
     // generate Jwt token
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(secret);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim(ClaimTypes.Role, user.Role.ToString()),
                new Claim(ClaimTypes.Sid, user.Id.ToString())
            }),
            Expires = DateTime.UtcNow.AddDays(50),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        user.Token = tokenHandler.WriteToken(token);
         return user;

}

Пример контроллера с атрибутом Role:

            [Authorize(Roles = "Administrator")]
    [HttpPost]
    public void Post([FromBody] Player player)
    {
        _service.AddPlayer(player);
    }

Наконец, большая часть этого очевидна, и я должен был знать, прежде чем начать проект, не берите в голову этот пост - но обновление, так что любой, кто сталкивается с этим в будущем, видит более подходящий маршрут.

Ответы [ 2 ]

1 голос
/ 21 марта 2019

Убедитесь, что Role претензии получены из JWT токена. Имя заявки на роль можно установить следующим образом:

.AddJwtBearer(x =>
{
    x.RequireHttpsMetadata = false;
    x.SaveToken = true;
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(key),
        ValidateIssuer = false,
        ValidateAudience = false,

        RoleClaimType = "role" // same name as in your JWT token, as by default it is 
        // "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" 
    };
    options.Events = new JwtBearerEvents
    {
        OnTokenValidated = context =>
        {
            var jwt = (context.SecurityToken as JwtSecurityToken)?.ToString();
            // get your JWT token here if you need to decode it e.g on https://jwt.io
            // And you can re-add role claim if it has different name in token compared to what you want to use in your ClaimIdentity:  
            AddRoleClaims(context.Principal);
            return Task.CompletedTask;
        }
    };

});

private static void AddRoleClaims(ClaimsPrincipal principal)
{
    var claimsIdentity = principal.Identity as ClaimsIdentity;
    if (claimsIdentity != null)
    {
        if (claimsIdentity.HasClaim("role", "AdminRoleNameFromToken"))
        {
            if (!claimsIdentity.HasClaim("role", Role.Administrator.ToString()))
            {
                claimsIdentity.AddClaim(new Claim("role", Role.Administrator.ToString()));
            }
        }
    }
}

И я бы изменил вашу политику как

options.AddPolicy("Administrator", policy => policy.RequireAssertion(context =>
                    context.User.IsInRole(Role.Administrator.ToString())
                ));
0 голосов
/ 22 марта 2019

Я неправильно использовал расширение политики атрибута Authorize.

Я должен был использовать [Authorize (Roles = "")].

Я обновил вопрос, чтобы отразить мою ошибку.

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