# JWT(Json Web Token)

### *安全的在web應用間傳遞信息*

JSON Web Token(以下簡稱JWT)是一個非常輕巧的規範。這個規範允許我們使用JWT在用戶和服務器之間傳遞安全可靠的信息。

來假想一下。用戶甲關注了用戶乙，系統發mail給用戶乙，並且附有一個連結"點此關注用戶甲"。連結的地址為：

```
https://your.awesome-app.com/add-friend/?from_user=乙&target_user=甲
```

上面的URL主要通過URL來描述，這樣作有一個弊端，必須要要求用戶乙先登錄。可不可以簡化這個流程，讓用戶乙部用登錄就可以完成這個操作，JWT就允許我們作到這點。

## JWT的組成

一個JWT實際上就是一個字串，它由三部份組成，頭部、載荷以及簽名。

## 載荷（Payload）

我們繼續沿用上面假想的加好友情境，將這操作描述成一個JSON對象。其中添加了一些其它的信息，幫助今後收到這個JWT的服務器理解這個JWT。

```
{
    "iss": "Jude Chih JWT",
    "iat": 1234567890,
    "exp": 1234567890,
    "aud": "www.example.com",
    "sub": "sausage@example.com",
    "from_user": "乙",
    "target_user": "甲"
}
```

這裡面前五個都是由JWT的標準所定義的。

* iss：該JWT的簽發者
* sub：該JWT所面向的用戶
* aud：接收該JWT的一方
* exp：什麼時候過期，這裡是一個Unix的timestamp
* iat：在什麼時候簽發的

將上面的JSON對象進行\[base64]編碼，可以得到下面的字串，這個字串我們將它稱作JWT的Payload。

```
eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
```

如果你使用Node.js，可以用Node.js的包\[ base64url ]來得到這個字串

```javascript
var base64url = require('base64url')
var header = {
    "from_user": "B",
    "target_user": "A"
}
console.log(base64url(JSON.stringify(header)))
// 输出：eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
```

PS：Base64是一種編碼，也就是說它是可以被翻譯回原來的樣子的，它並不是一種加密過程。

## 頭部（Header）

JWT還需要一個頭部，頭部用於描述關於該JWT的最基本的信息，例如其類型以及簽名所用的算法等，這也可以被看成一個JSON對象。

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

在這裡，我們說明了這是一個JWT，並且我們所用的簽名算法（後面會提到）是HS256算法。

我們將頭部也進行Base64編碼，出來的字串就是JWT的頭部了。

```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
```

## 簽名

將上面兩個編碼字串用`.`連接就形成了

```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0
```

最後，我們將上面拼接完的字串用HS256算法進行加密。在加密的時候，我們還需要個密鑰，假設我們用`mystar`做為密鑰的話，那麼就可以得到我們加密後的內容

```
rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
```

這就是簽名

最後將頭部、載荷以及簽名之間都用`.`連接，我們就可以得到完整的JWT

```
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
```

於是我們就可以把剛剛那個加好友的URL改成下面這樣

```
https://your.awesome-app.com/make-friend/?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
```

這樣就安全的完成天加好友的操作了。

## 簽名的目的

最後一步簽名的過程，實際上是對頭部以及載荷內容進行簽名。一般而言，加密算法對於不同的輸入產生的輸出會不一樣。

所以，如果有人對頭部以及載荷的內容解碼之後進行修改，再進行編碼，那麼新的頭部和載荷的簽名和之前的簽名就將是不一樣的，而且如果不知道服務器加密的時候用的密鑰的話，得出來的簽名也一定會是不一樣的。

服務器應用在接受到JWT後，會首先對頭部和載荷的內容用同一算法再次簽名，那麼服務器應用是怎麼知道我們用的是哪一種算法呢？因為我們在JWT的頭部中已經用`alg`指明了我們的加密算法了。

如果服務器應用對頭部和載荷再次以同樣方法簽名之後發現，自己計算出來的簽名和接受到的簽名不一樣，那麼就說明這個Token的內容被別人動過，我們將拒絕這個Token，跳出401錯誤頁面。

## 信息會暴露

所以，在JWT中，不應該在載荷裡面加入任何敏感的數據，在上面的例子中，我們傳輸的是用戶的UserID，這個實際上不是什麼敏感內容，一般情況下被知道也是安全的。

但是像密碼這樣的內容就不能被放在JWT中了。如果將用戶的密碼放在了JWT中，那麼遇到惡意的第三方就可以通過base64解碼很快的知道你的密碼了。

## JWT的適用場景

我們可以看到，JWT適合用於向Web應用傳遞一些非敏感信息。例如在上面題到的完成加好友的操作，還有諸如下訂單的操作等等。

其實JWT還經常用於設計用戶認證和授權系統。甚至實現Web應用的單點登入(這個功能我在Laravel那本筆記有紀錄)。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tobyisme.gitbook.io/php/jwtjson-web-token.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
