简体   繁体   English

hash 和使用 IPasswordHasher 时的盐问题<user>和 BCrypt 算法</user>

[英]hash and salt problem when using IPasswordHasher<User> and BCrypt algorhitm

I faced problem with hashing, salting and verifying password in ASP.NET.我在 ASP.NET 中遇到了散列、加盐和验证密码的问题。 I am creating a new User and then using hashing method.我正在创建一个新用户,然后使用散列方法。 But when I try to get some resources which requires Authorization and I enter the same username and password as I saved in database the result is failed.但是,当我尝试获取一些需要授权的资源并输入与保存在数据库中相同的用户名和密码时,结果失败。

Here is my password hasher class:这是我的密码哈希 class:

using Microsoft.AspNetCore.Identity;

namespace FlowerShop.ApplicationServices.Components.PasswordHasher
{
    public class BCryptPasswordHasher<User> : IPasswordHasher<User> where User : class
{
    
    public string HashPassword(User user, string password)
    {  
        return BCrypt.Net.BCrypt.HashPassword(password, 12);
    }

    public PasswordVerificationResult VerifyHashedPassword(User user, string hashedPassword, string providedPassword)
    {
        var isValid = BCrypt.Net.BCrypt.Verify(providedPassword, hashedPassword);

        if (isValid && BCrypt.Net.BCrypt.PasswordNeedsRehash(hashedPassword, 12))
        {
            return PasswordVerificationResult.SuccessRehashNeeded;
        }

        return isValid ? PasswordVerificationResult.Success : PasswordVerificationResult.Failed;
    }
}

This is my authentication class:这是我的身份验证 class:

public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly IQueryExecutor queryExecutor;
    private readonly IPasswordHasher<User> passwordHasher;

    public BasicAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, 
        ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, 
        IQueryExecutor queryExecutor, IPasswordHasher<User> passwordHasher)
        : base(options, logger, encoder, clock)
    {
        this.queryExecutor = queryExecutor;
        this.passwordHasher = passwordHasher;
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var endpoint = Context.GetEndpoint();
        if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
        {
            return AuthenticateResult.NoResult();
        }

        if (!Request.Headers.ContainsKey("Authorization"))
        {
            return AuthenticateResult.Fail("Missing Authorization Header");
        }

        User user = null;

        try
        {
            var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
            var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
            var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
            var username = credentials[0];
            var providedPassword = passwordHasher.HashPassword(user, credentials[1]);

            var query = new GetUserQuery()
            {
                UserName = username
            };
            user = await this.queryExecutor.Execute(query);
                           
            if (user == null || passwordHasher.VerifyHashedPassword(user, user.PasswordHash, providedPassword) 
                == PasswordVerificationResult.Failed)
            {
                return AuthenticateResult.Fail("Invalid Authorization Header");
            }
        }
        catch
        {
            return AuthenticateResult.Fail("Invalid Authorization Header");
        }

        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.UserName),
            new Claim(ClaimTypes.Role, user.Role.ToString()),
            new Claim(ClaimTypes.Email, user.Email),
        };
        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);
        
        return AuthenticateResult.Success(ticket);
    }
}

And in this place I am creating a new User:在这个地方我正在创建一个新用户:

        using MediatR;
        using Microsoft.AspNetCore.Identity;
        using System.Threading;
        using System.Threading.Tasks;

        public class AddUserHandler : IRequestHandler<AddUserRequest,
    AddUserResponse>
        {
        private readonly ICommandExecutor commandExecutor;
        private readonly IQueryExecutor queryExecutor;
        private readonly IMapper mapper;
        private readonly IPasswordHasher<User> passwordHasher;

            public AddUserHandler(ICommandExecutor commandExecutor,
    IQueryExecutor queryExecutor, 
                IMapper mapper, IPasswordHasher<User> passwordHasher)
            {
                this.commandExecutor = commandExecutor;
                this.queryExecutor = queryExecutor;
                this.mapper = mapper;
                this.passwordHasher = passwordHasher;
            }

            public async Task<AddUserResponse> Handle(AddUserRequest
    request, CancellationToken cancellationToken)
            {
            var query = new GetUserQuery()
            {                
                UserName = request.UserName,
                Email = request.Email
            };

            var getUser = await this.queryExecutor.Execute(query);
            if (getUser != null)
            {
                if (getUser.UserName == request.UserName)      

              {
                        return new AddUserResponse()
                        {
                            Error = new ErrorModel(ErrorType.ValidationError +
    "! The name is already taken.")
                        };
                    }
                if (getUser.Email == request.Email)
                {
         

               return new AddUserResponse()
                        {
                            Error = new ErrorModel(ErrorType.ValidationError +
    "! Email address is in use.")
                        };
                    }
                return new AddUserResponse()
                {
                    Error = new ErrorModel(ErrorType.Conflict)
                };
            }

            request.PasswordHash = passwordHasher.HashPassword(getUser,
request.Password);

            var user = this.mapper.Map<User>(request);
            var command = new AddUserCommand() 
            { 
                Parameter = user 
            };
            var addedUser = await this.commandExecutor.Execute(command);
            var response = new AddUserResponse()
            {
                Data =
this.mapper.Map<Domain.Models.UserDTO>(addedUser)
            };

            return response;
        }
    }

This is my Startup.cs:这是我的 Startup.cs:

      public void ConfigureServices(IServiceCollection services)
            {
                services.AddAuthentication("BasicAuthentication")
                        .AddScheme<AuthenticationSchemeOptions,
    BasicAuthenticationHandler>("BasicAuthentication", null);
    
                services.AddScoped<IPasswordHasher<User>,
    BCryptPasswordHasher<User>>();
}

Maybe first of all, is it all correct implemented?也许首先,这一切都正确实施了吗?

  1. Is hash in AddUserHandler correct assigned to request.PasswordHash? AddUserHandler 中的 hash 是否正确分配给 request.PasswordHash?
  2. How to retrieve salt and assign to request.PasswordSalt?如何检索盐并分配给request.PasswordSalt? Sorry for any unclear things if they occur.如果发生任何不清楚的事情,我们深表歉意。

Any feedback and help will be appreciated.任何反馈和帮助将不胜感激。

Thanks in advance:)提前致谢:)

Edit:编辑:

for example if I add user with password "pass123" and it is stored in database as 'user.PasswordHash = "$2a$12$Iqpy7FyQh/pt2O8upTtG5eOQKzo1V395wRNdAXPpp5Qf.NQ.KxUyy"' and provided password after hashing is 'providedPassword = "$2a$12$9vSz8Sw/WtmqGY6jyDiTleN/btZ0wXJkXdoB3sDpANVIIDGBpaqT."'例如,如果我添加密码为“pass123”的用户,并将其存储在数据库中为 'user.PasswordHash = "$2a$12$Iqpy7FyQh/pt2O8upTtG5eOQKzo1V395wRNdAXPpp5Qf.NQ.KxUyy"' 并且在散列后提供的密码是 'providedPassword = "$2a$12 $9vSz8Sw/WtmqGY6jyDiTleN/btZ0wXJkXdoB3sDpANVIIDGBpaqT。"'

I fixed the bug if anyone needs to use it in thee future.如果将来有人需要使用它,我修复了这个错误。 The problem was in my authentication class.问题出在我的身份验证 class 中。 In place of:代替:

var username = credentials[0];
var providedPassword = passwordHasher.HashPassword(user, credentials[1]);

Should be:应该:

var username = credentials[0];
var providedPassword = credentials[1];

I am sure that I have checked it a few times but somehow didn't work then.我确信我已经检查了几次,但不知何故没有工作。 Anyway, it finally works properly.无论如何,它终于可以正常工作了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么要散列(不使用盐)随机数? - Why hash (without using a salt) random numbers? 用户类上的身份安全标记是否减少了存储哈希盐的需要? - Does the Identity `SecurityStamp` on a `User` class lessen the need to store the hash salt? 使用Rijndael加密时的金钥,盐和IV - Key, salt and IV when using Rijndael encryption 如何将用户提供的登录凭据与数据库中存储的盐和哈希值进行比较,以允许用户登录 - How to compare the login credentials given by the user to the salt and hash stored in your database to permit user login c#sha256通过使用用户名作为盐计算密码哈希 - c# sha256 compute password hash by using username as salt BCrypt 在给定相同的盐、字符串和因子的情况下生成不同的哈希 - BCrypt generating different hashes given the same salt, string, and factor 使用Python对ASP.NET角色/成员资格创建的DB中的原始用户名,哈希,salt进行身份验证 - Using Python to authenticate against raw username, hash, salt in DB created by ASP.NET roles/membership 无法将哈希和盐密码与数据库值匹配 - Unable to match hash and salt passwords with database values 如何使用自定义IPasswordHasher? - How use custom IPasswordHasher? 如何使用C#中的PBKDF2 HMAC SHA-256或SHA-512在salt和迭代中散列密码? - How can I hash passwords with salt and iterations using PBKDF2 HMAC SHA-256 or SHA-512 in C#?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM