介绍 JWT (JSON Web Token)作为保护Web站点和REST服务的标准越来越流行。我将讨论如何使用。net Core为REST服务和MVC web应用程序实现JWT安全性。我把智威汤逊的安全分成了3个博客 使用JWT安全web应用程序创建JWT安全REST服务 这是三个博客中的第一个,我从JWT的一个小解释开始。 JWT底漆 JWT (JSON Web令牌)是一个开放的安全协议,用于在双方之间安全地交换声明。服务器生成或发出令牌,并通过密钥签名。客户端还知道密钥和密钥,并可以验证令牌是否真实。令牌包含身份验证和授权声明。身份验证就是验证某人是否真的是他声称的那个人。授权是指授予用户访问资源或执行特定任务的权限。例如,用户A可以查看支付,而用户B可以执行支付。JWT是独立的。因为JWT is 一个协议,而不是一个框架,它可以跨不同的语言工作,比如。net, Java Python等等。JWT is 通常通过将JWT添加到请求的头来传输,但也可以作为URL中的参数使用。这个传输使JWT无状态。 JWT结构 JWT分为三个部分: 头载荷的签名 这些部分用一个点隔开。 aaaa.bbbb.cccc 头 标题和有效负载有一个或多个键值对。头包含标记类型('typ')和散列算法('alg') SHA256。 隐藏,复制Code

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

头部和有效负载部分是base64编码,这使得头部部分: 隐藏,复制Code

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

有效载荷 有效负载部分是最有趣的部分,因为它包含所有声明。有三种登记索赔类型,公共索赔和私人索赔。 注册要求 注册的索赔是智威汤逊标准的一部分,在所有的实施中都有相同的目的。为了保持智威汤逊的小尺寸,密钥总是3个字符长。以下是一些简短的清单: iss的发行者识别出谁签发了JWT。子主题标识JWT的主体(读取用户)。aud受众识别JWT的目标受众。exp过期设置了过期日期,当过期时,JWT必须被拒绝。nbf不是之前。设定日期前,JWT不得使用。iat发行。设置创建JWT的日期。jti的JWT唯一标识符。用于一次性令牌,并防止令牌重放。 所有登记的索赔日期都采用Unix纪元日期格式,描述UTC时间1970年1月1日之后的几秒。 公开宣称 公开声明包含更多的一般资料,例如“姓名”。公众姓名也被登记,以防止与其他索赔冲突。 私人索赔 私人索赔是由发行方和受众达成协议的。经常检查私人申索是否与现有的申索冲突。声明“角色”是我们稍后将使用的私有声明示例。 负载的例子 隐藏,复制Code

{
"iss": "JwtServer",
"sub": "hrmanager",
"email": "hrmanager@xyz.com",
"jti": "e971bd9c-7655-41d5-9c49-fabc054dc466",
"iat": 1503922683,
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": [
"Employee",
"HR-Worker",
"HR-Manager"
],
"Department": [
"HR",
"HR"
],
"nbf": 1503922683,
"exp": 1503924483
}

将导致 隐藏,复制Code

eyJpc3MiOiJKd3RTZXJ2ZXIiLCJzdWIiOiJocm1hbmFnZXIiLCJlbWFpbCI6ImhybWFuYWdlckB4eXouY29tIiwianRpIjoiZTk3MWJkOWMtNzY1NS00MWQ1LTljNDktZmFiYzA1NGRjNDY2IiwiaWF0IjoxNTAzOTIyNjgzLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiRW1wbG95ZWUiLCJIUi1Xb3JrZXIiLCJIUi1NYW5hZ2VyIl0sIkRlcGFydG1lbnQiOlsiSFIiLCJIUiJdLCJuYmYiOjE1MDM5MjI2ODMsImV4cCI6MTUwMzkyNDQ4M30

签名 到目前为止,JWT还没有什么安全可言。所有数据都是使用base64编码的,虽然不是人类可读的,但是很容易将其解码成可读的文本。这就是签名出现的地方。有了签名,我们可以验证JWT是否是真实的,没有被篡改。签名是从头、有效负载和密钥计算出来的。 隐藏,复制Code

var headerAndPayload = base64Encode(header) + "." + base64Encode(payload);

var secretkey = "@everone:KeepitSecret!";

signature = HMACSHA256(headerAndPayload, secretkey);

密钥是对称的,并且发布者和客户端都知道。不用说,在存储密钥的地方一定要小心! 把它们放在一起 下面的屏幕转储是在https://jwt的帮助下构造的。可以测试和调试JWT声明的io/。左边的窗格保存JWT,另一个窗格显示提取的头部和有效负载。如果您添加了密钥,页面也会验证签名。 JWT安全概述 图1解决方案概况 解决方案概述展示了三个独立的服务器:Web应用程序、RESTful服务和JWT发布服务器。它们可以托管在一台服务器和一个项目中,但我为它做了三个项目。通过这种方式,每个服务器的配置方式更加清晰。因为JWT是自我包含的,所以JWT发布者和REST服务之间不需要某种连接来验证JWT索赔。 一般JWT流 JWT的基本流程很简单: 用户在web应用程序上输入登录凭据。web应用程序将登录凭据发送给JWT发布者并请求JWT索赔。JWT发布者使用用户数据库验证登录凭据。JWT发行者根据用户数据库中的索赔和角色创建JWT,并为有限的生命周期(30分钟)添加“exp”(Expires)索赔。JWT发行者将JWT发送到web应用程序。Web应用程序接收JWT并将其存储在一个身份验证cookie中。Web应用程序验证JWT和解析身份验证的有效负载授权。Web应用程序将JWT添加到REST服务调用。 优点和缺点 优点: 相对简单。无论你选择什么,安全从来都不是件容易的事。JWT是一种聪明的设计,与做“艰苦”工作的。net库相结合,使JWT相对容易实现。REST服务是真正无状态的,正如它应该是的那样。在大多数情况下,安全性为身份验证添加了某种类型的会话管理。无状态使可伸缩。如果您需要更多的服务器来处理工作负载,则不需要在所有服务器之间共享会话。这使得扩展更容易,出错的可能性更小。可跨不同服务使用。JWT是自包含的,服务可以在不访问用户数据库的情况下进行授权。JWT为临时授权提升提供了很好的选项。声明可以在用户会话期间添加或删除。例如,您可以向成功通过执行支付的双向身份验证的用户添加声明。当付款成功执行时,可以删除索赔。通过这种方式,不需要创建跟踪用户状态的特殊方法。 缺点: JWT没有针对滑动到期的内置特性,尽管您可以自己构建它。秘密密钥非常重要。如果密钥以某种方式被盗或泄漏,安全性就会严重受损。 创建JWT发行者项目 主要任务是根据用户凭证交付JWT声明。项目is 使用单个用户帐户进行身份验证的标准MVC应用程序。 个人用户帐户认证用于确保网站的安全,方便访问用户及其角色和声明。我添加了软件包Microsoft.AspNetCore.Authentication。实际创建JWT的jwtholder。Because JWT不用于保护这个网站调用者,没有必要在启动期间注册jwt承载服务。在启动期间只配置JWT参数。 隐藏,复制Code

{
...
"JwtIssuerSettings": {
"Issuer": "JwtServer",
"ValidFor": 30, // minutes
"SecretKey": "@everone:KeepitSecret!"
},
...

对配置应用DI(依赖项注入)模式。类JwtIssuerSettings映射到appsettings中的配置节JwtIssuerSettings。json和类JwtIssuerFactory创建了IJwtIssuerOptions接口的实例。 隐藏,复制Code

public void ConfigureServices(IServiceCollection services)
{
...
// setup JWT parameters
services.Configure<JwtIssuerSettings>(Configuration.GetSection(nameof(JwtIssuerSettings)));
services.AddTransient<IJwtIssuerOptions, JwtIssuerFactory>();
...

它们被添加到服务集合中,现在作为控制器构造函数中的参数可用。 创建JWT索赔 函数登录控制器JwtIssuerController创建JWT索赔。这个过程非常简单: 找到用户。检查密码。创建发行者、主题、电子邮件唯一Id和问题索赔。从存储中收集用户角色(声明)根据配置参数和密钥创建JWT。向调用者发送令牌 隐藏,收缩,复制Code

namespace Security.Controllers
{
[AllowAnonymous]
[Route("api/security")]
public class JwtIssuerController : Controller
{
private readonly IJwtIssuerOptions JwtOptions;
private readonly UserManager<ApplicationUser> UserManager;
private readonly SignInManager<ApplicationUser> SignInManager;
private readonly RoleManager<IdentityRole> RoleManager; public JwtIssuerController(IJwtIssuerOptions jwtOptions,
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
RoleManager<IdentityRole> roleManager)
{
JwtOptions = jwtOptions;
UserManager = userManager;
SignInManager = signInManager;
RoleManager = roleManager;
} [HttpPost(nameof(Login))]
public async Task<IActionResult> Login([FromBody] LoginResource resource)
{
if (resource == null)
return BadRequest("Login resource must be asssigned"); var user = await UserManager.FindByEmailAsync(resource.Email); if (user == null || (!(await SignInManager.PasswordSignInAsync(user, resource.Password, false, false)).Succeeded))
return BadRequest("Invalid credentials"); String result = await CreateJwtTokenAsync(user); // Token is created, we can sign out
await SignInManager.SignOutAsync(); return Ok(result);
} /// <summary>
/// Fetch user roles and claims from storage
/// </summary>
/// <param name="user">application user</param>
/// <returns>JWT token</returns>
private async Task<String> CreateJwtTokenAsync(ApplicationUser user)
{
// Create JWT claims
var claims = new List<Claim>(new[]
{
// Issuer
new Claim(JwtRegisteredClaimNames.Iss, JwtOptions.Issuer), // UserName
new Claim(JwtRegisteredClaimNames.Sub, user.UserName), // Email is unique
new Claim(JwtRegisteredClaimNames.Email, user.Email), // Unique Id for all Jwt tokes
new Claim(JwtRegisteredClaimNames.Jti, await JwtOptions.JtiGenerator()), // Issued at
new Claim(JwtRegisteredClaimNames.Iat, JwtOptions.IssuedAt.ToUnixEpochDate().ToString(), ClaimValueTypes.Integer64)
}); // Add userclaims from storage
claims.AddRange(await UserManager.GetClaimsAsync(user)); // Add user role, they are converted to claims
var roleNames = await UserManager.GetRolesAsync(user);
foreach (var roleName in roleNames)
{
// Find IdentityRole by name
var role = await RoleManager.FindByNameAsync(roleName);
if (role != null)
{
// Convert Identity to claim and add
var roleClaim = new Claim(ClaimTypes.Role, role.Name, ClaimValueTypes.String, JwtOptions.Issuer);
claims.Add(roleClaim); // Add claims belonging to the role
var roleClaims = await RoleManager.GetClaimsAsync(role);
claims.AddRange(roleClaims);
}
} // Prepare Jwt Token
var jwt = new JwtSecurityToken(
issuer: JwtOptions.Issuer,
audience: JwtOptions.Audience,
claims: claims,
notBefore: JwtOptions.NotBefore,
expires: JwtOptions.Expires,
signingCredentials: JwtOptions.SigningCredentials); // Serialize token
var result = new JwtSecurityTokenHandler().WriteToken(jwt); return result;
}

测试数据 在启动过程中,会创建一个内存中的数据库。它包含三个用户和三个角色,模拟了一个人力资源部门。 角色: 可以是任何公司成员。人力资源部的每个成员。人力资源经理,当然是人力资源老板。 用户: employee@xyz.com hrworker@xyz.com hrmanager@xyz.com|0>>>> 名称空间Microsoft.AspNetCore。标识包含RoleManager< IdentityRole>无需显式配置即可使用。在示例或文档中,您不会读到太多关于它的内容。因为该类对于管理系统中的角色非常有用,所以我们错失了一些机会。 隐藏,复制Code

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
// Fill empty inmemory database during development
if (env.IsDevelopment())
app.InitDb();
...

, 隐藏,收缩,复制Code

  public static class InitDbExtensions
{
public static IApplicationBuilder InitDb(this IApplicationBuilder app)
{
var roleManager = app.ApplicationServices.GetService<RoleManager<IdentityRole>>();
var userManager = app.ApplicationServices.GetService<UserManager<ApplicationUser>>(); if (userManager.Users.Count() == 0)
{
Task.Run(() => InitRoles(roleManager)).Wait();
Task.Run(() => InitUsers(userManager)).Wait();
} return app;
} private static async Task InitRoles(RoleManager<IdentityRole> roleManager)
{
var role = new IdentityRole("Employee");
await roleManager.CreateAsync(role); role = new IdentityRole("HR-Worker");
await roleManager.CreateAsync(role);
await roleManager.AddClaimAsync(role, new Claim("Department", "HR")); role = new IdentityRole("HR-Manager");
await roleManager.CreateAsync(role);
await roleManager.AddClaimAsync(role, new Claim("Department", "HR"));
} private static async Task InitUsers(UserManager<ApplicationUser> userManager)
{
var user = new ApplicationUser() { UserName = "employee", Email = "employee@xyz.com" };
await userManager.CreateAsync(user, "password");
await userManager.AddToRoleAsync(user, "Employee"); user = new ApplicationUser() { UserName = "hrworker", Email = "hrworker@xyz.com" };
await userManager.CreateAsync(user, "password");
await userManager.AddToRoleAsync(user, "Employee");
await userManager.AddToRoleAsync(user, "HR-Worker"); user = new ApplicationUser() { UserName = "hrmanager", Email = "hrmanager@xyz.com" };
await userManager.CreateAsync(user, "password");
await userManager.AddToRoleAsync(user, "Employee");
await userManager.AddToRoleAsync(user, "HR-Worker");
await userManager.AddToRoleAsync(user, "HR-Manager");
}
}
}

测试JWT索赔 我通过添加包装Swashbuckle来增加Swagger。AspNetCore进行测试。你可以在这里阅读。更多如何配置swagger。简而言之,事情是这样的 隐藏,复制Code

public void ConfigureServices(IServiceCollection services)
{
...
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
{
Description = "Authorization format : Bearer {token}",
Name = "Authorization",
In = "header",
Type = "apiKey"
}); c.SwaggerDoc("v1", new Info { Title = "Security Api", Version = "v1" });
});
...

隐藏,复制Code

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger(); // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Security Api v1");
});
...

Swagger现在可以通过http://localhost:49842/swagger/进行测试 我们可以在https://jwt.io/测试响应 一切看起来都很好,我们可以开始保护REST服务了。 Visual Studio启动项目 有时,Visual Studio启动项目会丢失,从而阻止应用程序运行。右键点击解决方案,选择“设置启动项目……” 修复启动设置: , 结论 这个博客演示了如何设置JWT (JSON Web令牌)发行方。无状态、自包含、可扩展和其他特性使JWT成为一种聪明的设计。在包的帮助下,JWT很好地集成了。net Core,而且安装起来不费什么力气。 下一篇文章:JWT安全第2部分,安全REST服务 进一步的阅读 JWT JSON Web令牌 JWT标准 JWT索赔 Unix纪元Datetime 设置大摇大摆 , 版本 1.0 2017-08-31首次发布 1.1 2017-09-05源代码升级为Dot Net Core 2.0 , 本文转载于:http://www.diyabc.com/frontweb/news19620.html

JWT安全性第1部分,创建令牌的更多相关文章

  1. 安全性与收尾工作 创建基本的安全策略 精通ASP-NET-MVC-5-弗瑞曼

  2. JSON Web令牌(JWT)介绍与使用

    手机端接口开发会遇到一个问题是,接口登录后需要返回一个Token.token首先有一点必须唯一,每次请求都需要把token给带上.基于必须唯一的特性,很多朋友在开发是都选择了uuid.是不是token ...

  3. 使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)——第1部分

    原文:使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)--第1部分 原文链接:https://www.codeproject.com/Articles/5160941/ASP- ...

  4. 什么是JWT令牌认证?

    当下,JWT(JSON Web Token)令牌认证已经变得越来越流行.本文主要介绍JWT令牌认证与传统的Session会话认证机制的区别. 为什么需要认证? HTTP是一种无状态协议,那就意味着当前 ...

  5. OAuth2.0实战!使用JWT令牌认证!

    大家好,我是不才陈某~ 这是<Spring Security 进阶>的第3篇文章,往期文章如下: 实战!Spring Boot Security+JWT前后端分离架构登录认证! 妹子始终没 ...

  6. 深入浅出JWT(JSON Web Token )

    1. JWT 介绍 JSON Web Token(JWT)是一个开放式标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象安全传输信息. 这些信息可以通过数字签名进行 ...

  7. Java JWT: JSON Web Token

    Java JWT: JSON Web Token for Java and Android JJWT aims to be the easiest to use and understand libr ...

  8. 认识JWT

    1. JSON Web Token是什么 JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的.自包含的方式,用于作为JSON对象在各方之间安全地传输信息.该 ...

  9. [转]认识JWT

    本文转自:https://www.cnblogs.com/cjsblog/p/9277677.html 1. JSON Web Token是什么 JSON Web Token (JWT)是一个开放标准 ...

随机推荐

  1. Git在windows上的设置详解

    这几天在学习使用Git版本管理工具,发现期间的各种配置还是挺繁琐的,而且好多命令的确记不住,于是写个blog记录下来,方便以后查阅. 1. 首先到GitHub官网上下载最新的Git,然后装上,装的过程 ...

  2. ctf常见源码泄露

    前言 在ctf中发现很多源码泄露的题,总结一下,对于网站的搭建要注意删除备份文件,和一些工具的使用如git,svn等等的规范使用,避免备份文件出现在公网 SVN源码泄露 原理 SVN(subversi ...

  3. Linux—账号管理及命令使用详解

    关注微信公众号:CodingTechWork,一起学习进步. 引言   在Linux系统中,我们常常会看到目录或文件的所属关系: [root@linux01 ~]# ll -d test.sh -rw ...

  4. js+canvas画随机4位验证码

    啥都不说了,复制代码吧!!! <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  5. JVM学习第三天(JVM的执行子系统)之类加载机制补充

    昨晚没看完,今天继续 系统的类加载器 对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间.这句话可以表达得更通俗一些: ...

  6. JS数组去重的实现

    其实数组去重的实现就分为两大类 利用语法自身键不可重复性 利用循环(递归)和数组方法使用不同的api来处理. 注意️:下列封装成方法的要在函数开始增加类型检测,为了让去重的实现代码更加简单易懂,封装时 ...

  7. PHP的七个数组指针函数

    1. PHP的七个数组指针函数 函数 描述 reset() 将一个数组的内部指针重置到首位,并返回第一个元素的值 end() 将一个数组的内部指针移动到数组的最后一个元素所在的位置,并返回最后一个元素 ...

  8. spring boot+spring security集成以及Druid数据库连接池的问题

    贴工程目录,其中bll目录下是service+dao层,common是一些公用的模块及功能类,web是controller层 用到了druid及Redis,工具及配置类目录(本文不介绍如何配置drui ...

  9. Java中AQS基本实现原理

    一.AQS概述 AQS全名AbstractQueuedSynchronizer,意为抽象队列同步器,JUC(java.util.concurrent包)下面的Lock和其他一些并发工具类都是基于它来实 ...

  10. d3力导图绘制节点间多条关系平行线的方法

    之前用d3做了多条线之间的绘图是曲线表示的,现在产品要求改成平行线的样式,经过在网上的调研和自己的尝试,实践出一个可用的方法,分享给大家,先展示下结果: 事先声明,本方法是在以下参考网站上进行的结合和 ...