把旧系统迁移到.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). 这个标准已经存在多个后端库和多家公司的支持.
// 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)的更多相关文章
- 把旧系统迁移到.Net Core 2.0 日记 (15) --Session 改用Redis
安装Microsoft.Extensions.Caching.Redis.Core NuGet中搜索Microsoft.Extensions.Caching.Redis.Core并安装,此NuGet包 ...
- 把旧系统迁移到.Net Core 2.0 日记(1) - Startup.cs 解析
因为自己到开发电脑转到Mac Air,之前的Webform/MVC应用在Mac 跑不起来,而且.Net Core 2.0 已经比较稳定了. 1. 为什么会有跨平台的.Net Core 近年来,我们已 ...
- 把旧系统迁移到.Net Core 2.0 日记 (12) --发布遇到的问题
1. 开发时是在Mac+MySql, 尝试发布时是在SQL2005+Win 2008 (第一版) 在Startup.cs里,数据库连接要改,分页时netcore默认是用offset关键字分页, 如果用 ...
- 把旧系统迁移到.Net Core 2.0 日记 (17) --多租户和SoftDelete
在EF Core 2.0版本中出现了全局过滤新特性即HasQueryFilter,它出现的意义在哪里?能够解决什么问题呢? 通过HasQueryFilter方法来创建过滤器能够允许我们对访问特定数据库 ...
- 把旧系统迁移到.Net Core 2.0 日记(10) -- EF core 和之前版本多对多映射区别
EF Core 现在不支持多对多映射,只能做2个一对多映射. 比如Product和Category 我现在定义Product和Category是多对多关系. 那么实体定义如下: public clas ...
- 把旧系统迁移到.Net Core 2.0 日记(8) - EASYUI datagrid+ Dapper+ 导出Excel
迁移也没太大变化,有一个, 之前的Request.QueryString 是返回NameValueCollection, 现在则是返回整个字符串. 你要改成Request.Query[“key”] 直 ...
- 把旧系统迁移到.Net Core 2.0 日记(5) Razor/HtmlHelper/资源文件
net core 的layout.cshtml文件有变化, 区分开发环境和非开发环境. 开发环境用的是非压缩的js和css, 正式环境用压缩的js和css <environment includ ...
- 把旧系统迁移到.Net Core 2.0 日记(4) - 使用EF+Mysql
因为Mac 不能装SqlServer, 所以把数据库迁移到MySql,然后EntityFramework要改成Pomelo.EntityFrameworkCore.MySql 数据库迁移时,nvarc ...
- 把旧系统迁移到.Net Core 2.0 日记(3) - 详解依赖注入 (转)
关于DI 依赖注入, 转载这篇文章, 写得很好的. ----------------------------- DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关 ...
随机推荐
- Mybatis中sql语句中的in查询,判断null和size为0的情况
不严谨的写法,可能会报错:in (),这种情况不符合SQL的语法,导致程序报错. 如果简单只做非空判断,这样也有可能会有问题:本来in一个空列表,应该是没有数据才对,却变成了获取全部数据! 所以一个比 ...
- c代码,输出i,j,k互不相同的三位数
#include <stdio.h> int main() { int i,j,k; printf("\n"); for(i=1;i<5;i++){ for(j= ...
- Systemd初始化进程/RHEL 6系统中System V init命令与RHEL 7系统中systemctl命令的对比
Linux操作系统的开机过程是这样的,即从BIOS开始,然后进入Boot Loader,再加载系统内核,然后内核进行初始化,最后启动初始化进程.初始化进程作为Linux系统的第一个进程,它需要完成Li ...
- idea输出目录详解
引言:在项目中遇到了一个问题,在使用idea时,项目中Tomcat的虚拟目录映射总是失败,而当我采用myeclipse时却能映射过去. 自己花费了很长时间,终于找出了问题所在,原来是由于idea自己采 ...
- java 二维数组的行列长度
在 java 中,其实只有一维数组,而二维数组是在一维数组中嵌套实现的.比如 int[][] a = {{},{},{},{}} 要取行数和某一行的列数可以 : int rowLen = a.leng ...
- public,protected,private
1.public:public表明该数据成员.成员函数是对所有用户开放的,所有用户都可以直接进行调用 2.private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直 ...
- 牛客国庆集训派对Day3 A Knight
Knight 思路: bfs打表找规律 如下图 代码: #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(4) ...
- org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat.1428942566812653608
一.异常信息 org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet reque ...
- python生成随机整数
python生成随机不重复的整数,用random中的sample index = random.sample(range(0,10),10) 上面是生成不重复的10个从1~10的整数 python生成 ...
- Dubbo简介---搭建一个最简单的Demo框架
Dubbo背景和简介 Dubbo开始于电商系统,因此在这里先从电商系统的演变讲起. 单一应用框架(ORM) 当网站流量很小时,只需一个应用,将所有功能如下单支付等都部署在一起,以减少部署节点和成本. ...