zoukankan      html  css  js  c++  java
  • 旅游网项目的身份验证功能的实现

    Session:每个用户通过服务端的认证后,服务端都要做一次记录,便于下次请求的鉴别,这样就会增加服务器的内存负担;
    Session的扩展性: 所以每次请求,都要确保我们曾经认证的记录存储在我们当前要访问的服务器上,负载均衡就受到了很大的限制;

    什么是JWT?-> JSON WEB TOKEN

    JWT包含了三段信息
    header 头部 -> 类型,加密算法

    {
      'typ': 'JWT',   
      'alg': 'HS256'
    }
    

    payload 载荷

    iss: jwt签发者
    sub: jwt所面向的用户
    aud: 接收jwt的一方(Token的持有者)
    exp: jwt的过期时间,且大于签发时间
    nbf: 定义在什么时间之前,该jwt都是不可用的.
    iat: jwt的签发时间
    jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

    公共的声明:
    公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

    signature 签证

    header (base64后的)+
    payload (base64后的)+
    secret

    下面我们正式开始创建数据库,配置等

    1.我们在模型中创建一个新的类:ApplicationUser.cs 需要继承自:IdentityUser

    这是在IdentityUser提供的属性不够满足我们项目的使用的情况下,否则我们直接使用IdentityUser即可
    下面的Virtual为添加的一些关联其他的表的属性

        public class ApplicationUser:IdentityUser
        {
            public string Address { get; set; }
    
            public virtual ICollection<IdentityUserRole<string>> UserRoles { get; set; }
            public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
            public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
            public virtual ICollection<IdentityUserToken<string>> Tokens{ get; set; }
    
    
        }
    

    2.我们需要在StartUp文件中添加服务
    这里一共添加了2个服务,第一个为创建相对应的数据库而添加的服务
    第二个为Jwt的验证配置服务,我们可以选择是否验证发布者,持有者,以及过期时间
    这其中所用的参数,我们是保存在Appsetting中的,使用—IConfiguration服务获取

                services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<AppDbContext>();
                services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                    .AddJwtBearer(option =>
                    {
                        var secretBytes = Encoding.UTF8.GetBytes(_Configuration["Authentication:SecretKey"]);
                        option.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                        {
                            ValidateIssuer = true,//验证发布者
                            ValidIssuer = _Configuration["Authentication:Issuer"],
    
                            ValidateAudience = true,//验证Token持有者
                            ValidAudience = _Configuration["Authentication:Audience"],
    
                            ValidateLifetime = true,//验证是否过期
                            IssuerSigningKey=new SymmetricSecurityKey(secretBytes)//传入私钥
                        };
                    });
    

    3.在管道中间添加验证授权中间件
    这对顺序是有要求的

                //我在哪
                app.UseRouting();
                //我是谁
                app.UseAuthentication();
                //我可以干什么
                app.UseAuthorization();
    

    4.在这里我们新建一个控制器(AuthenticationController)在这个控制器中进行注册登录验证等操作
    这里面包含了2个Action :Login 、register

    注入两个主要的服务:UserManager、SignInManager,分别为用户管理,和登录管理

    首先在registerAction中使用UserManager服务,创建一个新的用户,这个时候数据库创建了一个用户
    然后我们使用该用户的用户名和密码的Json格式作为HttpBody,传入LoginAction方法中
    New一个新的Claim里面包含了Sub,该Token的发布者
    随后返回一个Token,
    因为Jwt没有通过中间件进行验证所以对于要访问的方法前的标签应该为:
    [Authorize(AuthenticationSchemes ="Bearer")]
    [Authorize(Roles ="Admin")]

    之后我们每次访问需要验证授权的控制器方法的时候
    只需要在Http请求的Headers添加Authentication=bearer(这里有一个空格)+(Token,当然前提是Token有效)就可以访问相应的控制器方法了

     [Route("Auth")]
        public class AuthenticateController : ControllerBase
        {
            private readonly IConfiguration _configuration;
            private readonly UserManager<ApplicationUser> _userManager;
            private readonly SignInManager<ApplicationUser> _signInManager;
    
            public AuthenticateController(
                IConfiguration configuration,
                UserManager<ApplicationUser> userManager,
                SignInManager<ApplicationUser> signInManager)
            {
                _configuration = configuration;
                _userManager = userManager;
                _signInManager = signInManager;
            }
            [AllowAnonymous]
            [Route("Login")]
            [HttpPost]
            public async Task<IActionResult> Logi4n([FromBody] LoginDto loginDto)
            {
                //1.验证用户名密码
                
                var loginResult = await _signInManager.PasswordSignInAsync(loginDto.Email, loginDto.Password, false, false);
                if (!loginResult.Succeeded)
                {
                    return BadRequest("您所登录的账户不存在");
                }
    
                
               
                var user = await _userManager.FindByNameAsync(loginDto.Email);
                var signingAlgorithm = SecurityAlgorithms.HmacSha256;
                //2.创建jwt
                var claims = new List<Claim>
                {
                   // new Claim(ClaimTypes.Role,"Admin"),//添加角色
                    new Claim(JwtRegisteredClaimNames.Sub,user.Id)
            };
                var roleNames = await _userManager.GetRolesAsync(user);//从数据库中拿到User的角色
                foreach (var roleName in roleNames)//将该对象的所有Claim遍历存储到claims,在后面添加到token中一同返回
                {
                    var roleClaim = new Claim(ClaimTypes.Role,roleName);
                    claims.Add(roleClaim);
                }
                var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
                var signingKey = new SymmetricSecurityKey(secretByte);//非对称算法进行加密
                var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);
                //第三四个参数:表示多次登录是否锁定账户
                var token = new JwtSecurityToken(
                    issuer:_configuration["Authentication:Issuer"],//谁发布的
                    audience: _configuration["Authentication:Audience"],//发布给谁
                    claims,
                    notBefore:DateTime.UtcNow,//发布时间
                    expires:DateTime.UtcNow.AddDays(1),//有效时间
                    signingCredentials
                    );
    
                var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);//以string的格式输出token
                //3.return 200 ok+jwt
                return Ok(tokenStr);
            }
    
            [HttpPost("register")]
            [AllowAnonymous]
            public async Task<IActionResult> Register([FromBody] RegisterUserDto registerUserDto)
            {
                if (_userManager.FindByEmailAsync(registerUserDto.Email) != null)
                {
                    return BadRequest("用户名已经存在");
                }
                //1.使用用户名创建用户对象
                var user = new ApplicationUser()
                {
                    UserName = registerUserDto.Email,
                    Email = registerUserDto.Email
    
                };
                //2.hash 保存密码
                var result = await _userManager.CreateAsync(user, registerUserDto.Password);
                if (!result.Succeeded)
                {
                    return BadRequest("用户创建失败");
                }
                return Ok();
            }
        }
    

    我们还需再AppDbcontext中添加一些子数据(包括管理员,还有之前我们重写了IdentityUser的ApplicationUser类型中list table 的一些关系)

     public class AppDbContext:IdentityDbContext<ApplicationUser> 
        {
            public AppDbContext(DbContextOptions<AppDbContext> options):base(options)
            {
    
            }
            //指明哪些模型需要映射到数据库中
            public DbSet<TouristRoute> TouristRoutes { get; set; }//数据模型映射(每一张table都需要一个DbSet进行映射)
            public DbSet<TouristRoutePicture> TouristRoutePictures { get; set; }
    
            //添加种子数据
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                //从Json读取种子数据
              // var JsonData =  File.ReadAllText(@"C:UsersFlower-Lisource
    eposXiechengTravelXiechengTravelinDebug
    et5.0DatabaseTouristRouteData.json");
               var JsonData =  File.ReadAllText(Path.GetDirectoryName(Directory.GetCurrentDirectory()) + @"XiechengTravelDatabaseTouristRouteData.json");
                IList<TouristRoute> touristRoutes = JsonConvert.DeserializeObject<IList<TouristRoute>>(JsonData);//反序列化,将string=>object
    
                var jsonPicData =  File.ReadAllText(Path.GetDirectoryName(Directory.GetCurrentDirectory()) + @"XiechengTravelDatabaseTouristRoutePictureData.json");
                IList<TouristRoutePicture> touristRoutePictures = JsonConvert.DeserializeObject<IList<TouristRoutePicture>>(jsonPicData);
    
    
                // 初始化用户与角色的种子数据
                // 1. 更新用户与角色的外键关系
                modelBuilder.Entity<ApplicationUser>(b => {
                    b.HasMany(x => x.UserRoles)//有多个UserRoles
                    .WithOne()//设置为一对多的关系
                    .HasForeignKey(ur => ur.UserId)//UserId为外键
                    .IsRequired();//不能为空
                });
    
                // 2. 添加角色
                var adminRoleId = "308660dc-ae51-480f-824d-7dca6714c3e2"; // guid 
                modelBuilder.Entity<IdentityRole>().HasData(
                    new IdentityRole
                    {
                        Id = adminRoleId,
                        Name = "Admin",
                        NormalizedName = "Admin".ToUpper()
                    }
                );
    
                // 3. 添加用户
                var adminUserId = "90184155-dee0-40c9-bb1e-b5ed07afc04e";
                ApplicationUser adminUser = new ApplicationUser
                {
                    Id = adminUserId,
                    UserName = "admin@fakexiecheng.com",
                    NormalizedUserName = "admin@fakexiecheng.com".ToUpper(),
                    Email = "admin@fakexiecheng.com",
                    NormalizedEmail = "admin@fakexiecheng.com".ToUpper(),
                    TwoFactorEnabled = false,
                    EmailConfirmed = true,
                    PhoneNumber = "123456789",
                    PhoneNumberConfirmed = false
                };
    
                //加密密码
                PasswordHasher<ApplicationUser> ph = new PasswordHasher<ApplicationUser>();
                adminUser.PasswordHash = ph.HashPassword(adminUser, "Fake123$");
                modelBuilder.Entity<ApplicationUser>().HasData(adminUser);
    
                // 4. 给用户加入管理员权限
                // 通过使用 linking table:IdentityUserRole
                modelBuilder.Entity<IdentityUserRole<string>>()
                    .HasData(new IdentityUserRole<string>()
                    {
                        RoleId = adminRoleId,
                        UserId = adminUserId
                    });
    
                modelBuilder.Entity<TouristRoute>().HasData(touristRoutes);
                modelBuilder.Entity<TouristRoutePicture>().HasData(touristRoutePictures);
                base.OnModelCreating(modelBuilder);
    
            }
        }
    
  • 相关阅读:
    HIVE高级(14):优化(14) Hive On Spark配置
    HIVE高级(13):优化(13) Hive Job 优化
    HIVE高级(12):优化(12) 数据倾斜
    HIVE高级(11):优化(11) HQL 语法优化(2) 多表优化
    HIVE高级(10):优化(10) HQL 语法优化(1) 单表优化
    HIVE高级(9):优化(9) Hive 建表优化(1) 分区表/分桶表/合适的文件格式/合适的压缩格式
    HIVE高级(8):优化(8) Explain 查看执行计划(二)
    Hive基础(19):Hive 函数(2) 自定义函数/自定义 UDF 函数/自定义 UDTF 函数
    Hive基础(18):Hive语法(5) DDL(2) 分区表和分桶表
    MATLAB RGB2HSV、HSV2RGB
  • 原文地址:https://www.cnblogs.com/liflower/p/14724742.html
Copyright © 2011-2022 走看看