我有一个使用 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);
}
最后,大部分内容都是显而易见的,在我开始这个项目之前我应该知道不要介意这篇文章 - 但是更新所以将来遇到这个的任何人都会看到更合适的路线。