Tonr 是要读取照片的客户端,Sparklr 提供受保护照片,并在旧示例中承担授权服务角色。理解这个例子,关键不是先记住所有类名,而是分开看:业务请求、浏览器授权、授权码换令牌、携带令牌读照片。

这是我在 2019 年阅读 Spring Security OAuth 示例时的调用路径笔记。以下本机路径属于历史示例,不是可复用的线上地址;OAuth 授权本身也不等于完整的单点登录身份协议。

从“查看照片”进入业务代码

当时从 /tonr2 点击 “View my Sparklr photos”,请求进入 Tonr 的 /sparklr/photos,由 SparklrController.photos 调用 sparklrService.getSparklrPhotoIds()。

照片服务的关键读取类似于:

sparklrRestTemplate.getForObject(
        URI.create(sparklrPhotoListURL), byte[].class);

其中照片地址是示例中的 /sparklr2/photos?format=xml;照片、授权端点和 Token 端点地址由 sparklr.properties 等资源配置提供,不能只修改其中一个就认为流程全部改变。

声明为接口,为什么进入 OAuth2RestTemplate

变量声明类型可能是 RestOperations,运行时对象却是 OAuth2RestTemplate。父类方法调用被子类覆写的方法时,会发生动态分派,不必假设用了反射。

官方旧仓库的 OAuth2RestTemplate 显示它继承 RestTemplate,在创建请求时取得 access token,并按认证方案加入请求。这里以 2.5.2.RELEASE 作类关系和方法参照,不将其当作当年工程的精确版本。

没有授权码时,为什么先跳去浏览器授权

客户端上下文没有可用令牌时,模板沿 getAccessToken → acquireAccessToken → AccessTokenProvider 继续处理。授权码 Provider 发现需要用户授权,会以 UserRedirectRequiredException 表达重定向需求。

OAuth2ClientContextFilter 在过滤器链外围识别该异常,把授权端点与请求参数组成浏览器跳转。可参考 授权码 Provider 与 上下文 Filter 的对应分支。

此时可在脱敏跳转记录中检查:

参数作用
client_id标识请求授权的客户端
redirect_uri授权后回到的已登记地址
response_type=code请求授权码,不是直接请求访问令牌
scope申请的权限范围
state将回调关联到发起的授权请求,参与安全校验

不要复制某次调试产生的 state,也不要因为流程暂时不通就关闭校验。

用户登录和批准后,先回授权码,再换令牌

浏览器进入 Sparklr 的授权端点,用户完成所需认证与批准后,服务器将 code 带回可信回调。客户端取得授权码后,再向 Token 端点发送令牌请求;这一步还涉及客户端认证和回调一致性检查。

在旧调用链中可继续追踪 AuthorizationEndpoint、授权码服务、AuthorizationCodeAccessTokenProvider.retrieveToken、TokenEndpoint.postAccessToken 和 TokenGranter。它们分别承担授权响应、保存/消费授权码与令牌颁发,不是一个“登录成功就自动生成所有凭据”的动作。

获得 access token 后,模板才携带它访问照片资源,Sparklr 的照片控制器返回数据。后续请求能否复用令牌,取决于上下文和存储,不应理解为所有浏览器或用户共用一份内存令牌。

用调用阶段定位问题

有 code 没 Token,优先检查回调与令牌交换;拿到 Token 仍无法读照片,检查资源地址、权限和令牌使用;本地先报匿名异常,则查 ProviderChain 与资源配置。

Spring Security OAuth 已结束生命周期。这条阅读路线用于理解历史代码,不省略安全分支,也不作为今天新建授权或单点登录系统的配置教程。