JWT란 무엇인가요? 이 글은 여러분에게 JWT를 이해하고, node에서의 JWT 적용 방법과 JWT의 장점과 단점을 소개할 것입니다. 모두에게 도움이 되기를 바랍니다!
JWT는 JSON Web Token의 약어로, 기존 인증 메커니즘에서는 몇 단계만 거치면 됩니다.
1. 用户将账号密码发送到服务器; 2. 服务器通过验证账号密码后,会在当前session中保存一些用户相关的信息,用户角色或者过期时间等等; 3. 服务器给用户一个session_id, 写入用户的Cookie或者客户端自行保存在本地; 4. 用户每次请求服务,都需要带上这个session_id,或许会通过Cookie,或者其他的方式; 5. 服务器接收到后,回去数据库查询当前的session_id,校验该用户是否有权限;
이 모델의 장점 중 하나는 서버가 언제든지 사용자의 권한을 종료할 수 있고 데이터베이스로 이동하여 현재 사용자의 세션 정보를 수정하거나 삭제할 수 있다는 것입니다. 그러나 단점도 있습니다. 즉, 서버 클러스터인 경우 각 서버가 동일한 세션 저장 정보를 얻을 수 있도록 모든 시스템이 세션 정보를 공유해야 한다는 것입니다. 이러한 문제는 해결될 수 있지만 작업량이 엄청납니다.
JWT 솔루션의 장점은 이 정보가 저장되지 않는다는 것입니다. 토큰 데이터는 요청이 수락될 때마다 확인만 하면 됩니다.
JWT의 원리에 대해 간단히 이야기해 보겠습니다. 실제로 클라이언트가 인증 요청을 보내면 서버는 사용자를 인증한 후 JSON 개체를 생성합니다. 여기에는 "당신은 누구입니까?" 뭐하세요?" 잠깐, 만료 시간" 이 정보에 중요한 점은 만료 시간이 있어야 한다는 것입니다. 일반적인 형식은
{ username: "贼烦字符串er", role: "世代码农", endTime: "2022年5月20日" }
입니다. 하지만 그렇게 피상적인 방식으로 전달되지는 않습니다. 공식화된 서명 알고리즘을 통해 귀하에게 전달됩니다. 제출된 페이로드의 일부 정보는 가역적 서명 알고리즘을 사용하여 서명되고 전송됩니다. 일반적인 형식을 나타내기 위해 그림을 사용합니다.
그림에서 볼 수 있듯이, 반환된 정보는 크게 세 부분으로 나누어져 있으며 왼쪽에 서명이 있습니다. 후속 결과, 즉 클라이언트에 반환된 결과도 오른쪽에 Decoded의 소스 코드입니다. 세 부분은 "점"으로 구분됩니다. " 그리고 빨간색, 보라색, 청록색의 세 가지 색상에 해당합니다:
먼저 빨간색 부분이 헤더입니다. 헤더는 주로 방법을 지정합니다. 그림의 서명 알고리즘(기본 HS256)은 SHA- 256. 대칭 알고리즘입니다. 두 당사자 간에는 하나의 키만 공유됩니다. 필드는 JWT 유형으로 식별됩니다.
두 번째 보라색 부분 페이로드는 전송될 실제 데이터인 JSON 개체입니다. 사용 가능한 공식 필드는 7개입니다.
이 필드 외에도 JWT는 기본적으로 암호화되지 않으므로 일부 사용자 정의 필드를 만들 수도 있습니다. 사용할 때 일부 민감한 데이터를 사용하지 않도록 주의하세요.
세 번째 부분은 Signature
서명입니다. 이 부분은 본인이 지정한 비밀키로 서버에만 존재하며, 헤더에 지정된 알고리즘을 사용하여 다음 서명을 통해 서명합니다. 방법. Signature
签名,这一部分,是由你自己指定且只有服务器存在的秘钥,然后使用头部指定的算法通过下面的签名方法进行签名。
下面我们来感受一下具体的使用:
第一步:我们需要搭建一个nodejs的项目;通过npm init -y
初始化一个项目;之后我们需要安装依赖,分别按状express
、jsonwebtoken
和nodemon
三个依赖:
$ npm i express jsonwebtoken nodemon
之后在package.json
中的scripts
字段中添加nodemon app.js
命令:
"scripts": { "start": "nodemon app.js" },
第二步:初始化一下node应用,在根目录下创建app.js文件;
// app.js const express = require("express"); const app = express(); app.use(express.json()); app.listen(3000, () => { console.log(3000 + " listening..."); // 监听3000端口 });
第三步:引入jsonwebtoken
依赖,并且创建接口和服务器的私钥;
// app.js //... const jwt = require("jsonwebtoken"); const jwtKey = "~!@#$%^&*()+,"; // ...
这里面的jwtKey
是我们自定义保存仅限保存在服务器中的私钥,之后我们开始写一个 /login 接口,用来登录,并且创建本地的模拟数据库用来校验,并通过jwt.sign
方法进行校验签名:
// app.js const database = { username: "username", password: "password", }; app.post("/login", (req, res) => { const { username, password } = req.body; if (username === database.username && password === database.password) { jwt.sign( { username, }, jwtKey, { expiresIn: "30S", }, (_, token) => { res.json({ username, message: "登陆成功", token, }); } ); } });
上面代码中我们创建了database
变量来模拟创建了本地的账号密码数据库,用来校验登陆;接下来建立了一个/login
的post
接口,在校验账号密码完全匹配之后,我们通过jsonwebtoken
包导入的jwt
对象下的人sign
npm init -y
를 통해 프로젝트를 초기화합니다. express
, jsonwebtoken
및 nodemon
을 각각 설치해야 합니다. 🎜export function sign( payload: string | Buffer | object, secretOrPrivateKey: Secret, options?: SignOptions, ): string; export function sign( payload: string | Buffer | object, secretOrPrivateKey: Secret, callback: SignCallback, ): void; export function sign( payload: string | Buffer | object, secretOrPrivateKey: Secret, options: SignOptions, callback: SignCallback, ): void;🎜 뒤에
package를 추가합니다. json
의 scripts
필드에 있는 app.js 명령: 🎜export interface SignOptions { algorithm?: Algorithm | undefined; keyid?: string | undefined; expiresIn?: string | number | undefined; /** expressed in seconds or a string describing a time span [zeit/ms](https://github.com/zeit/ms.js). Eg: 60, "2 days", "10h", "7d" */ notBefore?: string | number | undefined; audience?: string | string[] | undefined; subject?: string | undefined; issuer?: string | undefined; jwtid?: string | undefined; mutatePayload?: boolean | undefined; noTimestamp?: boolean | undefined; header?: JwtHeader | undefined; encoding?: string | undefined; }🎜2단계: 노드 애플리케이션을 초기화하고 루트 디렉터리에 앱을 만듭니다. 🎜
POST http://localhost:3000/login content-type: application/json { "username": "username", "password": "password" }🎜3단계:
jsonwebtoken
종속성을 도입하고 인터페이스와 서버의 개인 키를 만듭니다. 🎜app.get("/afterlogin", (req, res) => { const { headers } = req; const token = headers["authorization"].split(" ")[1]; // 将token放在header的authorization字段中 jwt.verify(token, jwtKey, (err, payload) => { if (err) return res.sendStatus(403); res.json({ message: "认证成功", payload }); }); });🎜여기의
jwtKey
는 사용자 정의 저장 전용입니다. 저장된 개인 키를 제한합니다. 그런 다음 로그인을 위한 /login 인터페이스 작성을 시작하고 확인을 위한 로컬 시뮬레이션 데이터베이스를 생성한 다음 jwt.sign
메소드를 통해 서명을 확인합니다. :🎜// 有四个接口签名,可以自行查文档 export function verify( token: string, // 需要检验的token secretOrPublicKey: Secret | GetPublicKeyOrSecret, // 定义在服务器的签名秘钥 callback?: VerifyCallback<JwtPayload | string>, // 获取校验信息结果的回调 ): void;🎜에서 위의 코드에서는 로그인을 확인하기 위해 로컬 계정 및 비밀번호 데이터베이스 생성을 시뮬레이션하기 위해
database
변수를 만든 다음 /login
post
를 설정했습니다. > 인터페이스에서 계정 비밀번호가 완전히 일치하는지 확인한 후 jsonwebtoken
패키지를 사용하여 서명할 jwt
개체 코드> 메서드 아래의 sign 사람을 가져옵니다. , 이 메서드에는 세 가지 인터페이스 서명이 있습니다. 🎜<pre class="brush:js;toolbar:false;">export function sign(
payload: string | Buffer | object,
secretOrPrivateKey: Secret,
options?: SignOptions,
): string;
export function sign(
payload: string | Buffer | object,
secretOrPrivateKey: Secret,
callback: SignCallback,
): void;
export function sign(
payload: string | Buffer | object,
secretOrPrivateKey: Secret,
options: SignOptions,
callback: SignCallback,
): void;</pre><p>这里用到了函数重载的方式实现接口,我们这里将实现最后一个接口签名,第一个参数可以是一个自定义的对象类型,也可以是一个<code>Buffer
类型,还可以直接是一个string
类型,我们的源码使用了object
类型,自定义了一些字段,因为jwt在进行签名是也会对这些数据一并进行签名,但是值得注意的是,这里尽量不要使用敏感数据,因为JWT默认是不加密的,它的核心就是签名,保证数据未被篡改,而检查签名的过程就叫做验证。
当然你也可以对原始Token进行加密后传输;
第二个参数:是我们保存在服务器用来签名的秘钥,通常在客户端-服务端模式中,JWS 使用 JWA 提供的 HS256 算法加上一个密钥即可,这种方式严格依赖密钥,但在分布式场景,可能多个服务都需要验证JWT,若要在每个服务里面都保存密钥,那么安全性将会大打折扣,要知道,密钥一旦泄露,任何人都可以随意伪造JWT。
第三个参数:是签名的选项SignOptions
,接口的签名:
export interface SignOptions { algorithm?: Algorithm | undefined; keyid?: string | undefined; expiresIn?: string | number | undefined; /** expressed in seconds or a string describing a time span [zeit/ms](https://github.com/zeit/ms.js). Eg: 60, "2 days", "10h", "7d" */ notBefore?: string | number | undefined; audience?: string | string[] | undefined; subject?: string | undefined; issuer?: string | undefined; jwtid?: string | undefined; mutatePayload?: boolean | undefined; noTimestamp?: boolean | undefined; header?: JwtHeader | undefined; encoding?: string | undefined; }
这里我们用的是expiresIn
字段,指定了时效时间,使用方法参考这个文档;
第四个参数是一个回调,回调的第二个参数就是我们通过签名生成的token
,最后将这个token
返回给前端,以便存储到前端本地每次请求是带上到服务端进行验证。
接下来,我们来验证一下这个接口:
我是在vscode安装的REST Client插件,之后在根目录创建一个request.http
的文件,文件内写上请求的信息:
POST http://localhost:3000/login content-type: application/json { "username": "username", "password": "password" }
之后在命令行执行npm run start
命令启动服务,之后在requset.http
文件上方点击Send Request
按钮,发送请求:
请求成功后,会看到这样的响应报文:
token
字段就是我们JWT生成的token
;
下面来验证一下这个token
是否有效,我们在写一个登录过后的接口:
app.get("/afterlogin", (req, res) => { const { headers } = req; const token = headers["authorization"].split(" ")[1]; // 将token放在header的authorization字段中 jwt.verify(token, jwtKey, (err, payload) => { if (err) return res.sendStatus(403); res.json({ message: "认证成功", payload }); }); });
这段代码中,通过获取请求头中的authorization
字段中的token
进行获取之前通过JWT生成的token
。
之后通过调用jwt.verify
校验方法校验这个token
是否有效,这个方法分别有三个参数:
// 有四个接口签名,可以自行查文档 export function verify( token: string, // 需要检验的token secretOrPublicKey: Secret | GetPublicKeyOrSecret, // 定义在服务器的签名秘钥 callback?: VerifyCallback<JwtPayload | string>, // 获取校验信息结果的回调 ): void;
接下来我们把刚才响应的token
复制到请求头中:
### GET http://localhost:3000/afterlogin authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjUyNzg5NzA3LCJleHAiOjE2NTI3ODk3Mzd9.s9fk3YLhxTUcpUgCfIK4xQN58Hk_XEP5y9GM9A8jBbY
前面的Bearer认证, 是http协议中的标准认证方式
同样点击Send Request
当看到下面图片的响应,就意味着响应成功:
其实以上就是JWT的一些简单的用法,接下来再说一下JWT本身存在的优缺点.
JWT占用的存储空间其实并不小,如果我们需要签名做过多的信息,那么token很可能会超出cookie的长度限制,例如对比一下这两张图片:
很明显,随着payload的信息量增大,token的长度也会增加;
安全性,其实如果token
的占用空间过大,Cookie
最大存储空间只有4kb前端可以存储在localStorage
之类的本地存储,但是会带来一个问题,如果不是放在cookie的话,安全性就会大打折扣,就会有通过js脚本获取到的风险,就意味着任何hacker都可以拿着它做任何事情;
不灵活的时效性,其实JWT的某方面意义在于用户token
不需要持久化存储,而是采用服务器校验的方式对token
进行有效校验,刚才看到了,签名也是把到期时间一并签名的,如果改变到期时间token
就会被篡改,由于没有存储和手动更改时效的方法,所以很难立刻将这个token
删掉,如果用户重复登陆两次,生成两个token
,那么原则上两个token
都是有效的;
以上主要讲了几点:
JWT의 원리는 주로 서버의 개인 키를 통해 JSON 서명으로 생성된 토큰
과 통신하는 것입니다. token
进行会话;
也介绍了JWT内部数据的组成,是由Header用来指定签名算法和类型的,payload来传输JSON数据,Signature来对数据进行签名算法,放置篡改;
具体介绍了一下如何通过nodejs使用JWT,通过sign
方法进行数据签名,verify
方法进行签名验证;
还介绍了一些JWT的不足:
一个是存储空间随着签名数据量的增大而增加;
再有就是安全性,如果由于存储空间过大将无法存储在安全级别相对较高的Cookie
中,导致脚本可以随意获取;
再有就是时效性,无法灵活的控制token
nodejs를 통해 JWT를 사용하고 데이터 서명을 수행하는 방법을 구체적으로 소개합니다.
하나는 저장공간입니다. 서명데이터의 양이 늘어나면sign
메소드를 통해 서명 확인을 위한verify
메소드도 JWT의 몇 가지 단점을 소개합니다.
그다음은 보안입니다. 저장공간이 너무 크면 쿠키
에 저장되지 않습니다. > 비교적 높은 보안 수준으로 인해 스크립트가 마음대로 얻을 수 있습니다.
토큰
의 적시성은 유연하게 제어할 수 없습니다. 🎜🎜🎜참고용으로 위 nodejs의 🎜데모 소스 코드🎜입니다. 🎜🎜https://github.com/wangzi6224/jwt-usage-nodejs🎜🎜🎜노드 관련 지식을 더 보려면 다음을 방문하세요. 🎜nodejs 튜토리얼🎜! 🎜위 내용은 JWT란 무엇입니까? nodejs에서 JWT를 어떻게 사용하나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!