查询 Token 时收到 401,应先检查“谁有权调用查询接口”,再检查“被查询的 Token 是否有效”。这两种凭据职责不同,把目标 Token 放进请求参数,不代表调用者已经通过认证。
本文涉及 2019 年的 Spring Security OAuth 旧栈;该项目已结束生命周期,类名与配置不适合作为新系统模板。
先确认实际访问的是哪条路径
当时有两次记录。一次通过 RestTemplate.getForObject 访问 /check/token,抛出:
HttpClientErrorException$Unauthorized: 401 null
另一次访问的是框架端点 /oauth/check_token,响应包含 Unauthorized。路径相似不表示实现相同:前者可能是业务自定义接口,后者才需要查旧授权服务器配置。还要确认 401 来自网关还是目标应用、HTTP 方法是否正确、请求匹配了哪一条安全过滤器链。
checkTokenAccess 控制访问,不负责替调用者认证
旧配置有这个访问表达式入口:
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()");
}
它是授权服务器配置类中的方法片段,表示端点要求已认证调用者,并没有定义调用者凭据或所有客户端都能获得的权限。历史 API 可用于识别配置入口,不能替代完整安全设计。
当时把表达式改成 permitAll() 后不再出现原错误,只说明访问控制影响了请求。匿名暴露令牌查询可能泄漏令牌状态,不应作为默认修复。应配置并使用被授权的调用身份,权限更细时再限定调用范围。
调用凭据与查询参数分开核对
以受控测试环境为例,依次确认:
- 注册的调用者和端点允许的认证方式是否一致。
- 客户端凭据是否正确传入,反向代理是否移除了相关头部。
- 调用者是否有查询权限,实际过滤器链是否与配置对应。
- 认证通过后,再查看被查询 Token 的存在、过期、撤销和访问范围。
每次只改一个条件,分别验证正确凭据、错误凭据和无凭据的分支;日志只保留脱敏后的错误类别,不记录 Token、secret 或完整 Authorization 头。
RFC 7662 §2.1 要求保护标准内省入口以防令牌探测,但历史 check_token 的协议和响应不能直接等同于该 RFC。刷新请求的客户端认证又是另一条路径,见 invalid_client 排查。