zoukankan      html  css  js  c++  java
  • JWT With NetCore WebApi

    1 什么是JWT?

      JWT是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

    2 JWT 的应用场景是什么?

      身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。 信息交换在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的

    3 JWT的结构 

    JWT包含了使用.分隔的三部分: Header 头部 Payload 负载 Signature 签名

    其结构看起来是这样的Header.Payload.Signature

    Header

    在header中通常包含了两部分:token类型和采用的加密算法。{ "alg": "HS256", "typ": "JWT"} 接下来对这部分内容使用 Base64Url 编码组成了JWT结构的第一部分。

    Payload

      Token的第二部分是负载,它包含了claim, Claim是一些实体(通常指的用户)的状态和额外的元数据,有三种类型的claim:reservedpublic 和 private.Reserved claims: 这些claim是JWT预先定义的,在JWT中并不会强制使用它们,而是推荐使用,常用的有 iss(签发者),exp(过期时间戳), sub(面向的用户), aud(接收方), iat(签发时间)。 Public claims:根据需要定义自己的字段,注意应该避免冲突 Private claims:这些是自定义的字段,可以用来在双方之间交换信息 负载使用的例子:{ "sub": "1234567890", "name": "John Doe", "admin": true} 上述的负载需要经过Base64Url编码后作为JWT结构的第二部分。

    Signature

      创建签名需要使用编码后的header和payload以及一个秘钥,使用header中指定签名算法进行签名。例如如果希望使用HMAC SHA256算法,那么签名应该使用下列方式创建: HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 签名用于验证消息的发送者以及消息是没有经过篡改的。 完整的JWT 完整的JWT格式的输出是以. 分隔的三段Base64编码,与SAML等基于XML的标准相比,JWT在HTTP和HTML环境中更容易传递。 下列的JWT展示了一个完整的JWT格式,它拼接了之前的Header, Payload以及秘钥签名:

     

    4 如何使用JWT?

      在身份鉴定的实现中,传统方法是在服务端存储一个session,给客户端返回一个cookie,而使用JWT之后,当用户使用它的认证信息登陆系统之后,会返回给用户一个JWT,用户只需要本地保存该token(通常使用local storage,也可以使用cookie)即可。 当用户希望访问一个受保护的路由或者资源的时候,通常应该在Authorization头部使用Bearer模式添加JWT,其内容看起来是下面这样:Authorization: Bearer <token>

    因为用户的状态在服务端的内存中是不存储的,所以这是一种无状态的认证机制。服务端的保护路由将会检查请求头Authorization中的JWT信息,如果合法,则允许用户的行为。由于JWT是自包含的,因此减少了需要查询数据库的需要。 JWT的这些特性使得我们可以完全依赖其无状态的特性提供数据API服务,甚至是创建一个下载流服务。因为JWT并不使用Cookie的,所以你可以使用任何域名提供你的API服务而不需要担心跨域资源共享问题(CORS)。 下面的序列图展示了该过程:

    5 为什么要使用JWT?

      相比XML格式,JSON更加简洁,编码之后更小,这使得JWT比SAML更加简洁,更加适合在HTML和HTTP环境中传递。 在安全性方面,SWT只能够使用HMAC算法和共享的对称秘钥进行签名,而JWT和SAML token则可以使用X.509认证的公私秘钥对进行签名。与简单的JSON相比,XML和XML数字签名会引入复杂的安全漏洞。 因为JSON可以直接映射为对象,在大多数编程语言中都提供了JSON解析器,而XML则没有这么自然的文档-对象映射关系,这就使得使用JWT比SAML更方便

    6 在NetCore WebApi中怎么用?

    WebAPI使用NetCore2.1创建,无身份认证信息

    Startup.cs中的Configure方法中配置使用Authentication

     1 app.UseAuthentication(); 

    接下来我们需要新建一个文件夹Models,在文件夹下面新建一个类JwtSettings.cs

     1     public class JwtSettings
     2     {
     3         //token是谁颁发的
     4         public string Issuer { get; set; }
     5         //token可以给哪些客户端使用
     6         public string Audience { get; set; }
     7         //加密的key
     8         public string SecretKey{get;set;}
     9 
    10     }

    然后我们需要在appsettings.json中配置jwt参数的值 【注意】 SecretKey必须大于16个,是大于,不是大于等于

     1 {
     2   "Logging": {
     3     "IncludeScopes": false,
     4     "Debug": {
     5       "LogLevel": {
     6         "Default": "Warning"
     7       }
     8     },
     9     "Console": {
    10       "LogLevel": {
    11         "Default": "Warning"
    12       }
    13     }
    14   },
    15   
    16   "JwtSettings":{
    17     "Issuer":"http://localhost:44319",
    18     "Audience":"http://localhost:44319",
    19     "SecretKey":"Hello-key-----ztb"
    20   }
    21 }

    这时候我们重新回到Startup.cs的ConfigureServices方法下,将appsettings.json中的文件读取到JwtSettings中,进行Bind,然后设置jwt参数

     1 public void ConfigureServices(IServiceCollection services)
     2         {
     3             //将appsettings.json中的JwtSettings部分文件读取到JwtSettings中,这是给其他地方用的
     4             services.Configure<JwtSettings>(Configuration.GetSection("JwtSettings"));
     5 
     6             //由于初始化的时候我们就需要用,所以使用Bind的方式读取配置
     7             //将配置绑定到JwtSettings实例中
     8             var jwtSettings=new JwtSettings();
     9             Configuration.Bind("JwtSettings",jwtSettings);
    10 
    11             services.AddAuthentication(options=>{
    12                 //认证middleware配置
    13                 options.DefaultAuthenticateScheme=JwtBearerDefaults.AuthenticationScheme;
    14                 options.DefaultChallengeScheme=JwtBearerDefaults.AuthenticationScheme;
    15             })
    16             .AddJwtBearer(o=>{
    17                 //主要是jwt  token参数设置
    18                 o.TokenValidationParameters=new Microsoft.IdentityModel.Tokens.TokenValidationParameters{
    19             //Token颁发机构
    20                     ValidIssuer =jwtSettings.Issuer,
    21             //颁发给谁
    22                     ValidAudience =jwtSettings.Audience,
    23                     //这里的key要进行加密,需要引用Microsoft.IdentityModel.Tokens
    24                     IssuerSigningKey=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))
    25             //ValidateIssuerSigningKey=true,
    26             ////是否验证Token有效期,使用当前时间与Token的Claims中的NotBefore和Expires对比
    27             //ValidateLifetime=true,
    28             ////允许的服务器时间偏移量
    29             //ClockSkew=TimeSpan.Zero
    30 
    31                 };
    32             });
    33 
    34 
    35             services.AddMvc();
    36         }

    如果我们需要安装nuget包的话,只要在【查看】-》【命令面板】中输入NuGet Package Manager,即可进入package安装,输入Microsoft.AspNetCore.Authentication.JwtBearer即可进行安装

    首先我们新建一个ViewModel文件夹,并在ViewModel文件夹下面新建LoginViewModel.cs

     1 using System.ComponentModel.DataAnnotations;
     2 
     3 namespace JwtAuthSample
     4 {
     5     public class LoginViewModel
     6     {
     7         //用户名
     8         [Required]
     9         public string User { get; set; }
    10         //密码
    11         [Required]
    12         public string Password { get; set; }
    13 
    14     }
    15 }

    接下来在Controllers文件夹下新建控制器AuthorizeController.cs,完整代码如下

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Threading.Tasks;
     5 using Microsoft.AspNetCore.Mvc;
     6 
     7 //引用命名空间
     8 using System.Security.Claims;
     9 using Microsoft.IdentityModel.Tokens;
    10 using Microsoft.Extensions.Options;
    11 using System.Text;
    12 using System.IdentityModel.Tokens.Jwt;
    13 
    14 namespace JwtAuthSample.Controllers
    15 {
    16     [Route("api/[controller]")]
    17     public class AuthorizeController : Controller
    18     {
    19         private JwtSettings _jwtSettings;
    20 
    21         public AuthorizeController(IOptions<JwtSettings> _jwtSettingsAccesser)
    22         {
    23             _jwtSettings=_jwtSettingsAccesser.Value;
    24         }
    25 
    26         [HttpPost]
    27         public IActionResult Token([FromBody]LoginViewModel viewModel)
    28         {
    29             if(ModelState.IsValid)//判断是否合法
    30             {
    31                 if(!(viewModel.User=="ztb"&&viewModel.Password=="123456"))//判断账号密码是否正确
    32                 {
    33                     return BadRequest();
    34                 }
    35 
    36 
    37                 var claim=new Claim[]{
    38                     new Claim(ClaimTypes.Name,"ztb"),
    39                     new Claim(ClaimTypes.Role,"admin")
    40                 };
    41 
    42                 //对称秘钥
    43                 var key=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
    44                 //签名证书(秘钥,加密算法)
    45                 var creds=new SigningCredentials(key,SecurityAlgorithms.HmacSha256);
    46                 
    47                 //生成token  [注意]需要nuget添加Microsoft.AspNetCore.Authentication.JwtBearer包,并引用System.IdentityModel.Tokens.Jwt命名空间
    48                 var token=new JwtSecurityToken(_jwtSettings.Issuer,_jwtSettings.Audience,claim,DateTime.Now,DateTime.Now.AddMinutes(30),creds);
    49 
    50                 return Ok(new {token=new JwtSecurityTokenHandler().WriteToken(token)});
    51 
    52             }
    53 
    54             return BadRequest();
    55         }
    56     }
    57 }

     PostMan测试获取token

     

    这样可以成功获取token,下面来做权限校验

    在需要授权的api上新增 [Authorize] 标记

     比如万年values控制器

    我们分别使用携带token和不携带token访问values接口

    1 携带token访问,返回了想要的数据

     2 未携带token,返回401

     

    Demo地址:https://gitee.com/shaolinsaodiseng/JWTDemo.git

  • 相关阅读:
    POJ 3253 Fence Repair
    POJ 2431 Expedition
    NYOJ 269 VF
    NYOJ 456 邮票分你一半
    划分数问题 DP
    HDU 1253 胜利大逃亡
    NYOJ 294 Bot Trust
    NYOJ 36 最长公共子序列
    HDU 1555 How many days?
    01背包 (大数据)
  • 原文地址:https://www.cnblogs.com/saodiseng2015/p/9651619.html
Copyright © 2011-2022 走看看