- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
当我从我的 Angular 应用程序向我的 .Net Core 2 API 发出请求时,JWT 与请求 header 中发送的 JWT 不同。
Startup.cs
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
_config = builder.Build();
}
IConfigurationRoot _config;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_config);
services.AddDbContext<ApplicationDbContext>(ServiceLifetime.Transient);
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddSingleton<IUserTwoFactorTokenProvider<ApplicationUser>, DataProtectorTokenProvider<ApplicationUser>>();
// Add application services.
// Add application repositories.
// Add options.
services.AddOptions();
services.Configure<StorageAccountOptions>(_config.GetSection("StorageAccount"));
// Add other.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<ApiExceptionFilter>();
// this makes "this.User" reflect the properties of the jwt sent in the request
services.AddTransient<ClaimsPrincipal>(s => s.GetService<IHttpContextAccessor>().HttpContext.User);
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
// set password complexity requirements
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredLength = 6;
options.Tokens.ProviderMap.Add("Default",
new TokenProviderDescriptor(typeof(IUserTwoFactorTokenProvider<ApplicationUser>)));
}).AddEntityFrameworkStores<ApplicationDbContext>();
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(config =>
{
config.RequireHttpsMetadata = false;
config.SaveToken = true;
config.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = _config["Tokens:Issuer"],
ValidAudience = _config["Tokens:Audience"],
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"])),
ValidateLifetime = true
};
});
services.AddAuthorization(config =>
{
config.AddPolicy("Subscribers", p => p.RequireClaim("Subscriber", "True"));
config.AddPolicy("Artists", p => p.RequireClaim("Artist", "True"));
config.AddPolicy("Admins", p => p.RequireClaim("Admin", "True"));
});
services.Configure<DataProtectionTokenProviderOptions>(o =>
{
o.Name = "Default";
o.TokenLifespan = TimeSpan.FromHours(1);
});
services.Configure<AuthMessageSenderOptions>(_config);
// Add framework services.
services.AddMvc(opt =>
{
//opt.Filters.Add(new RequireHttpsAttribute());
}
).AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(_config.GetSection("Logging"));
loggerFactory.AddDebug();
app.Use(async (context, next) =>
{
// just to check the context.User.Claims on request
var temp = context;
await next();
});
app.UseAuthentication();
app.UseMvc();
}
}
这是颁发 token 的地方(在应用程序登录时)
AuthController.cs
private async Task<IList<Claim>> CreateUserClaims(ApplicationUser user)
{
var userClaims = await _userManager.GetClaimsAsync(user);
var newClaims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.NameId, user.Id)
}.Union(userClaims).ToList();
return newClaims;
}
private Object CreateToken(IList<Claim> claims)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _config["Tokens:Issuer"],
audience: _config["Tokens:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddDays(29),
signingCredentials: creds
);
return new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
expiration = token.ValidTo
};
}
private async Task<Object> CreateToken(ApplicationUser user)
{
var claims = await CreateUserClaims(user);
var token = CreateToken(claims);
return token;
}
[HttpPost("token")]
[AllowAnonymous]
public async Task<IActionResult> CreateToken([FromBody] CredentialModel model)
{
var user = await _userManager.FindByNameAsync(model.UserName);
if (user != null)
{
if (_hasher.VerifyHashedPassword(user, user.PasswordHash, model.Password)
== PasswordVerificationResult.Success)
{
var token = await CreateToken(user);
return Ok(token);
}
}
throw new ApiException("Bad email or password.");
}
我已通过 Chrome 调试器网络选项卡确认我请求中的 JWT 是我希望 API 获取的 JWT。
因此,我将在这篇文章中省略 Angular 请求代码。
这是一个通过 UserId 返回项目的 Controller
[HttpGet]
public async Task<IActionResult> Get()
{
var artists = await _manageArtistService.GetAllByUser(this.User);
if (artists == null) return NotFound($"Artists could not be found");
return Ok(artists);
}
这是 Controller 调用的服务
public async Task<IEnumerable<ManageArtistView>> GetAllByUser(ClaimsPrincipal user)
{
// gets all artists of a given user, sorted by artist
var userId = _userService.GetUserId(user);
var artists = await _manageArtistRepository.GetAllByUser(userId);
return artists;
}
在 UserService.cs
中,我尝试了几种不同的方法来访问当前用户。我检查从 Controller 传递的 this.User
。
我还检查了 _context
中的当前上下文 - 您可以在 Startup.cs
中看到的单例。
还有 _caller
来自 Startup.cs
services.AddTransient<ClaimsPrincipal>(s => s.GetService<IHttpContextAccessor>().HttpContext.User);
当我检查这些变量中的任何一个时,Claims
对象不包含与在请求期间发送的 JWT 相同的声明。
我已通过检查 jwt.io 上的声明来验证声明不匹配.
具体来说,我举个场景:
我使用电子邮件 user@example.com
登录我的应用程序。然后在 AuthController.cs
的 CreateUserClaims()
函数中将该电子邮件设置为声明 (Sub) 作为 user.UserName
:
var newClaims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.NameId, user.Id)
}.Union(userClaims).ToList();
然后设置一些其他属性,最终将 token 返回给客户端。客户端将其存储在 localStorage
中。
客户端然后发出请求,在 header 中包含 JWT,并将其添加到请求选项中,如下所示(Angular 服务):
private headers = new Headers(
{
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.authService.token
});
private options = new RequestOptions({ headers: this.headers });
我检查了“网络”选项卡中的 header ,它包含 JWT - 我在 jwt.io 上检查了它它看起来不错 - 有正确的电子邮件和其他声明。
现在我可以注销应用程序,以不同的用户身份登录,获取新的 JWT,并向上面显示的同一 Controller 发出请求,JWT 将有以前的电子邮件,而不是我刚刚登录的新用户。
我确实进行了相同的检查,检查网络选项卡上 header 中的 JWT,以确保声明包含作为 sub
的新电子邮件以及其他声明。
所以这意味着我在新登录时获得了正确的 JWT,但不知何故 API 仍在寻找旧的 JWT。
这有多疯狂?
我注意到的另一件事是,即使是在第一次登录时(假设我刚刚使用 dotnet run
启动 API,然后我向上面显示的同一 Controller 发出第一个请求,它也会丢失nameid
声明。我可以去检查在 Header 请求中发送的 JWT,它确实有 nameid
声明。 所以,再说一遍,< strong>api 将发出正确的 JWT,但是当我在请求中通过 HTTP 发回它时,API 没有我在请求中发送的 JWT。
还有一件事为简单起见,我在控制台中记录了 JWT。我回去发现我今天早上 9 点开始使用的第一个。它的 jti
与当前在 .net 核心 API 中的相同。现在是下午 4 点 45 分。在这两次(上午 9 点和下午 4 点 45 分)之间,我的控制台中有 9 个不同的 JTW,都是从 API 发出的。但是 API 似乎保留了它今天早上创建的第一个 API - 即使在我多次停止和启动该项目之后也是如此。
请帮助我理解我做错了什么。我一定没有完全理解 JWT 的处理方式。
最佳答案
我已经解决了部分问题。
我说来自 UI 的 token 与 .net API 接收的 token 不同是错误的。我说过我正在检查“网络”选项卡中的 header ,确实如此,但不是正确的请求。我的 UI 正在发送多个请求——来自不同的 Angular 模块。我在每个模块中注入(inject)了一个新的身份验证服务(我的 token 存储在其中)。注销时,不会刷新任何模块,因此那些没有保留其旧 token 副本的模块。因此,在登录时,只有受影响的模块(在我的例子中是我的主要 app.module.ts
)被刷新。那些没有被触及的人保留了他们相同的身份验证服务副本。
我从每个模块中删除了注入(inject),让它们从主 app.module.ts
继承,这解决了 UI 和 API 似乎具有不同 token 的问题。
我提到的无法看到 nameid
声明的另一个问题已部分解决。我在 User
中总共有 10 个 Claims
。当我解码 JWT 时,它说我有 sub
和 nameid
。但是,当我检查 UserService.cs
中的 Claims
时,它们没有列为 nameid
和 sub
,而是http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
和 http://schemas.xmlsoap.org/ws/2005/05/identity/声明/名称标识符
。每个都有正确的 Value
。我不确定这是在哪里或如何发生的。我创建了以下自定义中间件代码以查看进入时 token 是什么,它具有 Claim
作为 sub
和 nameid
。
app.Use(async (context, next) =>
{
var authHeader = context.Request.Headers["Authorization"].ToString();
if (authHeader != null && authHeader.StartsWith("bearer", StringComparison.OrdinalIgnoreCase))
{
var tokenStr = authHeader.Substring("Bearer ".Length).Trim();
System.Console.WriteLine(tokenStr);
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadToken(tokenStr) as JwtSecurityToken;
var nameid = token.Claims.First(claim => claim.Type == "nameid").Value;
var identity = new ClaimsIdentity(token.Claims);
context.User = new ClaimsPrincipal(identity);
}
await next();
});
因此,变量nameid
是正确的并且包含期望值。某处 Type
正在从 nameid
和 sub
更改为 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
关于c# - Request Header中的JWT在接收.Net Core API中是不一样的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46993274/
这两个句子有什么区别: res = requests.request('POST', url) 和 res = requests.request.post(url) 最佳答案 它们几乎是一样的:htt
我正在使用“请求对话框”来创建 Facebook 请求。为了让用户收到请求,我需要使用图形 API 访问 Request 对象。我已经尝试了大多数看起来合适的权限设置(read_requests 和
urllib.request和http.client都是python标准库。前者相关方法的文档是 here后者,here (我使用的是3.5) 有谁知道为什么标准库中有两种方法看起来做同样的事情,或者
我是 Twisted 的新手,我不明白为什么在运行我的脚本时会出现此错误。\ 基本上,该脚本由 2 个页面组成,第一个页面是一个 HTML 表单,它调用自身执行一个阻塞方法并显示结果。当请求同时发送到
我有一个客户端 JS 文件,其中包含: agent = require('superagent'); request = agent.get(url); 然后我有类似的东西 request.get(u
提前输入功能可以正常工作。但问题是,提前输入功能会在每个数据请求上发出 JSON 请求,而实际上只应针对一个特定请求发生。 我有以下 Controller : #controllers/agencie
我正在使用 Rust 开发一个小型 API,我不确定如何在两个地方访问来自 Iron 的 Request。 Authentication 中间件为 token 读取一次Request,如果路径被允许(
问题起因 今天一位网友向我们反馈,用Chrome打开某些博客文章时,会出现"Bad Request - Request Too Long. HTTP Error 400. The siz
当我从 LinkedIn 向 https://api.linkedin.com/uas/oauth/requestToken 请求请求 token 时,出现以下错误: oauth_problem=si
我只是想使用 okhttp 下载一些字节数据,但在我完成代码之前,我遇到了一个问题,android studio 报告了一个错误,说“Request(okhttp3.Request.Builder)
我正在使用 Windows 10。我想在我的系统上使用 Angular 4。当我运行 node -v 和 npm -v 时,它会显示版本。但是当我执行语句 npm install -g @angula
我正在尝试让一个简单的 Iron 示例起作用: extern crate iron; extern crate router; use iron::prelude::*; use iron::stat
我正在尝试使用嵌套字典“动态”创建一个数据输入表单(目前,我使用具有 3 个值的数组,但将来数组中的元素数量可能会有所不同)。这似乎工作正常,并且表单“正确”渲染了 html 模板(正确 = 我看到了
从 ASP.NET 中的代码隐藏访问表单或查询字符串值时,使用的优缺点是什么,例如: // short way string p = Request["param"]; 代替: // long way
我遇到了一个问题,我想知道更好的解决方法。 有五个 api 请求并行运行,第二个请求依赖于第四个请求的响应,但所有 5 个请求都已在运行。什么是更好的方法? 需要建议。提前致谢。 最佳答案 调度地面工
我收到以下错误:TypeError:序列项 0:预期字节、字节数组或具有缓冲区接口(interface)的对象、找到元组 我检查了Python文档,urllib.request.Request的参数似
当我向函数添加超时参数时,我的代码总是进入异常并打印出“我失败了”。当我删除超时参数时,代码会正常工作,并进入 try 子句。关于超时参数如何在 urllib.request 函数中工作的任何信息?
我使用 cURL 向服务器发送请求这是链接:Server Side script for cURL request我用 file_get_contents('php://input'); 读取发送的数
请大家帮帮我我正在尝试使用 NUTCH 抓取网站,但它给我错误“java.io.IOException: Job failed!” 我正在运行此命令“bin/nutch solrindex http:
在我的 AngularJS 应用程序中,我无法弄清楚如何对 then promise 的执行更改 location.url 进行单元测试。我有一个函数,登录 ,调用服务,身份验证服务 .它返回 pro
我是一名优秀的程序员,十分优秀!