JWT 请求无法通过授权策略
Posted
技术标签:
【中文标题】JWT 请求无法通过授权策略【英文标题】:JWT request can't go through Authorize policy 【发布时间】:2019-04-18 12:21:44 【问题描述】:我正在 Asp.Net 上实施 JWT 授权,但我在这里遇到了麻烦。当我尝试访问仪表板/主页控制器的方法时,我总是收到“401 Unauthorized”响应。根据我的研究,我可以说请求无法通过
[Authorize(Policy = "ApiUser")]
但是当评论那句话时,我在这里收到一个错误
var userId = _caller.Claims.Single(c => c.Type == "id");
这表明该序列不包含适当的元素,以及是否要打印
_httpContextAccessor.HttpContext.User.Claims
我基本上收到一个“[]”。 另外我想让你注意到令牌是 correct 我正在 postman 的帮助下测试应用程序 我在这个错误上堆积了两天,真的依赖你的帮助。告诉我是否需要一些额外的代码。 代码: 仪表板控制器:
[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";
【问题讨论】:
【参考方案1】:您的问题是由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。
【讨论】:
【参考方案2】:问这个问题已经过去了大约 4 个月,但我希望它对未来的访问者有所帮助,我遇到了同样的问题,这是因为我错过了将 app.UseAuthentication();
添加到我的 Configure
方法:
app.UseAuthentication();
//////////////////////////
app.UseHttpsRedirection();
app.UseStaticFiles();
我也错过了将JwtIssuerOptions
添加到我的appsettings.json
和appsettings.Development.json
:
"JwtIssuerOptions":
"Issuer": "webApi",
"Audience": "http://localhost:5000/"
,
【讨论】:
以上是关于JWT 请求无法通过授权策略的主要内容,如果未能解决你的问题,请参考以下文章
AuthTokenAccessException:无法解码JWT承载:没有JWT承载存在于“授权”的请求头
如何使用自定义策略模式实现 jwt 令牌基础身份验证以在 .net 核心中进行授权?
ReactJS-在http请求的axios方法中将JWT令牌作为授权传递