• C# 实现Jwtbearer Authentication


    Jwtbearer Authentication

    什么是JWT

    JWT(JSON Web Token), 顾名思义就是在Web上以JSON格式传输的Token(RFC 7519)。

    该Token被设计为紧凑声明表示格式,特别适用于分布式站点的单点登录(SSO)场景。

    紧凑 :意味着size小,所以可以在URL中,Header中,Post Parameter中进行传输,并且包含了所需要的信息。

    JWT的构成

    JWT一般由三段构成,用"."号分隔开

    Header.Payload.Signature

    例如:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

    点击链接 如下图

    图左边为Header.Payload.Signature的base64编码

    图右构成

    Header

    • alg:声明加密的算法 ,这里为HS256
    • typ:声明类型,这里为JWT

    然后将Header进行base64编码 得到第一部分

    eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

    Payload

    由三部分构成

    • 标准中注册的声明

    • 公共的声明

    • 私有的声明

      标准中注册的声明 (建议但不强制使用) :

      • iss: jwt签发者

      • sub: jwt所面向的用户

      • aud: 接收jwt的一方

      • exp: jwt的过期时间,这个过期时间必须要大于签发时间

      • nbf: 定义在什么时间之前,该jwt都是不可用的.

      • iat: jwt的签发时间

      • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

      公共的声明 :

    公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

    私有的声明 :

    私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base6编码可以归类为明文信息 。

    定义一个payload:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
    }
    
    

    然后将其进行base64加密,得到Jwt的第二部分。

    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

    Signature(数字签名,防止信息被篡改)

    jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

    • Header (base64后的)

    • Payload (base64后的)

    • Secret

      这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分 。

      // javascript 
      var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);  
      var signature = HMACSHA256(encodedString, 'secret');
      

      将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

      eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

      注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

    下面我们自己来实现一下 JwtBearer Authentication

    1. 新建一个WebApi项目

    1. 新建JwtSeetings类

          public class JwtSeetings
          {
              /// <summary>
              /// 谁颁发的jwt
              /// </summary>
              public string Issuer { get; set; }
      
              /// <summary>
              /// 谁使用这个jwt
              /// </summary>
              public string Audience { get; set; }
      
              /// <summary>
              /// secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,
              /// 所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了
              /// 通过jwt header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分
              /// </summary>
              public string SecretKey { get; set; }
          }
      
    2. appsettings.json里面配置如下

      "JwtSeetings": {
          "Issuer": "http://localhost:5000",
          "Audience": "http://localhost:5000",
          "SecretKey": "zhoudafu201807041123"
      
    3. Startup类里面ConfigureServices添加如下代码

          services.Configure<JwtSeetings>(Configuration.GetSection("JwtSeetings"));
      
                  var jwtSeetings = new JwtSeetings();
                  //绑定jwtSeetings
                  Configuration.Bind("JwtSeetings", jwtSeetings);
                  services.AddAuthentication(options =>
                  {
                      options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                      options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
      
                  })
                  .AddJwtBearer(options =>
                  {
                      options.TokenValidationParameters = new TokenValidationParameters
                      {
                          ValidIssuer = jwtSeetings.Issuer,
                          ValidAudience = jwtSeetings.Audience,
                          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSeetings.SecretKey))
                      };
                  })
                  ;
      
    4. Startup类里面Configure添加如下代码

       app.UseAuthentication();
      
    5. 新增AuthroizeController控制器,并添加如下代码

      [HttpPost]
              public ActionResult Post([FromBody]LoginViewModel loginViewModel)
              {
      
                  if (!ModelState.IsValid)
                  {
                      return BadRequest();
                  }
                  if (loginViewModel.Name == "jack" && loginViewModel.Password == "rose")
                  {
      
                      var claims = new Claim[]
                      {
                          new Claim(ClaimTypes.Name,"jack"),
                          new Claim(ClaimTypes.Role,"admin")
                      };
                      var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSeetings.SecretKey));
                      var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
      
                      var token = new JwtSecurityToken(
                          _jwtSeetings.Issuer,
                          _jwtSeetings.Audience,
                          claims,
                          DateTime.Now,
                          DateTime.Now.AddMinutes(30),
                          creds
                          );
                      return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
                  }
      
                  return BadRequest();
              }
      
    6. 给ValuesController控制器打上[Authorize]特性

    7. 用Postman直接访问http://localhost:5000/api/Values 返回401

      1530672358385

    8. 用Postman访问http://localhost:5000/api/Authroize 得到Token

      1530672442132

    9. 通过Bearer访问成功

    1530672572449

    源代码 https://github.com/HisKingdom/JwtAuthSample

    参考博客:https://www.jianshu.com/p/576dbf44b2ae

  • 相关阅读:
    netcore 开发问题整理(图片地址)
    netcore 开发问题整理(下拉框)
    C# 请求网址中汉字的编码转换
    C# 时间戳
    c# 判断指定文件是否存在
    js数组常用方法整理
    服务器断电 Mysql启动失败
    Bean初始化之postProcessBeforeInitialization、afterPropertiesSet、init-method、postProcessAfterInitialization等方法的加载
    Java多线程基础
    Mysql多字段order by用法
  • 原文地址:https://www.cnblogs.com/aishangyipiyema/p/9262642.html
Copyright © 2020-2023  润新知