aspnet 核心 jwt 令牌作为获取参数
aspnet core jwt token as get param
我正在做一个 aspnet core 2 web api 项目,主要使用者是一个 vue web 应用程序。
Api 使用 jwt 令牌作为身份验证方法,一切正常。
现在我已经编写了所有代码来管理图像存储和检索 to/from 数据库,但是我在从数据库中获取图像时遇到了问题。
所有路由(登录除外)都在身份验证之后,因此要检索图像,我在请求中有传递令牌 header(通常)
这会阻止我使用图像源标记来实际显示图像,如
<img src="/api/images/27" />
相反,我必须编写一些 javascript 代码来请求图像并将内容放在图像标签中,例如
// extracted from vue code (not my code, i'm the backend guy)
getImage() {
this.$http.get('api/images/27', {headers: {'Authorization': 'Bearer ' + this.token},responseType: 'blob'})
.then(response => {
return response.blob()
}).then(blob => {
this.photoUrl = URL.createObjectURL(blob)
})
}
这可行,但不知何故这是一个不必要的并发症。
我在 AspNet Core Identity 中看到
Alternatively, you could obtain the token from somewhere else, such as a different header, or even a cookie. In that case, the handler will use the provided token for all further processing
(摘自 Andre Lock 博客的 this 文章)
正如您还可以看到检查 aspnet core security code,其中显示
Give application opportunity to find from a different location, adjust, or reject token
但我找不到任何关于如何使用此功能和传递自定义令牌的示例。
所以,我的问题是:有没有人知道如何将自定义令牌(可能从 get 参数中读取)传递给身份提供者(甚至可能只针对某些已定义的路由)?
感谢 serpent5 的正确回答。
如果有人感兴趣,从 url 参数读取令牌并将其传递给验证的完整代码如下
service.AddAuthentication(...)
.AddJwtBearer(options =>
// ...
options.Events = new JwtBearerEvents
{
OnMessageReceived = ctx =>
{
// replace "token" with whatever your param name is
if (ctx.Request.Method.Equals("GET") && ctx.Request.Query.ContainsKey("token"))
ctx.Token = ctx.Request.Query["token"];
return Task.CompletedTask;
}
};
});
您的应用程序可以提供此功能,例如使用正确的凭据登录。 (前端 -> 登录正确 -> 后端发回 JWT 令牌。)
然后您可以将后端给您的令牌存储在 cookie/localstorage。
每次您向 API 发送请求时,只需从 cookie/localstorage 检索您的令牌并将其添加到请求 header。
我将向您展示一个示例,说明如何添加处理令牌生成和验证的中间件。
appsettings.conf
{
"Secret": {
"Key": "abcdefghijklmnop123456789"
}
}
密钥用于生成唯一的 JWT 令牌,应单独存储在机器上,这仅用于示例目的
TokenProviderOptions.cs
public class TokenProviderOptions
{
public string Path { get; set; } = "/token";
public string Issuer { get; set; }
public string Audience { get; set; }
public TimeSpan Expiration { get; set; } = TimeSpan.FromHours(1);
public SigningCredentials SigningCredentials { get; set; }
}
A class 将为我们提供令牌生成的基本信息。
"path" 可以更改为您要检索令牌的任何路径。
TokenProviderMiddleware.cs
public class TokenProviderMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenProviderOptions _options;
private readonly IAccountService _accountService;
public TokenProviderMiddleware(RequestDelegate next, IOptions<TokenProviderOptions> options, IAccountService accounteService)
{
_next = next;
_options = options.Value;
_accountService = accounteService;
}
public Task Invoke(HttpContext context)
{
//Check path request
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal)) return _next(context);
//METHOD: POST && Content-Type : x-www-form-urlencode
if (context.Request.Method.Equals("POST") && context.Request.HasFormContentType)
return GenerateToken(context);
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad Request");
}
private async Task GenerateToken(HttpContext context)
{
var username = context.Request.Form["username"];
var password = context.Request.Form["password"];
var identity = await GetIdentity(username, password);
if (identity == null)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid username or password");
return;
}
var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, now.Second.ToString(), ClaimValueTypes.Integer64)
};
var jwt = new JwtSecurityToken(
issuer: _options.Issuer,
audience: _options.Audience,
claims: claims,
notBefore: now,
expires: now.Add(_options.Expiration),
signingCredentials: _options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
access_token = encodedJwt,
expires_in = (int)_options.Expiration.TotalSeconds,
username = username
};
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response,
new JsonSerializerSettings { Formatting = Formatting.Indented }));
}
private Task<ClaimsIdentity> GetIdentity(string username, string password)
{
//THIS STEP COULD BE DIFFERENT, I HAVE AN ACCOUNTSERVICE THAT QUERIES MY DB TO CHECK THE USER CREDENTIALS
var auth = _accountService.Login(username, password).Result;
return auth
? Task.FromResult(new ClaimsIdentity(new GenericIdentity(username, "Token"), new Claim[] { }))
: Task.FromResult<ClaimsIdentity>(null);
}
}
这是中间件部分。您必须发送 POST 请求 header 类型 application/x-www-form-urlencoded
和 2 个字段 username
和 password
到您定义的 Path
在你的 TokenProviderOptions
.
如果检查通过,您将获得一个 jwt 令牌。
最后是 Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
//Mvc
services.AddMvc();
//...
//Authentication
services.AddAuthentication()
.AddJwtBearer(jwt =>
{
var signingKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("Secret:Key").Value));
jwt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = "2CIssuer",
ValidateAudience = true,
ValidAudience = "2CAudience",
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
//Authorization
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build());
});
}
public void Configure(IApplicationBuilder app)
{
//...
//Authentication
var signingKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("Secret:Key").Value));
var options = new TokenProviderOptions
{
Audience = "2CAudience",
Issuer = "2CIssuer",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
};
app.UseAuthentication();
//JWT
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
//Mvc
app.UseMvc();
}
}
我省略了多余的代码。这会添加您的自定义中间件并配置应用程序以使用 JWT 令牌。
您所要做的就是更改提到的自定义参数,使用 'token': tokenValue 签署您的请求,就可以了!
我这里有一个可用的后端模板:https://github.com/BusschaertTanguy/dotnet_core_backend_template
仔细检查所有内容。
希望对您有所帮助!
这可以使用连接到提供给 AddJwtBearer
的 JwtBearerOptions
实例的 JwtBearerEvents
来处理。具体来说,可以实施一个 OnMessageReceived
事件来提供令牌本身。这是一个例子:
services.AddAuthentication(...)
.AddJwtBearer(jwtBearerOptions =>
{
// ...
jwtBearerOptions.Events = new JwtBearerEvents
{
OnMessageReceived = ctx =>
{
// Access ctx.Request here for the query-string, route, etc.
ctx.Token = "";
return Task.CompletedTask;
}
};
})
您可以在 source code 中看到它是如何使用的:
// event can set the token
await Events.MessageReceived(messageReceivedContext);
// ...
// If application retrieved token from somewhere else, use that.
token = messageReceivedContext.Token;
我正在做一个 aspnet core 2 web api 项目,主要使用者是一个 vue web 应用程序。
Api 使用 jwt 令牌作为身份验证方法,一切正常。
现在我已经编写了所有代码来管理图像存储和检索 to/from 数据库,但是我在从数据库中获取图像时遇到了问题。
所有路由(登录除外)都在身份验证之后,因此要检索图像,我在请求中有传递令牌 header(通常)
这会阻止我使用图像源标记来实际显示图像,如
<img src="/api/images/27" />
相反,我必须编写一些 javascript 代码来请求图像并将内容放在图像标签中,例如
// extracted from vue code (not my code, i'm the backend guy)
getImage() {
this.$http.get('api/images/27', {headers: {'Authorization': 'Bearer ' + this.token},responseType: 'blob'})
.then(response => {
return response.blob()
}).then(blob => {
this.photoUrl = URL.createObjectURL(blob)
})
}
这可行,但不知何故这是一个不必要的并发症。
我在 AspNet Core Identity 中看到
Alternatively, you could obtain the token from somewhere else, such as a different header, or even a cookie. In that case, the handler will use the provided token for all further processing
(摘自 Andre Lock 博客的 this 文章) 正如您还可以看到检查 aspnet core security code,其中显示
Give application opportunity to find from a different location, adjust, or reject token
但我找不到任何关于如何使用此功能和传递自定义令牌的示例。
所以,我的问题是:有没有人知道如何将自定义令牌(可能从 get 参数中读取)传递给身份提供者(甚至可能只针对某些已定义的路由)?
感谢 serpent5 的正确回答。
如果有人感兴趣,从 url 参数读取令牌并将其传递给验证的完整代码如下
service.AddAuthentication(...)
.AddJwtBearer(options =>
// ...
options.Events = new JwtBearerEvents
{
OnMessageReceived = ctx =>
{
// replace "token" with whatever your param name is
if (ctx.Request.Method.Equals("GET") && ctx.Request.Query.ContainsKey("token"))
ctx.Token = ctx.Request.Query["token"];
return Task.CompletedTask;
}
};
});
您的应用程序可以提供此功能,例如使用正确的凭据登录。 (前端 -> 登录正确 -> 后端发回 JWT 令牌。)
然后您可以将后端给您的令牌存储在 cookie/localstorage。
每次您向 API 发送请求时,只需从 cookie/localstorage 检索您的令牌并将其添加到请求 header。
我将向您展示一个示例,说明如何添加处理令牌生成和验证的中间件。
appsettings.conf
{
"Secret": {
"Key": "abcdefghijklmnop123456789"
}
}
密钥用于生成唯一的 JWT 令牌,应单独存储在机器上,这仅用于示例目的
TokenProviderOptions.cs
public class TokenProviderOptions
{
public string Path { get; set; } = "/token";
public string Issuer { get; set; }
public string Audience { get; set; }
public TimeSpan Expiration { get; set; } = TimeSpan.FromHours(1);
public SigningCredentials SigningCredentials { get; set; }
}
A class 将为我们提供令牌生成的基本信息。 "path" 可以更改为您要检索令牌的任何路径。
TokenProviderMiddleware.cs
public class TokenProviderMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenProviderOptions _options;
private readonly IAccountService _accountService;
public TokenProviderMiddleware(RequestDelegate next, IOptions<TokenProviderOptions> options, IAccountService accounteService)
{
_next = next;
_options = options.Value;
_accountService = accounteService;
}
public Task Invoke(HttpContext context)
{
//Check path request
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal)) return _next(context);
//METHOD: POST && Content-Type : x-www-form-urlencode
if (context.Request.Method.Equals("POST") && context.Request.HasFormContentType)
return GenerateToken(context);
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad Request");
}
private async Task GenerateToken(HttpContext context)
{
var username = context.Request.Form["username"];
var password = context.Request.Form["password"];
var identity = await GetIdentity(username, password);
if (identity == null)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid username or password");
return;
}
var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, now.Second.ToString(), ClaimValueTypes.Integer64)
};
var jwt = new JwtSecurityToken(
issuer: _options.Issuer,
audience: _options.Audience,
claims: claims,
notBefore: now,
expires: now.Add(_options.Expiration),
signingCredentials: _options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
access_token = encodedJwt,
expires_in = (int)_options.Expiration.TotalSeconds,
username = username
};
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response,
new JsonSerializerSettings { Formatting = Formatting.Indented }));
}
private Task<ClaimsIdentity> GetIdentity(string username, string password)
{
//THIS STEP COULD BE DIFFERENT, I HAVE AN ACCOUNTSERVICE THAT QUERIES MY DB TO CHECK THE USER CREDENTIALS
var auth = _accountService.Login(username, password).Result;
return auth
? Task.FromResult(new ClaimsIdentity(new GenericIdentity(username, "Token"), new Claim[] { }))
: Task.FromResult<ClaimsIdentity>(null);
}
}
这是中间件部分。您必须发送 POST 请求 header 类型 application/x-www-form-urlencoded
和 2 个字段 username
和 password
到您定义的 Path
在你的 TokenProviderOptions
.
如果检查通过,您将获得一个 jwt 令牌。
最后是 Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
//Mvc
services.AddMvc();
//...
//Authentication
services.AddAuthentication()
.AddJwtBearer(jwt =>
{
var signingKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("Secret:Key").Value));
jwt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = "2CIssuer",
ValidateAudience = true,
ValidAudience = "2CAudience",
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
//Authorization
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build());
});
}
public void Configure(IApplicationBuilder app)
{
//...
//Authentication
var signingKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("Secret:Key").Value));
var options = new TokenProviderOptions
{
Audience = "2CAudience",
Issuer = "2CIssuer",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
};
app.UseAuthentication();
//JWT
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
//Mvc
app.UseMvc();
}
}
我省略了多余的代码。这会添加您的自定义中间件并配置应用程序以使用 JWT 令牌。
您所要做的就是更改提到的自定义参数,使用 'token': tokenValue 签署您的请求,就可以了!
我这里有一个可用的后端模板:https://github.com/BusschaertTanguy/dotnet_core_backend_template 仔细检查所有内容。
希望对您有所帮助!
这可以使用连接到提供给 AddJwtBearer
的 JwtBearerOptions
实例的 JwtBearerEvents
来处理。具体来说,可以实施一个 OnMessageReceived
事件来提供令牌本身。这是一个例子:
services.AddAuthentication(...)
.AddJwtBearer(jwtBearerOptions =>
{
// ...
jwtBearerOptions.Events = new JwtBearerEvents
{
OnMessageReceived = ctx =>
{
// Access ctx.Request here for the query-string, route, etc.
ctx.Token = "";
return Task.CompletedTask;
}
};
})
您可以在 source code 中看到它是如何使用的:
// event can set the token
await Events.MessageReceived(messageReceivedContext);
// ...
// If application retrieved token from somewhere else, use that.
token = messageReceivedContext.Token;