Cesium 报 401,ion Token 没生效
地球能出来,底图是黑的,网络面板里所有 api.cesium.com 请求都返回 401。
现象
构造函数里写了自己的 token,但控制台仍然出现下面这类报错,影像和地形全部加载失败:
An error occurred while accessing https://api.cesium.com/v1/assets/2/endpoint?access_token=undefined: 401 (Unauthorized)
DeveloperError: Ion token is required to use Cesium's ion services.
at IonResource.fromAssetId (IonResource.js:120)
如果 token 有值但仍然 401,报文会带上 token 的前几位,可以据此判断代码里实际生效的是哪一个 token。
原因
Cesium.Ion.defaultAccessToken 是一个全局静态属性,只在创建
Viewer 或 CesiumWidget 的那一刻被读取。赋值语句写在
new Viewer 之后,默认的 ion 资源已经带着旧 token 开始请求了。
另一种情况是 token 的 scope 不含 assets:read,或者 token 被删除、被冻结。
这类 401 与代码无关,换 token 才会恢复。
排查过程
- 在网络面板找
endpoint请求,看access_token的查询参数是不是undefined或不属于自己的 token。 - 在控制台执行
Cesium.Ion.defaultAccessToken,确认运行时值。 - 在脚本最顶部、任何 Cesium 对象创建之前打印一次,对比两处结果。
- 把同一 token 直接拼进
https://api.cesium.com/v1/assets请求头里测试,确认服务端是否接受。
解决方案
把赋值放到 Cesium 脚本加载之后、创建 Viewer 之前。用局部变量保存,避免被其他脚本覆盖。
<script src="/libs/cesium/1.121/Cesium.js"></script>
<script>
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
var viewer = new Cesium.Viewer('cesiumContainer', {
terrain: Cesium.Terrain.fromWorldTerrain({
requestWaterMask: false,
requestVertexNormals: false
}),
baseLayer: Cesium.ImageryLayer.fromWorldImagery({
style: Cesium.IonImageryProvider.fromAssetId(2)
})
});
</script>
不想依赖 ion 时,把影像和地形都换成自建服务,构造参数里不出现任何 ion 资源, 这样即使 token 为空也不会发 401 请求。
var viewer = new Cesium.Viewer('cesiumContainer', {
baseLayer: new Cesium.ImageryLayer(
new Cesium.UrlTemplateImageryProvider({
url: 'https://tile.example.cn/{z}/{x}/{y}.png',
maximumLevel: 18
})
),
terrainProvider: await Cesium.CesiumTerrainProvider.fromUrl(
'https://terrain.example.cn/tiles/'
)
});
验证
重新加载后网络面板里 endpoint 请求应为 200,
viewer.scene.globe.tilesLoaded 在几秒内变为 true,
控制台不再出现 Ion token is required。
注意:把 token 硬编码在前端等于公开。生产环境应通过后端签发短期 token, 或在构建时注入并按域名白名单限制,不要把长期有效的主 token 写进仓库。
经纬志(2026)。《Cesium 报 401,ion Token 没生效》。https://www.jingweizhi.com/cookbook/cesium-ion-token-401.html(访问日期 2026-11-03)。