• asp.net core认证与授权:Bearer验证与JWT


    1、Bearer认证概念

    Bearer验证也属于HTTP协议标准验证。

    Bearer验证中的凭证称为BEARER_TOKEN,或者是access_token,它的颁发和验证完全由我们自己的应用程序来控制,而不依赖于系统和Web服务器,Bearer验证的标准请求方式如下:

    Authorization: Bearer [BEARER_TOKEN] 
    

    那么使用Bearer验证的好处:

    • CORS: cookies + CORS 并不能跨不同的域名。而Bearer验证在任何域名下都可以使用HTTP header头部来传输用户信息。

    • 对移动端友好: 当你在一个原生平台(iOS, Android, WindowsPhone等)时,使用Cookie验证并不是一个好主意,因为你得和Cookie容器打交道,而使用Bearer验证则简单的多。

    • CSRF: 因为Bearer验证不再依赖于cookies, 也就避免了跨站请求攻击。

    • 标准:在Cookie认证中,用户未登录时,返回一个302到登录页面,这在非浏览器情况下很难处理,而Bearer验证则返回的是标准的401 challenge

    2、JWT(Json web token)

    上面介绍的Bearer认证,其核心便是BEARER_TOKEN,而最流行的Token编码方式便是:JSON WEB TOKEN。

    头部(Header)

    Header 一般由两个部分组成:

    • alg
    • typ

    alg是是所使用的hash算法,如:HMAC SHA256或RSA,typ是Token的类型,在这里就是:JWT。

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

    然后使用Base64Url编码成第一部分:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.<second part>.<third part>
    

    载荷(Payload)

    这一部分是JWT主要的信息存储部分,其中包含了许多种的声明(claims)。

    Claims的实体一般包含用户和一些元数据,这些claims分成三种类型:

    • reserved claims:预定义的 一些声明,并不是强制的但是推荐,它们包括 iss (issuer), exp (expiration time), sub (subject),aud(audience) 等(这里都使用三个字母的原因是保证 JWT 的紧凑)。

    • public claims: 公有声明,这个部分可以随便定义,但是要注意和 IANA JSON Web Token 冲突。

    • private claims: 私有声明,这个部分是共享被认定信息中自定义部分。

    一个简单的Pyload可以是这样子的:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
    }
    

    这部分同样使用Base64Url编码成第二部分:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.<third part>
    

    签名(Signature)

    Signature是用来验证发送者的JWT的同时也能确保在期间不被篡改。

    在创建该部分时候你应该已经有了编码后的Header和Payload,然后使用保存在服务端的秘钥对其签名,一个完整的JWT如下:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
    

    因此使用JWT具有如下好处:

    • 通用:因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。

    • 紧凑:JWT的构成非常简单,字节占用很小,可以通过 GET、POST 等放在 HTTP 的 header 中,非常便于传输。

    • 扩展:JWT是自我包涵的,包含了必要的所有信息,不需要在服务端保存会话信息, 非常易于应用的扩展。

    关于更多JWT的介绍,网上非常多,这里就不再多做介绍。下面,演示一下 ASP.NET Core 中 JwtBearer 认证的使用方式。

    DEMO

    1、添加jwt包引用

    dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 2.0.0
    

     2、Startup类中添加如下配置

    services.AddAuthentication(x =>
                {
                    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).AddJwtBearer(o =>
                {
                    o.TokenValidationParameters = new TokenValidationParameters
                    {
                        NameClaimType = JwtClaimTypes.Name,
                        RoleClaimType = JwtClaimTypes.Role,
    
                        ValidIssuer = "http://localhost:5200",
                        ValidAudience = "api",
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Consts.Secret))
    
                        /***********************************TokenValidationParameters的参数默认值***********************************/
                        // RequireSignedTokens = true,
                        // SaveSigninToken = false,
                        // ValidateActor = false,
                        // 将下面两个参数设置为false,可以不验证Issuer和Audience,但是不建议这样做。
                        // ValidateAudience = true,
                        // ValidateIssuer = true, 
                        // ValidateIssuerSigningKey = false,
                        // 是否要求Token的Claims中必须包含Expires
                        // RequireExpirationTime = true,
                        // 允许的服务器时间偏移量
                        // ClockSkew = TimeSpan.FromSeconds(300),
                        // 是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                        // ValidateLifetime = true
                    };
                    o.Events = new JwtBearerEvents() {
                        OnMessageReceived = context => {
                            //支持通过url传token
                           context.Token =  context.Request.Query["access_token"];
                            return Task.CompletedTask;
                        }
                    };
                });
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                    app.UseSwagger();
                    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWTDemo v1"));
                }
    
                app.UseHttpsRedirection();
    
                app.UseRouting();
    
                app.UseAuthentication();
    
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }

    3、增加生成token的action

    [HttpPost("authenticate")]
            public IActionResult Authenticate([FromBody] UserDto userDto)
            {
                //验证
                var user = _store.FindUser(userDto.UserName, userDto.Password);
                if (user == null) return Unauthorized();
    
                //JWT载荷(Payload)
                var key = Encoding.ASCII.GetBytes(Consts.Secret);
                var authTime = DateTime.UtcNow;
                var expiresAt = authTime.AddDays(7);
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    //内容
                    Subject = new ClaimsIdentity(new Claim[]
                    {
                        new Claim(JwtClaimTypes.Audience,"api"),
                        new Claim(JwtClaimTypes.Issuer,"http://localhost:5200"),
                        new Claim(JwtClaimTypes.Id, user.Id.ToString()),
                        new Claim(JwtClaimTypes.Name, user.UserName),
                        new Claim(JwtClaimTypes.Email, user.Email),
                        new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber)
                    }),
                    //过期时间
                    Expires = expiresAt,
                    //签证
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                };
    
                var tokenHandler = new JwtSecurityTokenHandler();
                var token = tokenHandler.CreateToken(tokenDescriptor);
                var tokenString = tokenHandler.WriteToken(token);
                return Ok(new
                {
                    access_token = tokenString,
                    token_type = "Bearer",
                    profile = new
                    {
                        sid = user.Id,
                        name = user.UserName,
                        auth_time = new DateTimeOffset(authTime).ToUnixTimeSeconds(),
                        expires_at = new DateTimeOffset(expiresAt).ToUnixTimeSeconds()
                    }
                });
            }

    4、添加受保护资源

    5、运行测试

    5.1直接访问WeatherForecast接口,会返回401。

    5.2先访问Authenticate?username=a&pwd=123获取token.

     

    5.3带上token重新请求WeatherForecast,请求成功。

     测试代码地址:https://gitee.com/xiaoqingyao/authentication-netcore

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

  • 相关阅读:
    Codeforces Round #364
    HDU5727 Necklace
    bzoj4578: [Usaco2016 OPen]Splitting the Field
    Codeforces Round #363 (Div. 1) C. LRU
    BestCoder Round #84
    2014 Multi-University Training Contest 2
    php中 new self 和 new static的区别
    浏览器带着cookie去访问服务器,取出的数据为空
    左边用0填充补齐
    centos7修改密码
  • 原文地址:https://www.cnblogs.com/chenxizhaolu/p/15473230.html
Copyright © 2020-2023  润新知