Подтвердить что ты не робот

Простая аутентификация JWT в веб-интерфейсе ASP.NET Core 1.0

Я ищу простейший способ настроить сервер веб-API, который использует JWT для аутентификации в ASP.NET Core (aka ASP.NET 5). Этот проект (сообщение в блоге/github) делает точно что я ищу, но использует ASP.NET 4.

Я просто хочу иметь возможность:

  • настроить маршрут входа, который может создать токен JWT и вернуть его в заголовок. Я интегрирую это с существующей службой RESTful, которая сообщит мне, являются ли имя пользователя и пароль действительными. В проекте ASP.NET 4, на который я смотрю, это можно сделать с помощью следующего маршрута 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, OpenIddict и EF, которые я бы хотел избежать.

Может ли кто-нибудь указать мне пример того, как это сделать в ASP.NET Core или помочь мне начать с этого?

ИЗМЕНИТЬ: ответ Я решил использовать этот ответ: qaru.site/info/52128/...

4b9b3361

Ответ 1

Примечание/Обновление:

Ниже приведен код для .NET Core 1.1
Так как .NET Core 1 был настолько RTM, аутентификация изменилась с помощью перехода от .NET Core 1 к 2.0 (он был [частично?] Исправлен с нарушением изменений).

Вот почему следующий код НЕ работает с .NET Core 2.0.
Но это будет полезно прочитать.


Некромантия.
Я углубился в JWT. Вот мои выводы:

Вам нужно добавить Microsoft.AspNetCore.Authentication.JwtBearer

то вы можете установить

app.UseJwtBearerAuthentication(bearerOptions);

в Startup.cs = > Настроить

где определены значения bearerOptions, например. как

var bearerOptions = new JwtBearerOptions()
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = tokenValidationParameters,
    Events = new CustomBearerEvents()
};

// Optional 
// bearerOptions.SecurityTokenValidators.Clear();
// bearerOptions.SecurityTokenValidators.Add(new MyTokenHandler());

где CustomBearerEvents - это место, где вы могли бы добавлять данные токена в httpContext/Route

// 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);
    }
}

И tokenValidationParameters определяется вами, например.

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, 
};

И MyTokenHandler опционально определяется вами, если вы хотите настроить проверку маркера, например.

// 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();
    }


}

Сложная часть заключается в том, как получить AsymmetricSecurityKey, потому что вы не хотите передавать rsaCryptoServiceProvider, потому что вам нужна интероперабельность в криптовом формате.

Создание идет по линиям

// 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);

например. с BouncyCastle из сертификата DER:

    // 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;
    }

Впоследствии вы можете добавить настраиваемый формат файлов cookie, содержащий JWT-носитель:

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");
    }


}

И вы также можете создать JWT-токен с Microsoft.IdentityModel.Token:

// 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)
        };



    }

}

Обратите внимание, что, поскольку вы можете указать другого пользователя в файле cookie vs. http-headers (Bearer) или любом другом методе проверки подлинности, который вы указали, вы действительно можете иметь БОЛЬШЕ, чем 1 пользователь!


Взгляните на это:
https://stormpath.com/blog/token-authentication-asp-net-core

это должно быть именно то, что вы ищете.

Там также эти два:

https://goblincoding.com/2016/07/03/issuing-and-authenticating-jwt-tokens-in-asp-net-core-webapi-part-i/

https://goblincoding.com/2016/07/07/issuing-and-authenticating-jwt-tokens-in-asp-net-core-webapi-part-ii/

и этот

http://blog.novanet.no/hooking-up-asp-net-core-1-rc1-web-api-with-auth0-bearer-tokens/

И источники JWT-Bearer https://github.com/aspnet/Security/tree/master/src/Microsoft.AspNetCore.Authentication.JwtBearer


Если вам нужна сверхвысокая безопасность, вы должны защищать от повторных атак путем обновления билета по каждому запросу и аннулировать старые билеты после определенного таймаута и после выхода пользователя (не только после истечения срока действия).


Для тех из вас, кто попадает сюда через Google, вы можете реализовать TicketDataFormat в cookie-аутентификации, когда хотите использовать свою собственную версию JWT.

Мне пришлось заглянуть в JWT для работы, потому что нам нужно было обеспечить наше приложение.

Поскольку мне все еще приходилось использовать .NET 2.0, я должен был написать свою собственную библиотеку.

Я переносил результат этого в .NET Core в эти выходные. Вы найдете его здесь: https://github.com/ststeiger/Jwt_Net20/tree/master/CoreJWT

В нем не используется база данных, это не работа с библиотекой JWT.

Получение и настройка данных БД - это ваша работа.

Библиотека разрешает авторизацию и проверку JWT в .NET Core со всеми алгоритмами указанными в JWT RFC, указанном в назначении IANA JOSE.

Что касается добавления авторизации к конвейеру и добавления значений для маршрута - это две вещи, которые нужно делать отдельно, и я думаю, что вы лучше всего это сделаете сами.

Вы можете использовать пользовательскую аутентификацию в ASP.NET Core.

Посмотрите "Безопасность" категории документов на docs.asp.net.

Или вы можете заглянуть в промежуточное ПО cookie без идентификатора ASP.NET или в Пользовательская авторизация на основе политик.

Вы также можете узнать больше в семинаре auth по github или в социальный логин или в этот канал 9 видеоуроков.
Если все остальное не удается, исходный код безопасности asp.net в github.


Исходный проект для .NET 3.5, из которого происходит моя библиотека, находится здесь:
https://github.com/jwt-dotnet/jwt

Я удалил все ссылки на методы расширения LINQ +, потому что они не поддерживаются в .NET 2.0. Если вы включаете LINQ или ExtensionAttribute в исходный код, вы не можете просто изменить среду выполнения .NET, не получая предупреждений; поэтому я полностью их удалил.

Кроме того, я добавил RSA + ECSD JWS-методы, поэтому CoreJWT-проект зависит от BouncyCastle.

Если вы ограничиваете себя HMAC-SHA256 + HMAC-SHA384 + HMAC-SHA512, вы можете удалить BouncyCastle.

JWE не поддерживается (пока).

Использование подобно jwt-dotnet/jwt, , за исключением того, что я изменил пространство имен JWT на CoreJWT.

Я также добавил внутреннюю копию PetaJSON в качестве сериализатора, поэтому нет никаких помех в отношении зависимостей проектов других людей.

Создайте JWT-токен:

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);

Проверьте JWT-токен:

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!");
}

Для RSA и ECSA вам нужно будет передать секретный ключ RSA/ECSD (BouncyCastle) вместо secretKey.

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;
        }
    }


}

Как создавать/экспортировать/импортировать RSA/ECSD-ключи с помощью BouncyCastle, см. проект под названием "BouncyCastleTests" в том же хранилище. Я оставляю это вам, чтобы безопасно хранить и извлекать ваши собственные секретные ключи RSA/ECSD.

Я проверил результаты моей библиотеки для HMAC-ShaXXX и RSA-XXX с помощью JWT.io - похоже, что они в порядке.

ECSD тоже должен быть в порядке, но я ничего не тестировал. В любом случае, я не проводил обширных тестов, FYI.

Ответ 2

Самый простой вариант, который я нашел до сих пор, - OpenIddict. Вы говорите, что хотите избежать Entity Framework и OpenIddict - тогда вы будете делать много кодирования самостоятельно, эффективно переписывая части OpenIddict и ASOS ( которые использует OpenIddict), чтобы делать то, что они делают в любом случае.

Если вы используете OpenIddict, это практически вся конфигурация, которая вам понадобится ниже. Это очень просто.

Если вы не хотите использовать EF, это возможно с OpenIddict. Я не знаю, как это сделать, но вам придется разобраться.

ConfigureServices:

services.AddIdentity<ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders()
            .AddOpenIddictCore<Application>(config => config.UseEntityFramework()); // this line is for OpenIddict

Настройка

app.UseOpenIddictCore(builder =>
{
    // tell openiddict you're wanting to use jwt tokens
    builder.Options.UseJwtTokens();
    // NOTE: for dev consumption only! for live, this is not encouraged!
    builder.Options.AllowInsecureHttp = true;
    builder.Options.ApplicationCanDisplayErrors = true;
});

// use jwt bearer authentication
app.UseJwtBearerAuthentication(options =>
{
    options.AutomaticAuthenticate = true;
    options.AutomaticChallenge = true;
    options.RequireHttpsMetadata = false;
    // these urls must match the value sent in the payload posted from the client side during login
    options.Audience = "http://localhost:58292/";
    options.Authority = "http://localhost:58292/";
});

Есть одна или две другие незначительные вещи, например, ваш DbContext должен вытекать из OpenIddictContext<ApplicationUser, Application, ApplicationRole, string>.

В этом сообщении блога вы можете увидеть полное объяснение (включая ссылки на репозиторий github): http://capesean.co.za/blog/asp-net-5-jwt-tokens/

Ответ 3

Если вам нужна только аутентификация с внешним поставщиком OAuth/OpenID (например, Google, GitHub, Facebook, учетная запись Microsoft и т.д.), вам не нужны сторонние инструменты.

Поставщики аутентификации для наиболее часто используемых поставщиков OAuth и OpenID уже поставляются с ASP.NET Core в пакетах Microsoft.AspNetCore.Authorization.*. Проверьте образцы, представленные в репозитории GitHub в хранилище Security

Если вам нужно создать свои собственные токены JWT, вам нужен сервер OAuth/OpenID. OpenIddict - это простой в настройке сервер авторизации. Для этого вам нужна некоторая форма базы данных, так как внешние поставщики будут использоваться для аутентификации пользователя, но вам также понадобится иметь учетную запись на вашем сервере авторизации.

Если вам нужна дополнительная настройка и больший контроль над потоком, вы должны использовать ASOS или IdentityServer4 (в настоящее время поддерживается только в ASP.NET Core при работе с полной .NET Framework или Mono. Core runtime пока не поддерживается как я знаю.

Существует также Gitter Chatroom для OpenIddict на https://gitter.im/openiddict/core и https://gitter.im/aspnet-contrib/AspNet.Security.OpenIdConnect.Server для ASOS.

Ответ 4

Существует полный пример для ASP.NET Core + JWT Auth + SQL Server + Swagger: https://github.com/wilsonwu/netcoreauth

Надеюсь, это поможет вам.