外观
使用凭证
拿到 token 之后,怎么带、能用多久、失效了怎么办 —— 本页说明这三件事。
放在哪里
所有需要鉴权的接口,都在请求头中携带:
http
Authorization: Bearer <token>1
格式必须严格一致
Bearer与token之间必须只有一个空格Bearer首字母大写(不是bearer)- token 本身不要加引号、不要加
token=前缀 - 不要把 token 放在 URL 参数或请求体中
各语言写法
bash
# curl
curl 'https://mqtt.iot.auto-control.com.cn/api/sensor-data?deviceCode=0010202606040002' \
-H "Authorization: Bearer <token>"1
2
3
2
3
python
import requests
resp = requests.get(
"https://mqtt.iot.auto-control.com.cn/api/sensor-data",
params={"deviceCode": "0010202606040002"},
headers={"Authorization": f"Bearer {token}"},
timeout=15,
)1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
java
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://mqtt.iot.auto-control.com.cn/api/sensor-data?deviceCode=0010202606040002"))
.header("Authorization", "Bearer " + token)
.timeout(Duration.ofSeconds(15))
.GET()
.build();1
2
3
4
5
6
2
3
4
5
6
javascript
const resp = await fetch(
'https://mqtt.iot.auto-control.com.cn/api/sensor-data?deviceCode=0010202606040002',
{ headers: { Authorization: `Bearer ${token}` } }
)1
2
3
4
2
3
4
有效期
token 是一个 JWT。过期时间写在 payload 的 exp 字段里,解码第二段即可读出。
如何读取你的 token 何时过期
bash
echo "$IOT_TOKEN" | cut -d. -f2 \
| tr '_-' '/+' \
| base64 -d 2>/dev/null \
| jq '{sub, customerId, username, roles, exp, exp_time: (.exp | todate)}'1
2
3
4
2
3
4
实际解码结果形如:
json
{
"sub": "626",
"customerId": "1667372262",
"username": "autotest",
"roles": "SYSTEM_ADMIN",
"authorities": "",
"generationTime": 1789466324254,
"exp": 7451690324
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
实测:exp 非常远(不是常见的 7 天)
实测账号的 exp 换算后落在 22 世纪,也就是凭证实际上长期有效。 但请注意:
- 这是当前实现的观测结果,平台未承诺该行为
- 不要因此把「永不过期」写死进代码 —— 请始终按
exp做本地判断
| payload 字段 | 说明 |
|---|---|
sub | 用户 ID(字符串),与登录返回的 userId 对应 |
customerId | 客户 ID(字符串),与登录返回的 customerId 对应 |
username | 账号名 |
roles | 角色标识 |
authorities | 权限串,业务账号上通常为空串 |
generationTime | 签发时间(毫秒) |
exp | 过期时间(Unix 秒) |
平台未提供「查询凭证剩余有效期」的接口
请通过上面这种方式本地解析 JWT 载荷。解析前务必确认 token 未被截断 (JWT 由 header.payload.signature 三段组成,以 . 分隔)。
凭证失效怎么办
先分清两种失败形态
| 场景 | 实际返回 | 含义 |
|---|---|---|
| 未带凭证 / 凭证无效,访问业务域名接口 | HTTP 403 {"error":"Forbidden","message":"Access Denied"} | 需要在框架层就被拒 |
| 访问实时控制域名接口 | HTTP 200,正常返回 | 该域名当前不校验凭证 |
业务域名下未授权返回的是 403,不是 401
如果你的代码只对 401 做「重新登录」处理,会漏掉 403 这种情况。 建议:
text
收到 401 或 403
→ 重新登录(POST /api/account/sign-in)
→ 更新本地 token 缓存
→ 用新 token 重试原请求一次
→ 仍失败 ⇒ 不再重试,记录 requestId 并告警(大概率是权限问题)1
2
3
4
5
2
3
4
5
推荐的 token 缓存实现
python
import base64
import json
import threading
import time
import requests
BIZ_HOST = "https://yunshangwenshi.auto-control.com.cn"
RT_HOST = "https://mqtt.iot.auto-control.com.cn"
class TokenManager:
"""线程安全的 token 缓存:提前 5 分钟刷新;401/403 时强制刷新重试一次。"""
def __init__(self, username: str, password: str, refresh_ahead: int = 300):
self._username = username
self._password = password
self._refresh_ahead = refresh_ahead
self._token = None
self._expire_at = 0.0
self._lock = threading.Lock()
def _sign_in(self) -> None:
resp = requests.post(
f"{BIZ_HOST}/api/account/sign-in",
json={"username": self._username, "password": self._password},
timeout=10,
)
body = resp.json()
if body.get("code") != 200: # 注意:失败时 HTTP 也是 200
raise RuntimeError(f"登录失败: {body.get('code')} {body.get('message')}")
self._token = body["data"]["token"]
self._expire_at = self._read_exp(self._token)
@staticmethod
def _read_exp(token: str) -> float:
"""从 JWT 载荷读取 exp(Unix 秒);解析失败时保守地给 1 小时。"""
try:
payload = token.split(".")[1]
payload += "=" * (-len(payload) % 4)
return float(json.loads(base64.urlsafe_b64decode(payload))["exp"])
except Exception:
return time.time() + 3600
def get(self, force: bool = False) -> str:
with self._lock:
if force or not self._token or time.time() >= self._expire_at - self._refresh_ahead:
self._sign_in()
return self._token
def request(self, method: str, url: str, **kwargs) -> requests.Response:
headers = dict(kwargs.pop("headers", {}))
headers["Authorization"] = f"Bearer {self.get()}"
resp = requests.request(method, url, headers=headers, timeout=30, **kwargs)
if resp.status_code in (401, 403): # ← 401 与 403 都要处理
headers["Authorization"] = f"Bearer {self.get(force=True)}"
resp = requests.request(method, url, headers=headers, timeout=30, **kwargs)
return resp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
常见问题
403 —— 排查顺序
- 请求头里到底有没有
Authorization(抓包确认,不要只看代码) Bearer与 token 之间的空格是否被吃掉或变成两个- token 是否被截断(JWT 三段,中间那两个点容易丢)
- 是否把
Bearer前缀重复写了两次 - 换新 token 仍然 403 ⇒ 权限问题,重试无意义,联系运营方
关于实时控制域名的鉴权现状
实测:不带 Authorization 头调用 mqtt.iot.auto-control.com.cn 下的接口,仍会正常返回数据。
但请仍然按本文档规范携带凭证。 规范实现可以保证平台开启校验后你的系统无需改动。
不要把 token 暴露给最终用户
- token 等价于账号权限,能查你名下全部设备数据
- 不要在移动端 App 里让 token 落在用户可提取的位置
- 不要放到前端页面、URL 或日志明文里