Asp.Net Core WebApi 身份验证、注册、用户管理


用了两天的时间研究了在Asp.Net WebApi 项目中如何实现注册、登录、身份验证功能。此项目使用JWT(Json Web Token)来进行身份验证。

用户服务

ASP.NET Core用户服务负责与用户身份验证,注册和管理相关的所有数据库交互和核心业务逻辑。

该文件的顶部包含一个定义用户服务的接口,下面是实现该接口的具体用户服务类。该类的底部包含一些私有方法,用于创建和验证存储在数据库中的散列密码。

  1. 通过Emain和Password来登录服务,Authenticate方法调用VerifyPasswordHash来验证Email和密码是否正确来进行登录授权。
public User Authenticate(string email, string password)
        {
            if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
                return null;

            var user = _iUserRepository.GetUserByEmail(email);

            // check if username exists
            if (user == null)
                return null;

            // check if password is correct
            if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt))
                return null;

            // authentication successful
            return user;
        }


private static bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt)
        {
            if (password == null) throw new ArgumentNullException(nameof(password));
            if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");
            if (storedHash.Length != 64) throw new ArgumentException("Invalid length of password hash (64 bytes expected).", "passwordHash");
            if (storedSalt.Length != 128) throw new ArgumentException("Invalid length of password salt (128 bytes expected).", "passwordHash");

            using (var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt))
            {
                var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
                for (int i = 0; i < computedHash.Length; i++)
                {
                    if (computedHash[i] != storedHash[i]) return false;
                }
            }

            return true;
        }

  1. 用户注册时需要创建新用户并将用户数据存放到数据库中
public User Create(User user, string password)
       {
           // validation
           if (string.IsNullOrWhiteSpace(password))
               throw new AppException("Password is required");

           if (_iUserRepository.Any(x => x.Email == user.Email))
               throw new AppException("Email \"" + user.Email + "\" is already taken");

           if (_iUserRepository.Any(x => x.UserName == user.UserName))
               throw new AppException("Username \"" + user.UserName + "\" is already taken");

           CreatePasswordHash(password, out var passwordHash, out var passwordSalt);

           user.PasswordHash = passwordHash;
           user.PasswordSalt = passwordSalt;

           _iUserRepository.Add(user);

           return user;
       }

对密码进行加密存储

private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
       {
           if (password == null) throw new ArgumentNullException(nameof(password));
           if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");

           using (var hmac = new System.Security.Cryptography.HMACSHA512())
           {
               passwordSalt = hmac.Key;
               passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
           }
       }
  1. 用户信息更新及删除代码
public void Update(User userParam, string password = null)
       {
           var user = _iUserRepository.GetById(userParam.Id);

           if (user == null)
               throw new AppException("User not found");

           if (userParam.UserName != user.UserName)
           {
               // username has changed so check if the new username is already taken
               if (_iUserRepository.Any(x => x.UserName == userParam.UserName))
                   throw new AppException("Username " + userParam.UserName + " is already taken");
           }

           // update user properties
           user.FirstName = userParam.FirstName;
           user.LastName = userParam.LastName;
           user.UserName = userParam.UserName;

           // update password if it was entered
           if (!string.IsNullOrWhiteSpace(password))
           {
               CreatePasswordHash(password, out var passwordHash, out var passwordSalt);

               user.PasswordHash = passwordHash;
               user.PasswordSalt = passwordSalt;
           }

           _iUserRepository.Update(user);
       }

       public void Delete(string id)
       {
           var user = _iUserRepository.GetById(id);
           if (user != null)
           {
               _iUserRepository.Remove(user);
           }
       }

用户实体、用户模型及数据上下文

public class User
    {
        public string Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string UserName { get; set; }
        public string Email { get; set; }
        public byte[] PasswordHash { get; set; }
        public byte[] PasswordSalt { get; set; }
    }
public class UserModel
    {
        public string Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string UserName { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
    }
public class VueContext : DbContext
    {
        public VueContext(DbContextOptions<VueContext> options)
            : base(options)
        {
        }

        public DbSet<User> User { get; set; }
    }

应用程序设置文件

/appsettings.json

{
  "AppSettings": {
    "Secret": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "VueDatabase": "serverName;Database=VueDb;Trusted_Connection=True;"
  }
}

在Startup.cs中配置身份验证

添加以下代码到ConfigureServices方法中以添加身份验证服务

// configure strongly typed settings objects
            var appSettingsSection = Configuration.GetSection("AppSettings");
            services.Configure<AppSettings>(appSettingsSection);

            // configure jwt authentication
            var appSettings = appSettingsSection.Get<AppSettings>();
            var key = Encoding.ASCII.GetBytes(appSettings.Secret);
            services.AddAuthentication(x =>
                {
                    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(x =>
                {
                    x.Events = new JwtBearerEvents
                    {
                        OnTokenValidated = context =>
                        {
                            var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
                            var userId = context.Principal.Identity.Name;
                            var user = userService.GetById(userId);
                            if (user == null)
                            {
                                // return unauthorized if user no longer exists
                                context.Fail("Unauthorized");
                            }
                            return Task.CompletedTask;
                        }
                    };
                    x.RequireHttpsMetadata = false;
                    x.SaveToken = true;
                    x.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(key),
                        ValidateIssuer = false,
                        ValidateAudience = false
                    };
                });

对应用配置身份验证,添加以下代码到Configure中

app.UseAuthentication();

核心代码大概就这些。最近看了微软官方文档,但是并没有发现详细教程,最终发现一篇博客,提取核心内容搬运记录下来。

参考文献

http://jasonwatmore.com/post/2018/06/26/aspnet-core-21-simple-api-for-authentication-registration-and-user-management#user-service-cs

https://neilq.github.io/2018/07/14/secure-your-asp-net-core-2-1-api-1-issuing-a-JWT/

https://www.cnblogs.com/RainingNight/p/jwtbearer-authentication-in-asp-net-core.html

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页