WebAPI下使用JWT验证

JWT(JSON Web Token)是目前最流行的跨域身份验证解决方案。

 

JWT 的特点:

无状态:JWT 包含认证信息,token 只需要保存在客户端,服务端不需要保存会话信息,所以 JWT 是无状态的。

    这一点使得服务器压力大大降低,增加了系统的可用性和可扩展性。但是无状态同时也是 JWT 最大的缺点,服务器无法主动让 token 失效。

安全性:客户端每次请求都会携带 token,可以有效避免 CSRF 攻击,同时 token 会自动过期,可以减少 token 被盗用的情况,服务器会通过 jwt 签名验证 token,可以避免 token 被篡改。

可见性:JWT 的 payload 部分可以直接通过 Base64 解码,所以可存储一些其他业务逻辑所必要的非敏感信息。

 

JWT 含有7个官方字段:

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

 

直接上代码:

Nuget:Microsoft.AspNetCore.Authentication.JwtBearer、Swashbuckle.AspNetCore.Filters

0.添加一个JwtHelper类

public class JwtHelper
{
    public static string IssueJwt(string userId)
    {
        var claims = new List<Claim>
            {
                new Claim(JwtRegisteredClaimNames.Jti,userId),//编号
                new Claim(JwtRegisteredClaimNames.Iat,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),//签发时间
                new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),//生效时间
                new Claim(JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(60)).ToUnixTimeSeconds()}"),// 过期时间(生命周期) 60秒
                new Claim(JwtRegisteredClaimNames.Iss,JwtSettings.Issuer), //签发者
                new Claim(JwtRegisteredClaimNames.Aud,JwtSettings.Audience) //接收者
            };
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSettings.SecretKey));//密钥
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        JwtSecurityToken jwt = new JwtSecurityToken(
            claims: claims,// 声明的集合
            expires: DateTime.Now.AddSeconds(30), // 过期时间 此处设置的过期时间会覆盖上面的过期时间
            signingCredentials: creds
            );
        // 生成 jwt字符串
        return new JwtSecurityTokenHandler().WriteToken(jwt);
    }
}

1.配置文件 appsettings.json 里面添加配置信息:

"AllowedHosts": "*",
  "JwtSettings": {
    "Audience": "zk",
    "Issuer": "123",
    "SecretKey": "keykeykeykeykeykeykeykeykeykey=="
  }

2.整个类来存放静态变量,获取配置信息的值:

public class JwtSettings
{
    public static IConfigurationRoot config;
    static JwtSettings()
    {
        //获取配置文件信息
        var builder = new ConfigurationBuilder();
        config = builder.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
    }

    /// <summary>
    /// 签发人
    /// </summary>
    public static string Audience => config["JwtSettings:Audience"]; 
    /// <summary>
    /// 受众
    /// </summary>
    public static string Issuer => config["JwtSettings:Issuer"];
    /// <summary>
    /// 签名
    /// </summary>
    public static string SecretKey => config["JwtSettings:SecretKey"];
}

3.在Startup.cs中ConfigureServices方法中添加服务:

//添加jwt验证
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(o =>
    {
        o.TokenValidationParameters = new TokenValidationParameters()
        {
            ValidateAudience = true,//是否验证Audience
            ValidateIssuer = true,//是否验证Issuer
            ValidateIssuerSigningKey = true,//是否验证SecurityKey
            ValidateLifetime = true,//是否验证失效时间
            ClockSkew = TimeSpan.FromSeconds(10),//默认是300,也就是5分钟后,假如生命周期设置的是30秒,还需要加上这300秒
            ValidAudience = JwtSettings.Audience,
            ValidIssuer = JwtSettings.Issuer,//Audience、Issuer这两项和前面签发jwt的设置一致
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSettings.SecretKey))//用于签名验证
        };
    });

 在ConfigureServices方法中进行配置Swagger:

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });
    // 为 Swagger 设置xml文档注释路径
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    // 添加控制器层注释,true表示显示控制器注释
    c.IncludeXmlComments(xmlPath, true);

    //开启加权小锁
    c.OperationFilter<AddResponseHeadersFilter>();
    c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
    //在Heder中添加Token 传递到后台
    c.OperationFilter<SecurityRequirementsOperationFilter>();
    c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
    {
        Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间有一个空格)",
        Name = "Authorization",//jwt默认的参数名称,
        In = ParameterLocation.Header,//jwt默认存放Autorization信息的位置(header中)
        Type = SecuritySchemeType.ApiKey
    });
});

4.在Startup.cs中Configure方法中配置管道:

//添加jwt验证
app.UseAuthentication();
app.UseAuthorization();

5.添加模拟登陆api,生成Token:

[Route("api/[controller]")]
[ApiController]
public class AuthorizeController : ControllerBase
{
    [AllowAnonymous]
    [HttpGet]
    public IActionResult Get(string userName, string pwd)
    {
        //验证用户
        //省略具体实现……
        //得到userid
        var userid = "userid_look";
        return Ok(
            //以userid生成Token
            JwtHelper.IssueJwt(userid)
        );
    }
}

6.可以使用了:

[HttpGet]
[Authorize] //添加权限校验特性标签后,该api需要token才能访问
public string Get()
{
    return "OK";
}

 

swagger测试jwt步骤:

1.访问Authorize控制器Get方法获得token:

 2.复制token,按下图操作:

 

如果不获取token来访问方法,报下图错误:

 扩展:网页错误代码表示的含义

400  请求出错,语法格式有误,服务器无法理解

401  未授权

403  禁止访问

404  找不到文件

500  服务器内部错误

 

C# 解析token:https://www.cnblogs.com/fanfan-90/p/12911203.html

C# 获得Url请求头中的token值:https://www.cnblogs.com/bindot/p/jwt_token.html

private readonly JwtSecurityTokenHandler _jwtSecurityToken = new JwtSecurityTokenHandler();


//获取token
string authHeader = this.Request.Headers["Authorization"];//Header中的token
if (authHeader == null || !authHeader.StartsWith("Bearer"))
{
    result.Code = -1; result.Message = "url请求头不含token!";
    return result;
}
string token = authHeader.Substring("Bearer ".Length).Trim();
var user_id = _jwtSecurityToken.ReadJwtToken(token).Id;

token在线解析:https://www.box3.cn/tools/jwt.html

时间戳转换:http://www.metools.info/code/c31.html

 

参考:https://www.sohu.com/a/320794114_468635

http://www.fwhyy.com/2020/04/using-jwt-to-implement-interface-authentication-in-dotnet-core-3/

posted @ 2021-04-09 14:57  kueizheng  阅读(920)  评论(0编辑  收藏  举报