0

我有一个使用 Json Web 令牌对用户进行身份验证和授权的 .NetCore 2.2 应用程序。

当我将 [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 值,但我不确定如何检查或如何包含它。

任何帮助表示赞赏。

编辑:

关注用户

用户类

    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 使用角色作为一种身份验证形式真正需要的所有内容都包含在下面的 Startup.cs 函数 ConfigureServices 中。我省略了 JWT 类,并在下面也包含了它。

我更改了控制器上的 auth 属性以查找 Roles = "Administrator" 而不是 Policies。

启动.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;

}

具有角色属性的控制器示例:

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

最后,大部分内容都是显而易见的,在我开始这个项目之前我应该​​知道不要介意这篇文章 - 但是更新所以将来遇到这个的任何人都会看到更合适的路线。

4

2 回答 2

1

确保RoleJWT令牌中提取声明。角色声明名称可以这样设置:

.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())
                ));
于 2019-03-21T19:15:31.680 回答
0

我滥用了 Authorize 属性的策略扩展。

我应该一直在使用 [Authorize(Roles = "")]。

我更新了问题以反映我的错误。

于 2019-03-22T16:45:59.523 回答