我们最常用的认证系统是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(),
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);
}

把旧系统迁移到.Net Core 2.0 日记 (18) --JWT 认证(Json Web Token)的更多相关文章

  1. 把旧系统迁移到.Net Core 2.0 日记 (15) --Session 改用Redis

    安装Microsoft.Extensions.Caching.Redis.Core NuGet中搜索Microsoft.Extensions.Caching.Redis.Core并安装,此NuGet包 ...

  2. 把旧系统迁移到.Net Core 2.0 日记(1) - Startup.cs 解析

    因为自己到开发电脑转到Mac Air,之前的Webform/MVC应用在Mac 跑不起来,而且.Net Core 2.0 已经比较稳定了. 1. 为什么会有跨平台的.Net Core  近年来,我们已 ...

  3. 把旧系统迁移到.Net Core 2.0 日记 (12) --发布遇到的问题

    1. 开发时是在Mac+MySql, 尝试发布时是在SQL2005+Win 2008 (第一版) 在Startup.cs里,数据库连接要改,分页时netcore默认是用offset关键字分页, 如果用 ...

  4. 把旧系统迁移到.Net Core 2.0 日记 (17) --多租户和SoftDelete

    在EF Core 2.0版本中出现了全局过滤新特性即HasQueryFilter,它出现的意义在哪里?能够解决什么问题呢? 通过HasQueryFilter方法来创建过滤器能够允许我们对访问特定数据库 ...

  5. 把旧系统迁移到.Net Core 2.0 日记(10) -- EF core 和之前版本多对多映射区别

    EF Core 现在不支持多对多映射,只能做2个一对多映射. 比如Product和Category 我现在定义Product和Category是多对多关系. 那么实体定义如下: public clas ...

  6. 把旧系统迁移到.Net Core 2.0 日记(8) - EASYUI datagrid+ Dapper+ 导出Excel

    迁移也没太大变化,有一个, 之前的Request.QueryString 是返回NameValueCollection, 现在则是返回整个字符串. 你要改成Request.Query[“key”] 直 ...

  7. 把旧系统迁移到.Net Core 2.0 日记(5) Razor/HtmlHelper/资源文件

    net core 的layout.cshtml文件有变化, 区分开发环境和非开发环境. 开发环境用的是非压缩的js和css, 正式环境用压缩的js和css <environment includ ...

  8. 把旧系统迁移到.Net Core 2.0 日记(4) - 使用EF+Mysql

    因为Mac 不能装SqlServer, 所以把数据库迁移到MySql,然后EntityFramework要改成Pomelo.EntityFrameworkCore.MySql 数据库迁移时,nvarc ...

  9. 把旧系统迁移到.Net Core 2.0 日记(3) - 详解依赖注入 (转)

    关于DI 依赖注入, 转载这篇文章, 写得很好的. ----------------------------- DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关 ...

随机推荐

  1. 前端开发——HTML学习笔记

    HTML03

  2. Codeforces 767D - Cartons of milk

    题目链接:http://codeforces.com/contest/767/problem/D D比C水系列. 将商店里面的牛奶按照保质期升序排序(显然优先买保质期久的)考虑二分答案,然后再将整个序 ...

  3. spring boot Tomcat访问日志

    1.Tomcat设置访问日志 <Host name="localhost" appBase="webapps" unpackWARs="true ...

  4. 日期时间函数 mysql 和sqlserver 中对于常用函数的日期和时间函数的区别

    1. sqlserver中获取时间用getdate(),默认返回格式是2019-01-21 13:58:33.053,具体的年月日,时分秒毫米,年月日之间用短线连接,时分秒之间用冒号连接,秒和毫米之间 ...

  5. 基于python Arcface 实现人脸检测和识别

    虹软的人脸识别技术也是很强的,重要的是他免费提供了离线的sdk,还提供了实例,这个是目前几家研究人脸识别的大公司里面少有的.识别能力正常用还是可以的.我这个代码是调用的离线sdk实现的 ``` fro ...

  6. 基于iOS用CoreImage实现人脸识别

    2018-09-04更新: 很久没有更新文章了,工作之余花时间看了之前写的这篇文章并运行了之前写的配套Demo,通过打印人脸特征CIFaceFeature的属性,发现识别的效果并不是很好,具体说明见文 ...

  7. 关于select的默认样式问题

    select { border: solid 1px #000; appearance:none; -moz-appearance:none; -webkit-appearance:none; pad ...

  8. Mac python3.6 安装即使用 psycopg2

    学习下python调用PostgreSQL数据库 首先需要安装 psycopg2 python3安装: pip install psycopg2-binary 官网地址: https://pypi.o ...

  9. c# 静态构造函数与私有构造函数共存

    在使用静态构造函数的时候应该注意几点: 1.静态构造函数既没有访问修饰符,也没有参数.因为是.NET调用的,所以像public和private等修饰符就没有意义了. 2.是在创建第一个类实例或任何静态 ...

  10. 练习:将从表读出来的时间戳除以1000(java读时间戳会多出3个000)用jackson包 实现

    练习:将从表读出来的时间戳除以1000(java读时间戳会多出3个000)jackson包 实现 entity @Entity @DynamicUpdate //自动更新日期 @Data //get/ ...