aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author杨宇千 <crupest@outlook.com>2019-07-20 19:01:35 +0800
committer杨宇千 <crupest@outlook.com>2019-07-20 19:01:35 +0800
commit8fcc916d53443936e4d504067caf0f55f3b141ae (patch)
tree371933aa07605d7eed95f808245e792f65196adb
parent8ff8e6b6fa823ea79ec30a4b62736fe0fb3e5b53 (diff)
downloadtimeline-8fcc916d53443936e4d504067caf0f55f3b141ae.tar.gz
timeline-8fcc916d53443936e4d504067caf0f55f3b141ae.tar.bz2
timeline-8fcc916d53443936e4d504067caf0f55f3b141ae.zip
Write a custom authenticate handler.
-rw-r--r--Timeline/Authenticate/AuthHandler.cs96
-rw-r--r--Timeline/Startup.cs33
2 files changed, 107 insertions, 22 deletions
diff --git a/Timeline/Authenticate/AuthHandler.cs b/Timeline/Authenticate/AuthHandler.cs
new file mode 100644
index 00000000..71d8aeaa
--- /dev/null
+++ b/Timeline/Authenticate/AuthHandler.cs
@@ -0,0 +1,96 @@
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.Net.Http.Headers;
+using System;
+using System.IdentityModel.Tokens.Jwt;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+
+namespace Timeline.Authenticate
+{
+ static class AuthConstants
+ {
+ public const string Scheme = "Bearer";
+ public const string DisplayName = "My Jwt Auth Scheme";
+ }
+
+ class AuthOptions : AuthenticationSchemeOptions
+ {
+ /// <summary>
+ /// The query param key to search for token. If null then query params are not searched for token. Default to <c>"token"</c>.
+ /// </summary>
+ public string TokenQueryParamKey { get; set; } = "token";
+
+ public TokenValidationParameters TokenValidationParameters { get;
+ set; }
+ = new TokenValidationParameters();
+ }
+
+ class AuthHandler : AuthenticationHandler<AuthOptions>
+ {
+ private readonly ILogger<AuthHandler> _logger;
+
+ public AuthHandler(IOptionsMonitor<AuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
+ : base(options, logger, encoder, clock)
+ {
+ _logger = logger.CreateLogger<AuthHandler>();
+ }
+
+ // return null if no token is found
+ private string ExtractToken()
+ {
+ // check the authorization header
+ string header = Request.Headers[HeaderNames.Authorization];
+ if (!string.IsNullOrEmpty(header) && header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+ {
+ var token = header.Substring("Bearer ".Length).Trim();
+ _logger.LogInformation("Token is found in authorization header. Token is {} .", token);
+ return token;
+ }
+
+ // check the query params
+ var paramQueryKey = Options.TokenQueryParamKey;
+ if (!string.IsNullOrEmpty(paramQueryKey))
+ {
+ string token = Request.Query[paramQueryKey];
+ if (!string.IsNullOrEmpty(token))
+ {
+ _logger.LogInformation("Token is found in query param with key \"{}\". Token is {} .", paramQueryKey, token);
+ return token;
+ }
+ }
+
+ // not found anywhere then return null
+ return null;
+ }
+
+ protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
+ {
+ var token = ExtractToken();
+ if (string.IsNullOrEmpty(token))
+ {
+ _logger.LogInformation("No jwt token is found.");
+ return AuthenticateResult.NoResult();
+ }
+
+ var handler = new JwtSecurityTokenHandler();
+ try
+ {
+ var principal = handler.ValidateToken(token, Options.TokenValidationParameters, out var validatedToken);
+ return AuthenticateResult.Success(new AuthenticationTicket(principal, AuthConstants.Scheme));
+ }
+ catch (SecurityTokenException e)
+ {
+ _logger.LogInformation(e, "A jwt token validation failed.");
+ return AuthenticateResult.Fail(e);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Arguments passed to the JwtSecurityTokenHandler.ValidateToken are bad.");
+ throw e;
+ }
+ }
+ }
+}
diff --git a/Timeline/Startup.cs b/Timeline/Startup.cs
index acabe55c..a865f366 100644
--- a/Timeline/Startup.cs
+++ b/Timeline/Startup.cs
@@ -10,6 +10,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.Threading.Tasks;
+using Timeline.Authenticate;
using Timeline.Configs;
using Timeline.Formatters;
using Timeline.Models;
@@ -53,28 +54,16 @@ namespace Timeline
var jwtConfig = Configuration.GetSection(nameof(JwtConfig)).Get<JwtConfig>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
- .AddJwtBearer(o =>
- {
- o.Events = new JwtBearerEvents
- {
- OnMessageReceived = delegate (MessageReceivedContext context)
- {
- context.Request.Query.TryGetValue("token", out var value);
- if (value.Count == 1)
- {
- context.Token = value[0];
- }
- return Task.CompletedTask;
- }
- };
- o.TokenValidationParameters.ValidateIssuer = true;
- o.TokenValidationParameters.ValidateAudience = true;
- o.TokenValidationParameters.ValidateIssuerSigningKey = true;
- o.TokenValidationParameters.ValidateLifetime = true;
- o.TokenValidationParameters.ValidIssuer = jwtConfig.Issuer;
- o.TokenValidationParameters.ValidAudience = jwtConfig.Audience;
- o.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtConfig.SigningKey));
- });
+ .AddScheme<AuthOptions, AuthHandler>(AuthConstants.Scheme, AuthConstants.DisplayName, o =>
+ {
+ o.TokenValidationParameters.ValidateIssuer = true;
+ o.TokenValidationParameters.ValidateAudience = true;
+ o.TokenValidationParameters.ValidateIssuerSigningKey = true;
+ o.TokenValidationParameters.ValidateLifetime = true;
+ o.TokenValidationParameters.ValidIssuer = jwtConfig.Issuer;
+ o.TokenValidationParameters.ValidAudience = jwtConfig.Audience;
+ o.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtConfig.SigningKey));
+ });
services.AddScoped<IUserService, UserService>();
services.AddScoped<IJwtService, JwtService>();