介绍 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. mac android 真机调试

    1.已经安装好Androidstudio或者eclipse 2.下载配置好Android Sdk等 3.将android手机通过USB数据线连接Mac,打开终端输入system_profiler SP ...

  2. 【转】mac上安装gradle

    http://www.douban.com/note/311599602/ 首先,先download最新版本的gradle,网址如下:http://www.gradle.org/get-started ...

  3. JetBrain破解

    https://blog.csdn.net/u014044812/article/details/78727496 https://jetlicense.nss.im/ https://zhile.i ...

  4. python学习第八天

    解析库之bs4的基本使用方法 ''' pip install beautifulsoup4#安装bs4 pip install lxml#安装lxml ''' html_doc = "&qu ...

  5. vue3剖析:响应式原理——effect

    响应式原理 源码目录:https://github.com/vuejs/vue-next/tree/master/packages/reactivity 模块 ref: reactive: compu ...

  6. 【新阁教育】基于Log4Net实现日志信息双向存储(含源码)

    1.引言 在上位机开发中,日志记录是必不可少的,我们可以通过日志记录做日志分析及错误追踪.初学者会采用txt文本写入来实现日志保存,但是文本写入不是线程安全,当存在多个线程同时写入日志时,就会出现一些 ...

  7. java输出1-100之间的数并求和for+while+do while实现

    public static void main(String args[]) {//do while int sum = 0; //当前之和 int i = 1; //加数 do { if (i%2= ...

  8. %s 表示格式化一个对象为字符

    比如: name=“good” print('%s'%name)会打印出good print('%3s'%name)  会打印出good,当%和s之间的数字,小于字符串长度时,实际打印出字符串的长度 ...

  9. 移动web开发之rem适配布局

    移动web开发之rem适配布局 方案: 页面布局文字能否随着屏幕大小变化而变化 流式布局和flex布局主要针对于宽度布局,那高度如何布局? 怎样让屏幕发生变化的时候元素高度和宽度等比例缩放? 1. r ...

  10. Mock简明文档

    Mock简明文档 Mock.mock() Mock.mock( requestUrl?, requestType?, template|function(options) ) Mock.mock( t ...