有没有什么方法可以在 asp.net 核心应用程序中进行令牌基础身份验证而无需多余的东西?

Is there any way to do token base auth in asp.net core application without redundant stuffs?

任何人都有一个很好的例子来在 asp.net 核心中进行基于令牌的授权,而没有像 IdentityContext 和其他垃圾这样的废话?我只想设置令牌生成设置,以便我的系统能够以正确的方式生成和检查令牌,我想自己管理身份验证过程。谢谢

已使用本文 go.microsoft.com/fwlink/?linkid=84547:

中的解决方案
public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(o =>
        {
            o.Authority = Configuration["AuthOptions:Authority"];
            o.RequireHttpsMetadata = false;
            o.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidIssuer = Configuration["AuthOptions:Issuer"],
                ValidateAudience = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AuthOptions:Key"])),
                ValidateLifetime = true,
            };
        });
        services.AddMvc();
        ConfigureDependincies(services);
    }


    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        var configuration = new SlackConfiguration
        {
            WebhookUrl = new Uri("https://hooks.slack.com/services/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"),
            MinLevel = LogLevel.Warning
        };

        loggerFactory.AddSlack(configuration, env);
        //  loggerFactory.AddDebug();
        app.UseDefaultFiles();
        app.UseDeveloperExceptionPage();
        app.UseAuthentication();
        //app.UseJwtBearerAuthentication(new JwtBearerOptions()
        //{
        //  AutomaticAuthenticate = true,
        //  AutomaticChallenge = true,
        //  RequireHttpsMetadata = false,
        //  TokenValidationParameters = new TokenValidationParameters()
        //  {

        //      ValidIssuer = Configuration["AuthOptions:Issuer"],
        //      ValidateAudience = false,
        //      ValidateIssuerSigningKey = true,
        //      IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["AuthOptions:Key"])),
        //      ValidateLifetime = true,
        //  }
        //});
        app.UseMvc();
    }


        var token = new JwtSecurityToken(
                 issuer: _root["AuthOptions:Issuer"],
                 notBefore: DateTime.UtcNow,
                 claims: identity.Claims,
                 expires: DateTime.UtcNow.Add(TimeSpan.FromMinutes(Convert.ToDouble(_root["AuthOptions:TokenLifeTime"]))),
                 signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_root["AuthOptions:Key"])), SecurityAlgorithms.HmacSha512)
         );
        return token;

它对我有用。

可以通过以下方式完成

  1. 有一个令牌生成器 API 端点(控制器)即 http://localhost/auth/token
  2. 在此,您通过验证身份验证用户生成令牌(根据商店检查用户)
  3. 可以通过在 ASP.NET 核心管道中提供身份验证模式来验证生成的令牌。 app.AddAuthentication()
  4. 对 API 的任何后续调用都应具有生成令牌的授权 header。

可以相应地改进此方法。

我曾经做过这件事。我厌倦了所有第三方的东西,所以我自己写了。

您想创建令牌并通过 api 提供/验证它们。

这是最初创建令牌的 api 控制器的示例。

[Route("api/[controller]")]
    public class TokenController : Controller
    {
        private readonly TokenCreatorOption _tco;
        private readonly CryptoHash _ch;

        public TokenController(IOptions<TokenCreatorOption> ioptTCO, IOptions<CryptoHash> ioptCH, IOptions<ConnectionStrings> ioptConn)
        {
            _tco = ioptTCO.Value;
            _ch = ioptCH.Value;
        }


        [HttpPost("")]
        public async Task<IActionResult> IssueToken([FromBody] CredentialUser model)
        {
            ///if model is null, this is an incorrect format
            if(model == null)
            {
                return BadRequest();
            }

            var user = GetUserFromDatabaseOrStore(model.userName, model.passWord);

            if(user == null)
            {
                return NotFound();
            }

            TokenCreatorOption newTCO = _tco; ///get your initial instantiation of the TokenCreatorOption. This is set to default values based off appsettings or in configure services

            newTCO.UserObject = user;
            newTCO.Expiration = DateTime.UtcNow.AddMinutes(30).ToString("yyyy-MM-dd hh:mm:ss.ss tt");

            ///anything within the TokenCreatorOption will be hashed, anything in the token Provider is not going to be hashed (not secured), but acts as a good object to store just general things that are needed on client side.
            TokenProvider _tpo = new TokenProvider();
            _tpo.tco = TokenInteraction.CreateToken(newTCO, _ch.salt);
            _tpo.listApp = xapp; ///put anything you wouldn't want to be hashed and claimed against outside of the object. so you always validate things inside the tco, but never exclude anything inside tco. This was a fatal flaw in tokens in the past.

            ///this is using messagepack to serialize, to make it smaller since this is going to be passed between every request/response. Consider zipping as well if large enough.
            var serializer = MessagePackSerializer.Get<TokenProvider>();
            byte[] obj = null;

            using (var byteStream = new MemoryStream())
            {
                serializer.Pack(byteStream, _tpo);
                obj = byteStream.ToArray();
            }

            return File(obj, "application/octet-stream");
        }

TokenCreatorOption Class

public class TokenCreatorOption
    {
        public string Issuer { get; set; }

        public UserFromThatDatabaseOrStore UserObject { get; set; }

        public string Expiration { get; set; }

        public string HashValue { get; set; }
    }

请注意,TokenCreatorOption 中的所有这些对象都是声明。每一个都在哈希函数中检查。

这里是Token Creator和Token Validator,一旦一个token有效,你就可以补发一个新的。

TokenInteraction

public static class TokenInteraction
    {
        public static TokenCreatorOption CreateToken(TokenCreatorOption _tco, byte[] salt)
        {
            byte[] exp = Encoding.UTF8.GetBytes(_tco.Expiration);
            byte[] issuer = Encoding.UTF8.GetBytes(_tco.Issuer);
            byte[] user = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_tco.UserObject));
            byte[] salty = salt;

            IEnumerable<byte> rv = exp.Concat(issuer).Concat(user).Concat(salty);
            HashAlgorithm alg = SHA512.Create();
            _tco.HashValue = Convert.ToBase64String(alg.ComputeHash(rv.ToArray()));

            return _tco;
        }

        public static bool ValidateToken(TokenCreatorOption _tco, byte[] salt)
        {
            byte[] exp = Encoding.UTF8.GetBytes(_tco.Expiration);
            byte[] issuer = Encoding.UTF8.GetBytes(_tco.Issuer);
            byte[] user = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_tco.UserObject));
            byte[] salty = salt;

            IEnumerable<byte> rv = exp.Concat(issuer).Concat(user).Concat(salty);
            HashAlgorithm alg = SHA512.Create();

            if (_tco.HashValue != Convert.ToBase64String(alg.ComputeHash(rv.ToArray())))
            {
                return false;
            }
            else
            {
                return true;
            }

        }

TokenInteraction中的注意添加到rv的字节顺序需要与我们验证令牌时的顺序相同。

现在我们可以拥有一个验证控制器。

[Route("api/[controller]")]
    public class ValidateController : Controller
    {
        private readonly TokenCreatorOption _tco;
        private readonly CryptoHash _ch;

        public ValidateController(IOptions<TokenCreatorOption> ioptTCO, IOptions<CryptoHash> ioptCH)
        {
            _tco = ioptTCO.Value;
            _ch = ioptCH.Value;
        }


        [HttpPost("")]
        public async Task<IActionResult> ValidateToken([FromBody] TokenCreatorOption model)
        {
            if (model == null)
            {
                return BadRequest("Model Cannot be Null");
            }

            ///Kick them right now if session is expired, so we don't have to do the full hashing.    
            if (DateTime.ParseExact(model.Expiration, "yyyy-MM-dd hh:mm:ss.ss tt", CultureInfo.InvariantCulture) < DateTime.UtcNow)
            {
                return BadRequest("Expired Datetime");
            }
            if(!TokenInteraction.ValidateToken(model, _ch.salt))
            {
                return Unauthorized();
            }

            model.Expiration = DateTime.UtcNow.AddMinutes(30).ToString("yyyy-MM-dd hh:mm:ss.ss tt");

            TokenProvider _tpo = new TokenProvider();
            _tpo.tco = TokenInteraction.CreateToken(model, _ch.salt);


            var serializer = MessagePackSerializer.Get<TokenProvider>();
            byte[] obj = null;

            using (var byteStream = new MemoryStream())
            {
                serializer.Pack(byteStream, _tpo);
                obj = byteStream.ToArray();
            }

            return File(obj, "application/octet-stream");
        }

当然还有在您最初注册服务时。通过证书或随机加密数字生成器创建您的盐值。

public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddCors();
            services.AddOptions();
            services.AddSwaggerGen();
            services.ConfigureSwaggerGen(options =>
            {
                options.SingleApiVersion(new Swashbuckle.Swagger.Model.Info
                {
                    Version = "v1"
                });


            services.Configure<TokenCreatorOption>(myopt =>
            {
                myopt.Issuer = "Issuer"; //either from appsettings or manually
                myopt.Expiration = null;
                myopt.UserObject = null;
                myopt.HashValue = "";
            });
            byte[] salty;
            new RNGCryptoServiceProvider().GetBytes(salty = new byte[64]);
            services.Configure<CryptoHash>(copt =>
            {
                copt.salt = (new Rfc2898DeriveBytes("Super!SecretKey!123456789!@#$", salty, 1000)).GetBytes(64);
            });

            services.AddSingleton<IConfiguration>(Configuration);