JWT学习文章:

第一篇:JWT原理

第二篇:JWT原理实现代码


上一篇学习了JWT的基本理论,这一篇将根据原理进行代码实现。

要想实现jwt的加密解密,要先生成一个SecurityKey,大家可以在网上工具生成一个随机的密钥。我是在这里生成的。

下面篇幅大量都是代码,因为注释写得很清楚,因此就不再有过多文字说明。

代码实现

新建常量类:Const

public class Const
{
public const string SecurityKey= "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSfLGu+kcFDcJUCV46J+SbgR0lNc2NqgCGzojQTWW9xqjuzPF3mpisvTggYZSGfBzN+88YLZYbBLrDTUMJ4nTieElbP6SHkBFu8F+7fFBi7w3UPsaAXDr2E2srQYU5ZlKAcFBoNajNWj3sfSVRoYRPdqDTj4WdJlUPSNGz0wgRrQIDAQAB";
public const string Domain = "http://localhost:5000";
}

新建控制器:AuthController

    [ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
[HttpGet]
public IActionResult Get(string userName, string pwd)
{
//此处只简单的验证用户名和密码的不为空,实际中使用时不要这样
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(pwd))
{
//Header
var header = "{\"alg\": \"HS256\",\"typ\": \"JWT\"}";
var headerBase = Base64UrlTextEncoder.Encode(Encoding.UTF8.GetBytes(header)); //Payload
var payloadDic = new Dictionary<string, object>();
payloadDic["iss"]= Const.Domain;
//添加jwt可用时间
var now = DateTime.UtcNow;
payloadDic["nbf"] = now.ToUniversalTime();//可用时间起始
payloadDic["exp"] = now.AddMinutes(30).ToUniversalTime();//可用时间结束
var payload = JsonConvert.SerializeObject(payloadDic);
var payloadBase = Base64UrlTextEncoder.Encode(Encoding.UTF8.GetBytes(payload)); //Signature
//声明hs256对象
var hs256 = new HMACSHA256(Encoding.UTF8.GetBytes(Const.SecurityKey));
//生成signature
var signature = hs256.ComputeHash(Encoding.UTF8.GetBytes(headerBase + "." + payloadBase));
var signatureBase = Base64UrlTextEncoder.Encode(signature);
return Ok(new
{
token = headerBase + "." + payloadBase + "." + signatureBase
}) ;
}
else
{
return BadRequest(new { message = "username or password is incorrect." });
}
}
}

为了过滤哪些接口需要验证,此处新建一个特性:AuthAttribute

    public class AuthAttribute : Attribute
{
public AuthAttribute()
{
}
}

修改原有的Home控制器:

    [ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
[HttpGet]
[Route("api/value1")]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value1" };
} [HttpGet]
[Route("api/value2")]
[Auth]
public ActionResult<IEnumerable<string>> Get2()
{
return new string[] { "value2", "value2" };
}
}

Value2接口标记了Auth特性,在下面验证时有Auth特性标记的接口才会被要求token。

新建静态类:AuthExtension,并且增加一个IApplicationBuilder的扩展方法:

    public static class AuthExtension
{
public static void AddAuthorize(this IApplicationBuilder applicationBuilder)
{
applicationBuilder.Use(async (currentContext, nextContext) =>
{
//获取是否具有自定义的auth特性
var authAttribute = currentContext.GetEndpoint()?.Metadata.GetMetadata<AuthAttribute>();
if (authAttribute != null)
{
if (currentContext.Request.Headers.ContainsKey("Authorization"))
{
var authorize = currentContext.Request.Headers["Authorization"].ToString();
if (authorize.Contains("Bearer"))
{
var info = authorize.Replace("Bearer ", string.Empty);
var jwtStr = info.Split('.').ToArray();
//声明hs256对象
var hs256 = new HMACSHA256(Encoding.UTF8.GetBytes(Const.SecurityKey));
//生成signature
var signature = Base64UrlTextEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(jwtStr[0] + "." + jwtStr[1])));
//验证加密后是否相等
if (jwtStr[2] == signature)
{
//验证是否在有效时间内
var now = DateTime.UtcNow.ToUniversalTime();
var payload = JsonConvert.DeserializeObject<Dictionary<string, object>>(Encoding.UTF8.GetString(Base64UrlTextEncoder.Decode(jwtStr[1])));
if (now >= Convert.ToDateTime(payload["nbf"]) && now <= Convert.ToDateTime(payload["exp"]))
{
//await currentContext.Response.WriteAsync("验证通过").ConfigureAwait(true);
await nextContext?.Invoke();
return;
}
currentContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await currentContext.Response.WriteAsync("Authorization time has passed, please log in again!").ConfigureAwait(true);
}
}
}
currentContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await currentContext.Response.WriteAsync("Verification failed, no permission to access!").ConfigureAwait(true);
}
await nextContext?.Invoke();
});
}
}

在启动类Startup的请求管道中(Configure)添加上面的扩展方法:

//添加身份验证
app.AddAuthorize();

注意一定要把这句话添加在UseRouting()之后,因为在扩展方法中获取Auth特性只有在注册了Routing规则后才能获取到值。

测试

访问无需权限的Value1接口:

访问成功!!!

获取token:

我启用了swagger,如果没有启用在postman中请求https://localhost:5001/Auth?userName=admin&pwd=admin也是一样的。

代码中用户名和密码我只是简单的验证了下是否为空,所以这里填写什么都能通过。

获取token成功说明获取token的代码没有问题,逻辑有没有问题还不能确定,所以要经过后面接口的确认看是否成功。

访问要求权限验证的Value2接口:

不带token:

访问失败!!!

带上token:

成功!!!

静待三十分钟(代码中设置token过期时间为三十分钟),调用Value2接口:

失败了!!!错误提示是token过期。

如果觉得不保险,还可以逐步调试看一下是否所有逻辑都正确执行,这里就不再进行赘述了。

至此证明我们依照jwt原理写的权限验证成功!!!

JWT原理实现代码的更多相关文章

  1. JWT原理和使用

    jwt JSON Web Tokens,是一种开发的行业标准RFC 7519,用于安全的表示双方之间的声明.目前,jwt广泛的用在系统的用户认证方面,特别是前后端分离项目. 1.jwt认证流程 在项目 ...

  2. JWT原理

    1.COOKIE使用和优缺点 https://www.cnblogs.com/xiaonq/p/11094480.html   1.1 cookie原理: 用户名+密码 cookie是保存在用户浏览器 ...

  3. JWT原理及常见攻击方式

    JWT的全称是Json Web Token.它遵循JSON格式,将用户信息加密到token里,服务器不保存任何用户信息,只保存密钥信息,通过使用特定加密算法验证token,通过token验证用户身份. ...

  4. flume原理及代码实现

    转载标明出处:http://www.cnblogs.com/adealjason/p/6240122.html 最近想玩一下流计算,先看了flume的实现原理及源码 源码可以去apache 官网下载 ...

  5. Java Base64加密、解密原理Java代码

    Java Base64加密.解密原理Java代码 转自:http://blog.csdn.net/songylwq/article/details/7578905 Base64是什么: Base64是 ...

  6. Base64加密解密原理以及代码实现(VC++)

    Base64加密解密原理以及代码实现 转自:http://blog.csdn.net/jacky_dai/article/details/4698461 1. Base64使用A--Z,a--z,0- ...

  7. AC-BM算法原理与代码实现(模式匹配)

    AC-BM算法原理与代码实现(模式匹配) AC-BM算法将待匹配的字符串集合转换为一个类似于Aho-Corasick算法的树状有限状态自动机,但构建时不是基于字符串的后缀而是前缀.匹配 时,采取自后向 ...

  8. Java基础知识强化之集合框架笔记47:Set集合之TreeSet保证元素唯一性和比较器排序的原理及代码实现(比较器排序:Comparator)

    1. 比较器排序(定制排序) 前面我们说到的TreeSet的自然排序是根据集合元素的大小,TreeSet将它们以升序排列. 但是如果需要实现定制排序,比如实现降序排序,则要通过比较器排序(定制排序)实 ...

  9. PHP网站安装程序的原理及代码

    原文:PHP网站安装程序的原理及代码 原理: 其实PHP程序的安装原理无非就是将数据库结构和内容导入到相应的数据库中,从这个过程中重新配置连接数据库的参数和文件,为了保证不被别人恶意使用安装文件,当安 ...

随机推荐

  1. JAVA并发(1)-AQS(亿点细节)

    AQS(AbstractQueuedSynchronizer), 可以说的夸张点,并发包中的几乎所有类都是基于AQS的. 一起揭开AQS的面纱 1. 介绍 为依赖 FIFO阻塞队列 的阻塞锁和相关同步 ...

  2. 攻防世界(十三)unserialize3

    攻防世界系列 :unserialize3 1.打开题目,反序列化 2.代码审计 类xctf被调用时_weakeup()函数会被自动执行,但当序列化字符串中属性值个数大于属性个数,就会导致反序列化异常, ...

  3. 030. Python装饰器

    一  装饰器 1.1 装饰器介绍 扩展函数新功能的@定义:替换旧函数,返回新函数,在不改变原有代码的前提下,为该函数扩展新功能;语法:@ (语法糖) 1.2 装饰器的原型 def show(func) ...

  4. shell基础之EOF的用法

    一.EOF的用法 EOF是(END Of File)的缩写,表示自定义终止符.既然自定义,那么EOF就不是固定的,可以随意设置别名,在linux按ctrl-d 就代表EOF. EOF一般会配合cat能 ...

  5. KEIL中查看程序存储空间的大小

    Program Size: Code=86496 RO-data=9064 RW-data=1452 ZI-data=16116 Code是代码占用的空间,RO-data是 Read Only 只读常 ...

  6. 常用数据库连接池配置及使用(Day_11)

    世上没有从天而降的英雄,只有挺身而出的凡人. --致敬,那些在疫情中为我们挺身而出的人. 运行环境 JDK8 + IntelliJ IDEA 2018.3  优点: 使用连接池的最主要的优点是性能.创 ...

  7. java 文件上传下载

    翻新十年前的老项目,文件上传改为调用接口方式,记录一下子~~~ java后台代码: //取配置文件中的上传目录 @Value("${uploadPath}") String pat ...

  8. 记一次 .NET 某电商交易平台Web站 CPU爆高分析

    一:背景 1. 讲故事 已经连续写了几篇关于内存暴涨的真实案例,有点麻木了,这篇换个口味,分享一个 CPU爆高 的案例,前段时间有位朋友在 wx 上找到我,说他的一个老项目经常收到 CPU > ...

  9. docker中ubuntu源更新慢加速 换为国内源 Debian10源

    本来以为是Ubuntu打包的镜像,换了阿里源老是报错100公钥不可用,结果发现是Debian的操作系统,换位Debian的操作系统打包的,换位Debian的源即可 #源如果使用错误也会报错,没有Deb ...

  10. ubuntu设置允许root用户登录

    一.允许ssh登录root用户(命令行) 1.修改root 密码,sudo passwd root 2.修改ssh配置, sudo vim /etc/ssh/shd_config,修改文件中的Perm ...