理解 JSON Web Token(JWT) 验证

JSON Web Token认证的操作指南

在本文中,我们将了解JSON Web Token的全部内容。

我们将从JWT的基本概念开始,然后查看其结构,最后创建一个简单的服务器,它将获取一些数据并将其插入到JWT中。

什么是JSON Web Token?为什么我们需要它?

JSON Web Token(JWT)是一种安全,紧凑且独立的方式,以JSON对象的形式在多方之间传输信息。

假设你想登录一个应用程序,比如说 Tinder。 Tinder允许用户使用他们的Facebook个人资料登录。 因此,当用户选择使用Facebook登录的选项时,该应用程序会使用用户的凭据(用户名和密码)联系Facebook的身份验证服务器。

验证服务器验证用户的凭据后,将创建JWT并将其发送给用户。 该应用程序现在获得此JWT并允许用户访问其数据。

jwt的结构

一个jwt是由三个以"."来区分开的部分来组成的,他们是:

  • 头部
  • 负载
  • 签名

标头通常由两部分组成:令牌的类型和正在使用的散列算法。

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

负载是我们要发送的实际信息的存储位置。 以下是简单有效负载的示例。 要知道负载可能比这个示例更复杂,以确保更好的安全性。

{
"sub": "65165751325",
"name": "Rajat S",
"admin": true
}

签名用于验证消息在到达目的地之前没有被更改。 这通常通过使用私钥来实现。

这三个部分通常编码为三个在他们之间由.分隔的Base64-URI字符串

要轻松解码,验证,生成或只是使用JWT,请查看Auth0的JWT.IO调试器

现在我们已经基本了解了JSON是什么,让我们来看看如何构建一个简单的身份验证服务器来发布JWT,然后我们将使用它来访问API。

提示:在JavaScript中编写可重用代码时,可以使用Bit快速将其转换为共享组件,以便在不同位置使用,开发和同步。 试试吧。

让我们开始吧

在开始之前, 让我们构建一个简单的web服务端,首先用npm下载express到你的电脑当中,然后打开命令行工具,执行以下语句

$ npm install express

然后,新建一个文件夹叫做 jwt-auth. 当然你也可以命名为其他名字

$ mkdir jwt-auth
$ cd jwt-auth

在这个文件夹里新建一个文件叫做index.js . 这个文件是用来写我们的代码并且启动一个web服务器,并且包含一个独立的路由能够显示当前日期和时间还有一个404页面的处理程序,

打开一个编辑器,写如下的代码

const express = require("express");
const app = express();
const PORT = 8888;
app.get('/time', (req, res) => {
const time = (new Date()).toLocaleTimeString();
res.status(200).send(`The Time is ${time}`);
});

在这里,我们首先导入我们刚刚安装的express库。 然后我正在创建一个名为appconst,它将使express库。 我还创建了另一个名为PORT的常量,它将包含我们的服务器将运行的端口号。 我选择了8888作为端口号。 如果您愿意,也可以使用任何其他端口号。

接下来,我创建了一个名为time的新路由。 此路由以响应请求作为参数进行回调。 简而言之,此路由将向用户显示当前本地时间。

当想要服务器把我们带到除了time之外的任何路由时,我们还要添加一个路由。 这被称为catchall路由。 我希望服务器在此路由上返回404错误。 要实现此功能,请将以下代码写入index.js文件:

app.get("*", (req, res) => {
res.sendStatus(404);
});

还有最后一件事要做。 我们没有告诉我们的服务器要监听哪个端口。 我们已将端口号初始化为8888.但我们还没有在代码中实际使用此定义。 为此,请使用app.listen方法,如下所示:

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});

完成后,我们可以通过命令行输入node来启动服务器。 然后,转到localhost:8888,您会发现它显示Not Found消息。 添加/时间到URL,它会显示本地时间,如下所示:

最终我们已经建立了一个express服务器,它将向我们在/time路由显示当地时间以及任何其他路由上的404错误。

**添加一个登陆路由
由于本文是关于JWT身份验证的,因此我们实现一个名为login的路由。 与之前使用getlisten的部分中的路由不同,此路由将使用post创建。

app.post("/login", (req, res) => {
const user = req.body.username;
res.status(200).send(`User's name is ${user}`);
})

但目前我们的服务器无法读取请求的正文。 为了解决这个问题,我们将安装一个名为body-parser的新中间件。

$ npm install body-parser

然后就像我们使用express库一样,使用require语法将其导入index.js文件。

const bodyParser = require("body-parser");

我们现在可以告诉express使用body-parser来处理所有JSON响应,如下所示:

app.use(bodyParser.json());

现在我们通过node.来启动服务器

为了测试这个新的路由,我们需要用到

,如果你还没有安装的话,下载一下这个吧。

在POSTMAN中,我们创建一个post请求到这个登陆路由,如下所示:

在这个请求体内,创建一个如下的json:

点击发送应该得到以下响应:

发布JWT
现在我们已经构建了一个可以处理GET和POST请求的简单服务器,让我们构建一个简单的JWT发布。

让我们首先创建一个名为users的新数组,其中包含几个用户及其密码,如下所示:

const users = [
{id: 1, username: "clarkKent", password: "superman"},
{id: 2, username: "bruceWayne", password: "batman"}
];

让我们重写 login 路由,首先我们需要检查提交上来的请求里面是否同时包含username 和 password,如果不是的话,那么服务端需要返回400状态码。

app.post("/login", (req, res) => {
if (!req.body.username || !req.body.password) {
res.status(400).send("Error. Please enter the correct username and password");
return;
}

然后如果这个请求有效,我们需要检查是否用户名在我们的users数组里面,
在if语句下面的相同的login路由中写:

const user = users.find((u) => {
return u.username === req.body.username && u.password === req.body.password;
});

如果这个用户并未在我们的users数组当中,我们需要返回401的状态码,同样是在/login路由中:

在继续进行之前,我们下载另一个库叫做 jsonwebtoken

$ npm install jsonwebtoken

在index.js文件的头部,用require语句引入jsonwebtoken

const jwt = require("jsonwebtoken");

我们将使用此库为每个有效用户创建JSON Web令牌。 为此,我们将使用jsonwebtoken库中的sign方法创建一个名为token的新const。 请注意,我们仍然在/ login路由中编写代码。

const token = jwt.sign({
sub: user.id,
username: user.username
}, "mykey", {expiresIn: "3 hours"});
res.status(200).send({access_token: token})

现在去到POSTMAN,发送一个post请求,请求中带有如下的json结构

{
"username": "clarkKent",
"password": "superman",
}

运行结果会是如下的一个JSON Web Token

如果你复制这个access_token到 JWT.IO Debugger中,你会看到相同的用户名和密码,只不过多了一些其他的消息,

旁注 - CORS

在某些情况下,您的应用尝试访问的API运行在与应用程序不同的服务器上,将返回某种“无法加载”错误。

为了防止这种情况发生,我们可以安装另一个名为cors的库

$ npm install cors

在index.js头部,用require语句引入这个库

const cors = require("cors");

然后类似于我们对body-parser库所做的,我们将告诉express使用cors,如下所示:

app.use(cors());

使用JWT为API提供用户访问权限

在这里,我们将创建一个包含两个路由的新API。 第一个路由将是公共的,而第二个将要求用户使用JWT进行身份验证以便访问它。

让我们在jwt-auth文件夹中创建一个名为api.js的新文件。 在此文件中,编写以下启动代码:

const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const PORT = process.env.API_PORT || 8888;
app.use(bodyParser.json());
app.get("*", (req, res) => {
res.sendStatus(404);
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});

让我们创建一个路由名叫 '/asset' 这个是公共路由,将会直接返回一个200的状态码

app.get("/asset", (req, res) => {
res.status(200).send("Everybody can see this");
});

第二个秘密路由是/asset/secret,它将使用jwtCheck进行保护,如下所示:

app.get("/asset/secret", jwtCheck, (req, res) => {
res.status(200).send("Only logged in people can see me");
});

因为index.js文件已经设置启动在8888端口,我们需要把这个文件设置在其他端口,为了实现,我们需要打开命令行工具,然后运行如下代码:

$ export API_PORT=5555

尽管我们已经说过/ asset / secret路由是安全的,但我们还没有真正实现它。 为此,我们需要安装另一个名为express-jwt的中间件库。

$ npm install express-jwt

在index.js头部,用require语句引入这个库

const expressjwt = require("express-jwt");

我们将使用此库来定义jwtCheck。 我们将使用jwtCheck来检查签名是否与我们从身份验证服务器获得的签名相匹配。 如果你还记得,我们把它命名为“mykey”。

const jwtCheck = expressjwt({
secret: "mykey"
});

运行node api.js命令, 检查这个权限的运行情况是否正常, 去POSTMAN发送get请求到localhost:5000/asset,他的响应应该是Everybody can see this

然后你发送请求到localhost:5000/asset/secret ,你应该会看到一个大的错误提示:

要解决此问题,请转到POSTMAN中的Authentication选项卡,然后选择Type as Bearer Token。 然后输入Token的值为我们在前面部分中算出的值。

结论

与其他Web令牌(如简单Web令牌(SWT)或安全断言标记语言(SAML))相比,JWT更简单,因为它基于JSON,比XML更容易理解。

如果我们对JSON进行编码,它的大小将比SAML更小,从而更容易传入HTML和HTTP环境。

安全方面,SWT使用单个密钥,而JWT和SAML都使用公钥和私钥对进行更好的身份验证。

从使用的角度来看,JWT用于互联网规模。这意味着在用户的设备上处理更容易,无论是笔记本电脑还是移动设备。

除了身份验证之外,JSON Web令牌是一种在多方之间传输数据的绝佳方式。 JWT具有签名这一事实使每个人都可以更轻松地识别信息发送者。您只需要正确的密钥即可

JWT验证的更多相关文章

  1. 踩坑之路---JWT验证

    使用JWT验证客户的携带的token 客户端在请求接口时,需要在request的head中携带一个token令牌 服务器拿到这个token解析获取用户资源,这里的资源是非重要的用户信息 目前我的理解, ...

  2. golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息

    golang学习笔记10 beego api 用jwt验证auth2 token 获取解码信息 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放 ...

  3. springboot成神之——basic auth和JWT验证结合

    本文介绍basic auth和JWT验证结合 目录结构 依赖 config配置文件WebSecurityConfig filter过滤器JWTLoginFilter filter过滤器JWTAuthe ...

  4. webapi中使用token验证(JWT验证)

    本文介绍如何在webapi中使用JWT验证 准备 安装JWT安装包 System.IdentityModel.Tokens.Jwt 你的前端api登录请求的方法,参考 axios.get(" ...

  5. 手写jwt验证,实现java和node无缝切换

    前言 前端时间和我朋友写了一个简易用户管理后台,功能其实很简单,涉及到的技术栈有:vue+elementUI,java+spring MVC以及node+egg,数据库用的mysql,简单方便. 一开 ...

  6. Nginx实现JWT验证-基于OpenResty实现

    介绍 权限认证是接口开发中不可避免的问题,权限认证包括两个方面 接口需要知道调用的用户是谁 接口需要知道该用户是否有权限调用 第1个问题偏向于架构,第2个问题更偏向于业务,因此考虑在架构层解决第1个问 ...

  7. Jwt验证登录

    练习模板:https://gitee.com/zh1446802857/swagger-multi-version-api.git Jwt在我的 认知里,是一套门锁.别人(用户)需要用到你的接口 的时 ...

  8. jwt验证登录信息

    为什么要告别session?有这样一个场景,系统的数据量达到千万级,需要几台服务器部署,当一个用户在其中一台服务器登录后,用session保存其登录信息,其他服务器怎么知道该用户登录了?(单点登录), ...

  9. spring集成jwt验证方式,token验证

    为什么要告别session?有这样一个场景,系统的数据量达到千万级,需要几台服务器部署,当一个用户在其中一台服务器登录后,用session保存其登录信息,其他服务器怎么知道该用户登录了?(单点登录), ...

随机推荐

  1. 「PKUSC2018」神仙的游戏

    题目链接 比如说上面\(|S|\)为12的字符串,我们欲求出\(f(9)\)的值,那么上面相同颜色的字符必须两两能够匹配.也就是说,同种颜色的字符集里不能同时出现0和1.如果只考虑同种颜色集里相邻的两 ...

  2. Unity手游之路手游代码更新策略探讨

    版权声明: https://blog.csdn.net/janeky/article/details/25923151 这几个月公司项目非常忙.加上家里事情也多,所以blog更新一直搁置了. 近期在项 ...

  3. Git提交新项目

    Github或者码云上新建项目 $ git init $ git add * $ git remote add origin https://gitee.com/demo/demo.git $ git ...

  4. Spark本地运行成功,集群运行空指针异。

    一个很久之前写的Spark作业,当时运行在local模式下.最近又开始处理这方面数据了,就打包提交集群,结果频频空指针.最开始以为是程序中有null调用了,经过排除发现是继承App导致集群运行时候无法 ...

  5. 帝国CMS 列表模板页面 list.var 内容截取

    每天学习一点点 编程PDF电子书免费下载: http://www.shitanlife.com/code list.var 中没有好的办法,只能用程序代码来实现.将整个HTML以一个变量来拼接.如下: ...

  6. mha高可用以及读写分离

    一.MHA简介 二.工作流程 三.MHA架构图 四.MHA工具介绍 五.基于GTID的主从复制 六.部署MHA 七.配置VIP漂移 八.配置binlog-server 九.MySQL中间件Atlas ...

  7. ucml设置列表分页类型

    aginationCtype 分页栏显示效果配置,取值为UCML.Pagination或UCML.PaginationMin,默认为UCML.PaginationMin,在视图显示前设置. 1.pag ...

  8. Java中static、final、static final的区别【转】

    说明:不一定准确,但是最快理解. final: final可以修饰:属性,方法,类,局部变量(方法中的变量) final修饰的属性的初始化可以在编译期,也可以在运行期,初始化后不能被改变. final ...

  9. Python虚拟环境和包管理工具Pipenv的使用详解--看完这一篇就够了

    前言 Python虚拟环境是一个虚拟化,从电脑独立开辟出来的环境.在这个虚拟环境中,我们可以pip安装各个项目不同的依赖包,从全局中隔离出来,利于管理. 传统的Python虚拟环境有virtualen ...

  10. NodeJS之path模块

    NodeJS之path模块 常用的主要有如下工具函数: 1. path.basename(path[, ext]) 2. path.extname(path) 3. path.dirname(path ...