>
设置一个可以创建JWT令牌并在头中返回的登录路由。我将此与现有的RESTful服务集成,该服务将告诉我用户名和密码是否有效。在我正在研究的ASP.NET4项目中,这可以通过以下路由来完成:https://github.com/stewartm83/jwt-webapi/blob/master/src/jwtwebapi/controllers/accountController.cs#L24-L54
拦截到需要授权的路由的传入请求,解密和验证头中的JWT令牌,并使路由可以访问JWT令牌有效载荷中的用户信息。例如:https://github.com/stewartm83/jwt-webapi/blob/master/src/jwtwebapi/app_start/authhandler.cs
我在ASP.NET Core中看到的所有示例都非常复杂,并且依赖于OAuth、IS、OpenIDICT和EF中的一些或全部,我希望避免这些示例。
编辑:答案我最终使用了这个答案:https://stackoverflow.com/A/33217340/373655
注/更新:
下面的代码是针对.NET Core 1.1的
由于.NET Core 1非常RTM,身份验证随着从.NET Core 1到2.0的跳转而改变(aka是[部分?]通过中断更改而修复的)。
这就是为什么bellow代码不再适用于.NET Core 2.0的原因。
但它仍然是一本有用的读物。
同时,您可以在我的github测试repo上找到一个ASP.NET Core 2.0 JWT-Cookie-Authentication的工作示例。通过BouncyCastle实现了MS-RSA&MS-ECDSA抽象类,并为RSA&ECDSA提供了密钥生成器。
亡灵巫术。
我更深入地挖掘了JWT。以下是我的发现:
您需要添加Microsoft.AspNetCore.Authentication.JWTBearer
然后您可以设置
app.UseJwtBearerAuthentication(bearerOptions);
var bearerOptions = new JwtBearerOptions()
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters,
Events = new CustomBearerEvents()
};
// Optional
// bearerOptions.SecurityTokenValidators.Clear();
// bearerOptions.SecurityTokenValidators.Add(new MyTokenHandler());
// https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication.JwtBearer/Events/JwtBearerEvents.cs
public class CustomBearerEvents : Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents
{
/// <summary>
/// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed.
/// </summary>
public Func<AuthenticationFailedContext, Task> OnAuthenticationFailed { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked when a protocol message is first received.
/// </summary>
public Func<MessageReceivedContext, Task> OnMessageReceived { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked after the security token has passed validation and a ClaimsIdentity has been generated.
/// </summary>
public Func<TokenValidatedContext, Task> OnTokenValidated { get; set; } = context => Task.FromResult(0);
/// <summary>
/// Invoked before a challenge is sent back to the caller.
/// </summary>
public Func<JwtBearerChallengeContext, Task> OnChallenge { get; set; } = context => Task.FromResult(0);
Task IJwtBearerEvents.AuthenticationFailed(AuthenticationFailedContext context)
{
return OnAuthenticationFailed(context);
}
Task IJwtBearerEvents.Challenge(JwtBearerChallengeContext context)
{
return OnChallenge(context);
}
Task IJwtBearerEvents.MessageReceived(MessageReceivedContext context)
{
return OnMessageReceived(context);
}
Task IJwtBearerEvents.TokenValidated(TokenValidatedContext context)
{
return OnTokenValidated(context);
}
}
var tokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = "ExampleIssuer",
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = "ExampleAudience",
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero,
};
// https://gist.github.com/pmhsfelix/4151369
public class MyTokenHandler : Microsoft.IdentityModel.Tokens.ISecurityTokenValidator
{
private int m_MaximumTokenByteSize;
public MyTokenHandler()
{ }
bool ISecurityTokenValidator.CanValidateToken
{
get
{
// throw new NotImplementedException();
return true;
}
}
int ISecurityTokenValidator.MaximumTokenSizeInBytes
{
get
{
return this.m_MaximumTokenByteSize;
}
set
{
this.m_MaximumTokenByteSize = value;
}
}
bool ISecurityTokenValidator.CanReadToken(string securityToken)
{
System.Console.WriteLine(securityToken);
return true;
}
ClaimsPrincipal ISecurityTokenValidator.ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
// validatedToken = new JwtSecurityToken(securityToken);
try
{
tokenHandler.ValidateToken(securityToken, validationParameters, out validatedToken);
validatedToken = new JwtSecurityToken("jwtEncodedString");
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
throw;
}
ClaimsPrincipal principal = null;
// SecurityToken validToken = null;
validatedToken = null;
System.Collections.Generic.List<System.Security.Claims.Claim> ls =
new System.Collections.Generic.List<System.Security.Claims.Claim>();
ls.Add(
new System.Security.Claims.Claim(
System.Security.Claims.ClaimTypes.Name, "IcanHazUsr_éèêëïàáâäåãæóòôöõõúùûüñçø_ÉÈÊËÏÀÁÂÄÅÃÆÓÒÔÖÕÕÚÙÛÜÑÇØ 你好,世界 Привет\tмир"
, System.Security.Claims.ClaimValueTypes.String
)
);
//
System.Security.Claims.ClaimsIdentity id = new System.Security.Claims.ClaimsIdentity("authenticationType");
id.AddClaims(ls);
principal = new System.Security.Claims.ClaimsPrincipal(id);
return principal;
throw new NotImplementedException();
}
}
创作沿着以下路线进行
// System.Security.Cryptography.X509Certificates.X509Certificate2 cert2 = new System.Security.Cryptography.X509Certificates.X509Certificate2(byte[] rawData);
System.Security.Cryptography.X509Certificates.X509Certificate2 cert2 =
DotNetUtilities.CreateX509Cert2("mycert");
Microsoft.IdentityModel.Tokens.SecurityKey secKey = new X509SecurityKey(cert2);
例如使用DER证书中的BouncyCastle:
// http://stackoverflow.com/questions/36942094/how-can-i-generate-a-self-signed-cert-without-using-obsolete-bouncycastle-1-7-0
public static System.Security.Cryptography.X509Certificates.X509Certificate2 CreateX509Cert2(string certName)
{
var keypairgen = new Org.BouncyCastle.Crypto.Generators.RsaKeyPairGenerator();
keypairgen.Init(new Org.BouncyCastle.Crypto.KeyGenerationParameters(
new Org.BouncyCastle.Security.SecureRandom(
new Org.BouncyCastle.Crypto.Prng.CryptoApiRandomGenerator()
)
, 1024
)
);
Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair keypair = keypairgen.GenerateKeyPair();
// --- Until here we generate a keypair
var random = new Org.BouncyCastle.Security.SecureRandom(
new Org.BouncyCastle.Crypto.Prng.CryptoApiRandomGenerator()
);
// SHA1WITHRSA
// SHA256WITHRSA
// SHA384WITHRSA
// SHA512WITHRSA
// SHA1WITHECDSA
// SHA224WITHECDSA
// SHA256WITHECDSA
// SHA384WITHECDSA
// SHA512WITHECDSA
Org.BouncyCastle.Crypto.ISignatureFactory signatureFactory =
new Org.BouncyCastle.Crypto.Operators.Asn1SignatureFactory("SHA512WITHRSA", keypair.Private, random)
;
var gen = new Org.BouncyCastle.X509.X509V3CertificateGenerator();
var CN = new Org.BouncyCastle.Asn1.X509.X509Name("CN=" + certName);
var SN = Org.BouncyCastle.Math.BigInteger.ProbablePrime(120, new Random());
gen.SetSerialNumber(SN);
gen.SetSubjectDN(CN);
gen.SetIssuerDN(CN);
gen.SetNotAfter(DateTime.Now.AddYears(1));
gen.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
gen.SetPublicKey(keypair.Public);
// -- Are these necessary ?
// public static readonly DerObjectIdentifier AuthorityKeyIdentifier = new DerObjectIdentifier("2.5.29.35");
// OID value: 2.5.29.35
// OID description: id-ce-authorityKeyIdentifier
// This extension may be used either as a certificate or CRL extension.
// It identifies the public key to be used to verify the signature on this certificate or CRL.
// It enables distinct keys used by the same CA to be distinguished (e.g., as key updating occurs).
// http://stackoverflow.com/questions/14930381/generating-x509-certificate-using-bouncy-castle-java
gen.AddExtension(
Org.BouncyCastle.Asn1.X509.X509Extensions.AuthorityKeyIdentifier.Id,
false,
new Org.BouncyCastle.Asn1.X509.AuthorityKeyIdentifier(
Org.BouncyCastle.X509.SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keypair.Public),
new Org.BouncyCastle.Asn1.X509.GeneralNames(new Org.BouncyCastle.Asn1.X509.GeneralName(CN)),
SN
));
// OID value: 1.3.6.1.5.5.7.3.1
// OID description: Indicates that a certificate can be used as an SSL server certificate.
gen.AddExtension(
Org.BouncyCastle.Asn1.X509.X509Extensions.ExtendedKeyUsage.Id,
false,
new Org.BouncyCastle.Asn1.X509.ExtendedKeyUsage(new ArrayList()
{
new Org.BouncyCastle.Asn1.DerObjectIdentifier("1.3.6.1.5.5.7.3.1")
}));
// -- End are these necessary ?
Org.BouncyCastle.X509.X509Certificate bouncyCert = gen.Generate(signatureFactory);
byte[] ba = bouncyCert.GetEncoded();
System.Security.Cryptography.X509Certificates.X509Certificate2 msCert = new System.Security.Cryptography.X509Certificates.X509Certificate2(ba);
return msCert;
}
随后,您可以添加一个包含jwt-barer的自定义cookie格式:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "MyCookieMiddlewareInstance",
CookieName = "SecurityByObscurityDoesntWork",
ExpireTimeSpan = new System.TimeSpan(15, 0, 0),
LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Unauthorized/"),
AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Forbidden/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
CookieSecure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest,
CookieHttpOnly = false,
TicketDataFormat = new CustomJwtDataFormat("foo", tokenValidationParameters)
// DataProtectionProvider = null,
// DataProtectionProvider = new DataProtectionProvider(new System.IO.DirectoryInfo(@"c:\shared-auth-ticket-keys\"),
//delegate (DataProtectionConfiguration options)
//{
// var op = new Microsoft.AspNet.DataProtection.AuthenticatedEncryption.AuthenticatedEncryptionOptions();
// op.EncryptionAlgorithm = Microsoft.AspNet.DataProtection.AuthenticatedEncryption.EncryptionAlgorithm.AES_256_GCM:
// options.UseCryptographicAlgorithms(op);
//}
//),
});
其中CustomJwtDataFormat是类似于
public class CustomJwtDataFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string algorithm;
private readonly TokenValidationParameters validationParameters;
public CustomJwtDataFormat(string algorithm, TokenValidationParameters validationParameters)
{
this.algorithm = algorithm;
this.validationParameters = validationParameters;
}
// This ISecureDataFormat implementation is decode-only
string ISecureDataFormat<AuthenticationTicket>.Protect(AuthenticationTicket data)
{
return MyProtect(data, null);
}
string ISecureDataFormat<AuthenticationTicket>.Protect(AuthenticationTicket data, string purpose)
{
return MyProtect(data, purpose);
}
AuthenticationTicket ISecureDataFormat<AuthenticationTicket>.Unprotect(string protectedText)
{
return MyUnprotect(protectedText, null);
}
AuthenticationTicket ISecureDataFormat<AuthenticationTicket>.Unprotect(string protectedText, string purpose)
{
return MyUnprotect(protectedText, purpose);
}
private string MyProtect(AuthenticationTicket data, string purpose)
{
return "wadehadedudada";
throw new System.NotImplementedException();
}
// http://blogs.microsoft.co.il/sasha/2012/01/20/aggressive-inlining-in-the-clr-45-jit/
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private AuthenticationTicket MyUnprotect(string protectedText, string purpose)
{
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
ClaimsPrincipal principal = null;
SecurityToken validToken = null;
System.Collections.Generic.List<System.Security.Claims.Claim> ls =
new System.Collections.Generic.List<System.Security.Claims.Claim>();
ls.Add(
new System.Security.Claims.Claim(
System.Security.Claims.ClaimTypes.Name, "IcanHazUsr_éèêëïàáâäåãæóòôöõõúùûüñçø_ÉÈÊËÏÀÁÂÄÅÃÆÓÒÔÖÕÕÚÙÛÜÑÇØ 你好,世界 Привет\tмир"
, System.Security.Claims.ClaimValueTypes.String
)
);
//
System.Security.Claims.ClaimsIdentity id = new System.Security.Claims.ClaimsIdentity("authenticationType");
id.AddClaims(ls);
principal = new System.Security.Claims.ClaimsPrincipal(id);
return new AuthenticationTicket(principal, new AuthenticationProperties(), "MyCookieMiddlewareInstance");
try
{
principal = handler.ValidateToken(protectedText, this.validationParameters, out validToken);
JwtSecurityToken validJwt = validToken as JwtSecurityToken;
if (validJwt == null)
{
throw new System.ArgumentException("Invalid JWT");
}
if (!validJwt.Header.Alg.Equals(algorithm, System.StringComparison.Ordinal))
{
throw new System.ArgumentException($"Algorithm must be '{algorithm}'");
}
// Additional custom validation of JWT claims here (if any)
}
catch (SecurityTokenValidationException)
{
return null;
}
catch (System.ArgumentException)
{
return null;
}
// Validation passed. Return a valid AuthenticationTicket:
return new AuthenticationTicket(principal, new AuthenticationProperties(), "MyCookieMiddlewareInstance");
}
}
// https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication.JwtBearer/Events/IJwtBearerEvents.cs
// http://codereview.stackexchange.com/questions/45974/web-api-2-authentication-with-jwt
public class TokenMaker
{
class SecurityConstants
{
public static string TokenIssuer;
public static string TokenAudience;
public static int TokenLifetimeMinutes;
}
public static string IssueToken()
{
SecurityKey sSKey = null;
var claimList = new List<Claim>()
{
new Claim(ClaimTypes.Name, "userName"),
new Claim(ClaimTypes.Role, "role") //Not sure what this is for
};
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
SecurityTokenDescriptor desc = makeSecurityTokenDescriptor(sSKey, claimList);
// JwtSecurityToken tok = tokenHandler.CreateJwtSecurityToken(desc);
return tokenHandler.CreateEncodedJwt(desc);
}
public static ClaimsPrincipal ValidateJwtToken(string jwtToken)
{
SecurityKey sSKey = null;
var tokenHandler = new JwtSecurityTokenHandler();
// Parse JWT from the Base64UrlEncoded wire form
//(<Base64UrlEncoded header>.<Base64UrlEncoded body>.<signature>)
JwtSecurityToken parsedJwt = tokenHandler.ReadToken(jwtToken) as JwtSecurityToken;
TokenValidationParameters validationParams =
new TokenValidationParameters()
{
RequireExpirationTime = true,
ValidAudience = SecurityConstants.TokenAudience,
ValidIssuers = new List<string>() { SecurityConstants.TokenIssuer },
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
IssuerSigningKey = sSKey,
};
SecurityToken secT;
return tokenHandler.ValidateToken("token", validationParams, out secT);
}
private static SecurityTokenDescriptor makeSecurityTokenDescriptor(SecurityKey sSKey, List<Claim> claimList)
{
var now = DateTime.UtcNow;
Claim[] claims = claimList.ToArray();
return new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Issuer = SecurityConstants.TokenIssuer,
Audience = SecurityConstants.TokenAudience,
IssuedAt = System.DateTime.UtcNow,
Expires = System.DateTime.UtcNow.AddMinutes(SecurityConstants.TokenLifetimeMinutes),
NotBefore = System.DateTime.UtcNow.AddTicks(-1),
SigningCredentials = new SigningCredentials(sSKey, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.EcdsaSha512Signature)
};
}
}
这应该正是你要找的。
还有这两个:
https://goblincoding.com/2016/07/03/isualing-and-authenticating-jwt-tokens-in-asp-net-core-webapi-part-i/
我不得不研究JWT,因为我们需要保护我们的应用程序。
因为我仍然必须使用.NET2.0,所以我必须编写自己的库。
本周末我已经将其结果移植到.NET核心。在这里可以找到:https://github.com/ststeiger/jwtnet20/tree/master/corejwt
它不使用任何数据库,这不是JWT libary的工作。
获取和设置数据库数据是您的工作。
该库允许使用IANA JOSE分配中列出的JWT RFC中指定的所有算法在.NET Core中进行JWT授权和验证。
至于向管道添加授权和向路由添加值--这是两件应该分开做的事情,我认为您最好自己做。
您还可以在github的auth workshop或social login部分或本channel 9视频教程中了解更多信息。
如果所有其他内容都失败,则ASP.NET security的源代码在github上。
.NET3.5的原始项目是我的库的来源,在这里:
https://github.com/jwt-dotnet/jwt
我删除了对LINQ+扩展方法的所有引用,因为它们在.NET2.0中不受支持。如果在sourcecode中包含LINQ或ExtensionAttribute,则无法在不得到警告的情况下更改.NET运行库;这就是为什么我已经把它们完全移除了。
此外,我还添加了RSA+ECSD JWS-methods,因此CoreJWT-project依赖于BouncyCastle。
如果您将自身限制为HMAC-SHA256+HMAC-SHA384+HMAC-SHA512,则可以删除BouncyCastle。
目前尚不支持JWE。
用法与jwt-dotnet/JWT一样,只是我将名称空间JWT更改为corejwt。
我还添加了PetaJSON的内部副本作为序列化器,因此不会干扰其他人的项目的依赖项。
创建jwt-token:
var payload = new Dictionary<string, object>()
{
{ "claim1", 0 },
{ "claim2", "claim2-value" }
};
var secretKey = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
string token = JWT.JsonWebToken.Encode(payload, secretKey, JWT.JwtHashAlgorithm.HS256);
Console.WriteLine(token);
var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjbGFpbTEiOjAsImNsYWltMiI6ImNsYWltMi12YWx1ZSJ9.8pwBI_HtXqI3UgQHQ_rDRnSQRxFL1SR8fbQoS-5kM5s";
var secretKey = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
try
{
string jsonPayload = JWT.JsonWebToken.Decode(token, secretKey);
Console.WriteLine(jsonPayload);
}
catch (JWT.SignatureVerificationException)
{
Console.WriteLine("Invalid token!");
}
namespace BouncyJWT
{
public class JwtKey
{
public byte[] MacKeyBytes;
public Org.BouncyCastle.Crypto.AsymmetricKeyParameter RsaPrivateKey;
public Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters EcPrivateKey;
public string MacKey
{
get { return System.Text.Encoding.UTF8.GetString(this.MacKeyBytes); }
set { this.MacKeyBytes = System.Text.Encoding.UTF8.GetBytes(value); }
}
public JwtKey()
{ }
public JwtKey(string macKey)
{
this.MacKey = macKey;
}
public JwtKey(byte[] macKey)
{
this.MacKeyBytes = macKey;
}
public JwtKey(Org.BouncyCastle.Crypto.AsymmetricKeyParameter rsaPrivateKey)
{
this.RsaPrivateKey = rsaPrivateKey;
}
public JwtKey(Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters ecPrivateKey)
{
this.EcPrivateKey = ecPrivateKey;
}
}
}
我正在开发一个具有自己的身份验证和授权机制的REST应用程序。我想使用JSON Web Tokens进行身份验证。以下是有效且安全的实现吗? < li >将开发一个REST API来接受用户名和密码并进行认证。要使用的HTTP方法是POST,因此没有缓存。此外,在传输时还会有安全SSL < li >在认证时,将创建两个JWTs访问令牌和刷新令牌。刷新令牌将具有更长的有效期。这两个令牌都将写入coo
在auth-routes示例中,api和nuxt一起启动并使用一个Node.js服务器实例。但是,有时我们应该使用jsonWebToken处理外部api身份验证问题。在这个例子中,将用最简单的方式解释。 官方 auth-module 如果要实现复杂的身份验证流程,例如OAuth2,我们建议使用官方 auth-module 结构 由于Nuxt.js同时提供服务器和客户端呈现,并且浏览器的cookie
我试图在我的Web API应用程序中支持JWT承载令牌(JSON Web令牌),但我迷路了。 我看到了对.NET核心和OWIN应用程序的支持。 我当前正在IIS中托管我的应用程序。 我如何在我的应用程序中实现这个身份验证模块?是否有任何方法可以使用配置,与使用Forms/Windows身份验证的方法类似?
我正在使用SpringBoot开发具有微服务架构的Rest Backend。为了保护endpoint,我使用了JWT令牌机制。我正在使用Zuul API网关。 如果请求需要权限(来自JWT的角色),它将被转发到正确的微服务。Zuul api网关的“WebSecurityConfigrerAdapter”如下。 这样,我必须在这个类中编写每个请求授权部分。因此,我希望使用方法级安全性,即“Enabl
我必须说,我对整个模型非常困惑,我需要帮助把所有的浮动件粘在一起。 我不是在做Spring REST,只是简单的WebMVC控制器。 什么让人困惑?(错误之处请指正) 第三方身份验证 要针对第三方进行身份验证,我需要通过扩展AuthenticationProvider来拥有自定义提供程序 null 问题: 何时调用AbstractAuthenticationProcessingFilter#Suc
我正在使用Spring Boot编写Restful APIendpoint。我想创建登录/注销功能。我不想使用Spring boot默认登录页面。 根据我的理解,一个简单而安全的方法是: 客户端为服务器提供用户名和密码 服务器会发回身份验证代码,用户可以使用该代码对APIendpoint进行后续调用 身份验证码在用户注销/经过一定时间后才有效