欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C# 实现Jwtbearer Authentication

程序员文章站 2022-03-14 08:46:12
Jwtbearer Authentication 什么是JWT JWT(JSON Web Token), 顾名思义就是在Web上以JSON格式传输的Token "(RFC 7519" )。 该Token被设计为紧凑声明表示格式,特别适用于分布式站点的单点登录(SSO)场景。 紧凑 :意味着size小 ......

Jwtbearer Authentication

什么是JWT

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

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

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

JWT的构成

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

Header.Payload.Signature

例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

如下图

C# 实现Jwtbearer Authentication

图左边为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项目

C# 实现Jwtbearer Authentication

  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

    C# 实现Jwtbearer Authentication

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

    C# 实现Jwtbearer Authentication

  9. 通过Bearer访问成功

C# 实现Jwtbearer Authentication

源代码

参考博客: