Jwt、Session、Cookie

一、Jwt

JWT(JSON Web Token)是一种用于在网络上安全传输信息的开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。JWT常用于身份验证、信息交换和授权等方面。

1、Jwt的结构

JWT由三部分组成,它们通过点(.)分隔:

1.1.Header(头部):包含令牌的元数据,如令牌的类型(JWT)和签名算法(如HMAC SHA256或RSA)。被Base64编码后作为JWT的第一部分。

示例(JSON格式):

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

1.2.Payload(载荷):包含要传递的信息,如用户ID、角色、权限等。载荷分为注册声明、公共声明和私有声明。被Base64编码后作为JWT的第二部分。

注册声明包括:

  • iss(签发者):标识谁签发了这个令牌。
  • sub(主题):标识这个令牌所面向的用户。
  • aud(受众):标识这个令牌的接收对象。
  • exp(过期时间):标识令牌的有效期限。
  • nbf(生效时间):标识令牌开始生效的时间。
  • iat(签发时间):标识令牌的生成时间。
  • jti(JWT ID):令牌的唯一标识符。

示例(JSON格式):

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

1.3.Signature(签名):使用头部和载荷的Base64编码字符串以及一个密钥(secret)进行签名计算,以确保信息传输的安全性。

示例计算签名:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

2、Jwt的配置

2.1、包  Microsoft.AspNetCore.Authentication.JwtBearer

2.2、配置Jwt参数

在 appsettings.json 文件中,配置JWT的相关参数,如密钥(Secret)、颁发者(Issuer)和受众(Audience)。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Jwt": {
    "Key": "ni-hai-yao-wo-zen-yang-YAOZENYANG!@#$%^&*()",
    "Issuer": "JwtIssuer",
    "Audience": "JwtAudience"
  }
}

2.3、注册JWT认证服务

这包括配置JWT令牌验证参数,并添加JWT Bearer认证中间件。

//添加Jwt Bearer认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,//验证签名密钥
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection("Jwt:Key").Value)),//签名密钥
            ValidateIssuer = true,//验证发行人
            ValidIssuer = builder.Configuration.GetSection("Jwt:Issuer").Value,//发行人
            ValidateAudience = true,//验证受众
            ValidAudience = builder.Configuration.GetSection("Jwt:Audience").Value,//受众
            ValidateLifetime = true,//验证Token是否过期
            ClockSkew = TimeSpan.Zero // 设置令牌时钟的偏差 

        };

        //如果jwt过期,在返回的header中加入Token-Expired字段为true,前端在获取返回header时判断
        options.Events = new JwtBearerEvents()
        {
            OnAuthenticationFailed = context =>
            {
                if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                {
                    context.Response.Headers.Add("Token-Expired", "true");
                }
                return Task.CompletedTask;
            }
        };
    });


// 启用认证和授权中间件  
app.UseAuthentication();
app.UseAuthorization();

2.4、生成JWT令牌

新建个Jwt生成帮助类

namespace Jwt_lianxi
{
    public class JwtHelper
    {
        private readonly IConfiguration _configuration;
        public JwtHelper(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public string GenerateJwtToken(User user)
        {
            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Sub, user.Name),
                new Claim(ClaimTypes.Name, user.Name)
            };

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));//签名密钥
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var token = new JwtSecurityToken(
                issuer: _configuration["Jwt:Issuer"],
                audience: _configuration["Jwt:Audience"],
                claims: claims,
                expires: DateTime.Now.AddHours(1), // 过期时间  
                signingCredentials: credentials
                );

            return new JwtSecurityTokenHandler().WriteToken(token);

        }


    }

    //简单的用户模型
    public class User
    {
        public string Name { get; set; }
        public string Password { get; set; }
    }
}

2.5、swagger界面的token图形化验证

为了方便验证token,所以在swagger界面添加一个界面化的token验证。

program.cs

//添加swagger服务并配置Jwt授权
builder.Services.AddSwaggerGen(options =>
{
        //安全模式配置
        options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
        {
            Description = "请输入token,格式为 Bearer xxxxxxxx(注意中间必须有空格)",
            Name = "Authorization",
            In = ParameterLocation.Header,
            Type = SecuritySchemeType.ApiKey,
            BearerFormat = "JWT",
            Scheme = "Bearer"
        });

        //添加安全要求
        options.AddSecurityRequirement(new OpenApiSecurityRequirement {
        {
            new OpenApiSecurityScheme{
                Reference =new OpenApiReference{
                    Type = ReferenceType.SecurityScheme,
                    Id ="Bearer"
                }
            },new string[]{ }
        } 
        });
});

2.6、测试

最后新建控制器来测试

namespace Jwt_lianxi.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AuthorizeTestController : ControllerBase
    {
        private readonly IConfiguration _configuration;
        public AuthorizeTestController(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        [HttpPost]
        public string GetJwtToken(User user)
        {
            return new JwtHelper(_configuration).GenerateJwtToken(user);
        }

        [HttpGet]
        [Authorize]//只有有权限的才可以调用这个接口
        public ActionResult<string> Test1()
        {
            return "请求成功";
        }
    }
}

结果:调GetJwtToken接口会生成token,如果直接再调Test1接口,则会Error: response status is 401,需要先把GetJwtToken生成的token放入界面右上角的Authorize上验证,再次调Test1接口就成功了。界面如下

二、Cookie

1、cookie概述

cookie 常用于保存用户相关并保存在客户端电脑上的一段数据

1.1、cookie基础

//读取cookie内容
Request.Cookies["Key"];

//添加cookie值
1、正常添加
Response.Cookies.Append(key, value);

2、配置
ookieOptions option = new CookieOptions(); 
option.Expires = DateTime.Now.AddMilliseconds(10); 
Response.Cookies.Append(key, value, option);

//删除cookie
Response.Cookies.Delete("Key");

2、Cookie认证、授权

基于Cookie进行身份认证,通常的方案是用户成功登录后,服务端将用户的必要信息记录在Cookie中,并发送给浏览器,后续当用户发送请求时,浏览器将Cookie传回服务端,服务端就可以通过Cookie中的信息确认用户信息了。

  • 配置认证和授权
// 添加 Cookie 认证
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(options =>
            {
                options.LoginPath = "/Home/Login";//如果一个未授权的用户尝试访问应用程序安全的URL时将会被自动跳转到/Home/Login
                options.LogoutPath = new PathString("/Account/Logout");
                options.AccessDeniedPath = "/account/accessdenied"; // 设置拒绝访问路径
                options.Cookie.Name = ".AspNetCore.Cookies";//成功登录后,将为用户创建一个cookie,名字是这个
                options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
                options.SlidingExpiration = true;


                //配置回调函数
                options.Events.OnSigningIn = context =>
                {
                    Console.WriteLine($"{context.Principal.Identity.Name} 正在登录...");
                    return Task.CompletedTask;
                };
 
                options.Events.OnSignedIn = context =>
                {
                    Console.WriteLine($"{context.Principal.Identity.Name} 已登录");
                    return Task.CompletedTask;
                };
                
                options.Events.OnSigningOut = context =>
                {
                    Console.WriteLine($"{context.HttpContext.User.Identity.Name} 注销");
                    return Task.CompletedTask;
                };
 
                options.Events.OnValidatePrincipal += context =>
                {
                    Console.WriteLine($"{context.Principal.Identity.Name} 验证 Principal");
                    return Task.CompletedTask;
                };
            });


// 添加授权
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireLoggedIn", policy =>
        policy.RequireAuthenticatedUser());//在需要权限的控制器上可加上特性[Authorize(Policy = "RequireLoggedIn")]。如果没配置这个,则默认[Authorize]就可以了
});


app.UseAuthentication();
app.UseAuthorization();
  • 创建测试控制器
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace MyWebApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class AccountController : ControllerBase
    {
        [HttpPost("login")]
        public async Task<IActionResult> Login(string username, string password)
        {
            // 验证用户名和密码(这里只是示例,实际应用中应使用更安全的方式)
            if (username == "test" && password == "password")
            {
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, username)
                };

                var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
                var authProperties = new AuthenticationProperties
                {
                    IsPersistent = true // 是否持久化Cookie
                    ExpiresUtc = DateTimeOffset.UtcNow.AddDays(7) // Cookie过期时间 
                };

                await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties);

                return Ok(new { code = 200, message = "登录成功" });
            }

            return Unauthorized(new { code = 401, message = "登录失败" });
        }

        [HttpPost("logout")]
        public async Task<IActionResult> Logout()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return Ok(new { code = 200, message = "注销成功" });
        }

        [HttpGet("accessdenied")]
        public IActionResult AccessDenied()
        {
            return Forbid();
        }
    }
}
  • Claim:表示一条信息的声明。以我们的身份证为例,里面包含姓名、性别等信息,如“姓名:张三”、“性别:男”,这些都是Claim。

  • Identity:表示一个身份。对于一个ClaimsIdentity来说,它是由一个或多个Claim组成的。我们的身份证就是一个Identity。

  • Principal:表示用户本人。对于一个ClaimsPrincipal来说,它是由一个或多个ClaimsIdentity组成的。想一下,我们每个人的身份不仅仅只有一种,除了身份证外,还有驾驶证、会员卡等。

三、Session

1、基础配置
//需要包 Microsoft.Extensions.Caching.StackExchangeRedis
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost"; // Redis 服务器地址
    options.InstanceName = "aaaa";
});

//配置session
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(5);// 会话空闲超时时间 
    options.Cookie.Name = "SessionId1";
    options.Cookie.HttpOnly = true;// Cookie仅通过HTTP传输
    options.Cookie.IsEssential = true; // 标记为必需
});


app.UseSession();
2、测试
//设置session
HttpContext.Session.SetString("age", "18");//还有SetInt32

//获取session
string str = HttpContext.Session.GetString("age");//还有GetInt32

//移除session
HttpContext.Session.Remove("age");

//清除所有session
HttpContext.Session.Clear();

一般在用户登录成功时,SetString设置username,以此来判断用户是否登录。用户注销则Remove这个username

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值