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);
            }
  • 相关阅读:
    JavaScript实现类的private、protected、public、static以及继承
    OSS网页上传和断点续传(STSToken篇)
    OSS网页上传和断点续传(OSS配置篇)
    Linq sum()时遇到NULL
    SQLSERVER事务日志已满 the transaction log for database 'xx' is full
    笔记本高分辨软件兼容问题,字体太小或模糊
    H5上传图片之canvas
    An error occurred while updating the entries. See the inner exception for details.
    无限级结构SQL查询所有的下级和所有的上级
    SQLserver 进程被死锁问题解决
  • 原文地址:https://www.cnblogs.com/zitjubiz/p/net_core_daily_18_JWT.html
Copyright © 2011-2022 走看看