zoukankan      html  css  js  c++  java
  • 把旧系统迁移到.Net Core 2.0 日记 (18) --JWT 认证(Json Web Token)

    我们最常用的认证系统是Cookie认证,通常用一般需要人工登录的系统,用户访问授权范围的url时,会自动Redirect到Account/Login,登录后把认证结果存在cookie里。

    系统只要找到这个cookie就认为这个web用户是已经登录的了。

    通常的代码段是这样的,StartUp.cs

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    }).AddCookie();
    public async Task<IActionResult> Login(IFormCollection form)
            {
                string userName = form["txtLoginId"];
                string pwd = form["txtPwd"];
                if (Validate(userName, pwd))
                {
                    var claimsIdentity = new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Name, userName) }, "Basic");
                    var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
                    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal);
                    return Json(new { isSuccess = true, message = "登录成功" });
                }
    
    
    
            }

    如果我们用RestFul API写接口给前端或第三方程序使用时,这种重定向登录的方式就不合适了。

    最合适的方法是,前端访问一个Login接口,获得一个AccessToken,然后用这个Token去访问后端的资源, 后端验证这个token的正确性,就放行让前端访问。

    一开始比较笨的方法,是在每个接口方法都加一个accessToken的字段,这样不够优雅,可以把这个token放在Header里。这就是JWT认证

    JWT有什么好处?

    1、支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输.

    2、无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息.

    4、更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可.

    5、去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.

    6、更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。

    7、CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。

    8、性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多.

    9、不需要为登录页面做特殊处理

    10、基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库和多家公司的支持.

    JWT是用于验证而非加密,任何人即使没有密钥secret,header与payload中的数据都是可以获取的。
    使用 jwt.io,可以进行JWT的编码、解码和验证

    .net Core 使用Jwt认证的方法

    1. 在startup.cs 的configureService 方法里注册JWT Authetication,为Swagger增加JWT Auth支持
                // Register the Swagger generator, defining one or more Swagger documents
                services.AddSwaggerGen(c =>
                {
                    c.SwaggerDoc("v1", new Info { Title = "API", Version = "v1" });
                    //启用auth支持      
                    var security = new Dictionary<string, IEnumerable<string>> { { "Bearer", new string[] { } }, };
                    c.AddSecurityRequirement(security);
                    c.AddSecurityDefinition("Bearer", new ApiKeyScheme
                    { Description = "JWT Authorization header using the Bearer scheme. Example: "Authorization: Bearer {token}"",
                        Name = "Authorization",
                        In = "header",
                        Type = "apiKey"
                    });
    
                });
                services.AddAuthentication(options=> {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).AddJwtBearer(o =>
                {
                    o.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidIssuer = "Bearer",
                        ValidAudience = "wr",
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(JwtHelper.secretKey)),
                        RequireSignedTokens = true,
                        // 将下面两个参数设置为false,可以不验证Issuer和Audience,但是不建议这样做。
                        ValidateAudience = false,
                        ValidateIssuer = true,
                        ValidateIssuerSigningKey = true,
                        // 是否要求Token的Claims中必须包含 Expires
                        RequireExpirationTime = true,
                        // 是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
                        ValidateLifetime = true
                    };
                });
    
                services.AddAuthorization(options =>
                {
                    options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());
                    options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());
                    options.AddPolicy("AdminOrClient", policy => policy.RequireRole("Admin,Client").Build());
                });

    2. 在Configure方法里,useMVC的前面增加中间件,这样每次Web请求时,会先到 JwtTokenAuth 这里处理验证token

                app.UseMiddleware<JwtTokenAuth>();
                app.UseMvc();

    3. JwtHelper.cs 里包含颁发和验证token2个方法

            public static string IssueJWT(TokenModelJWT tokenModel)
            {
                var dateTime = DateTime.UtcNow;
                var claims = new Claim[]
                {
                    new Claim(JwtRegisteredClaimNames.Jti,tokenModel.Uid.ToString()),//Id
                    new Claim("Role", tokenModel.Role),//角色
                    new Claim(JwtRegisteredClaimNames.Iat,dateTime.ToString(),ClaimValueTypes.Integer64)
                };
                //秘钥
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtHelper.secretKey));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
                var jwt = new JwtSecurityToken(
                    issuer: "Bearer",
                    claims: claims, //声明集合
                    expires: dateTime.AddHours(2),
                    signingCredentials: creds);
    
                var jwtHandler = new JwtSecurityTokenHandler();
                var encodedJwt = jwtHandler.WriteToken(jwt);
    
                return encodedJwt;
            }
    public static TokenModelJWT SerializeJWT(string jwtStr)
            {
                var jwtHandler = new JwtSecurityTokenHandler();
                JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
                if (jwtToken.ValidTo < DateTime.UtcNow)
                    return null;
    
                object role = new object(); ;
                try
                {
                    jwtToken.Payload.TryGetValue("Role", out role);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
                var tm = new TokenModelJWT
                {
                    Uid = int.Parse(jwtToken.Id),
                    Role = role != null ? role.ToString() : "",
                };
                return tm;
            }

    4. JwtTokenAuth.cs 中间件验证token,并增加User到httpContext,表示已登录

            public Task Invoke(HttpContext httpContext)
            {
                //检测是否包含'Authorization'请求头
                if (!httpContext.Request.Headers.ContainsKey("Authorization"))
                {
                    return _next(httpContext);
                }
                var tokenHeader = httpContext.Request.Headers["Authorization"].ToString();
    
                TokenModelJWT tm = JwtHelper.SerializeJWT(tokenHeader);
                if (tm != null)
                {
                    //授权
                    var claimList = new List<Claim>();
                    var claim = new Claim(ClaimTypes.Role, tm.Role);
                    claimList.Add(claim);
                    var identity = new ClaimsIdentity(claimList);
                    var principal = new ClaimsPrincipal(identity);
                    httpContext.User = principal;
                }
    
    
                return _next(httpContext);
            }
  • 相关阅读:
    UICollectionView添加 HeaderView FooterView
    Swift-UIDynamic初见
    Swift归档
    通知NSNotication&通知中心NSNoticationCenter
    iOS沙盒
    lldb-320.4.152﹣Debugger commands:
    UIPickerView採摘控件的學習
    hive的优化
    zookeeper简易配置及hadoop高可用安装
    hadoop组件概念理解
  • 原文地址:https://www.cnblogs.com/zitjubiz/p/net_core_daily_18_JWT.html
Copyright © 2011-2022 走看看