Sa-Token OAuth2 Server 端 API 对接全指南:四大授权模式与 Token 生命周期管理

发布时间:2026/9/14 15:57:09
Sa-Token OAuth2 Server 端 API 对接全指南:四大授权模式与 Token 生命周期管理 Sa-Token OAuth2 Server 端 API 对接全指南四大授权模式与 Token 生命周期管理【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token本文以 Sa-Token OAuth2 官方仓库的搭建示例为基准系统梳理 OAuth2-Server 端对外暴露的全部 API 及其参数、返回值与源码实现供 OAuth2-Client 端按文档完成对接。读者学完后将能够独立完成授权码、隐藏式、密码式、凭证式四种授权模式的接口调用并掌握 Code / Access-Token / Refresh-Token / Client-Token 的获取、刷新、回收与校验全流程。一、API 总览Server 端暴露的 7 个接口基于官方仓库的搭建示例OAuth2-Server端会暴露以下 APIOAuth2-Client端可据此文档进行对接接口路径作用对应授权模式/oauth2/authorize获取授权码 / 直接下发 Token授权码式、隐藏式/oauth2/doLogin用户登录非 OAuth2 标准协议接口授权流程前置步骤/oauth2/doConfirm用户确认授权非 OAuth2 标准协议接口授权流程前置步骤/oauth2/tokenCode 换 Access-Token / 密码式登录授权码式、密码式/oauth2/refreshRefresh-Token 刷新 Access-Token授权码式、密码式/oauth2/revoke回收 Access-Token通用/oauth2/client_token获取 Client-Token应用自身授权凭证式这 7 个路径常量集中在 SaOAuth2Consts.java 的Api类中定义。所有/oauth2/*请求由统一的入口分发官方 demo 中 SaOAuth2ServerController.java 用RequestMapping(/oauth2/*)捕获请求后转交SaOAuth2ServerProcessor.instance.dister()做路由分发分发逻辑在 SaOAuth2ServerProcessor.java 中按路径逐一匹配。二、模式一授权码模式Authorization Code授权码模式是 OAuth2 中最安全、最常用的授权流程全程分“引导授权 → 登录 → 确认授权 → 换 Token”四个阶段。2.1、获取授权码/oauth2/authorize根据以下格式构建 URL引导用户访问复制时请注意删减掉相应空格和换行符http://{host}:{port}/oauth2/authorize ?response_typecode client_id{client_id} redirect_uri{redirect_uri} scope{scope} state{state}参数详解参数是否必填说明response_type是返回类型这里请填写codeclient_id是应用 idredirect_uri是用户确认授权后重定向的 url 地址scope否具体请求的权限多个用逗号(或空格)隔开state否随机值此参数会在重定向时追加到 url 末尾不填不追加如果填写则每次填写的值不可以重复注意点如果用户在OAuth-Server端尚未登录会被转发到登录视图你可以参照文档或官方示例自定义登录页面。如果scope参数为空或者请求的scope用户近期已确认授权过则无需用户再次确认达到静默授权的效果否则需要用户手动确认服务器才可以下放code授权码。从源码看authorize()的处理顺序依次为校验授权模式是否开启 → 判断是否已登录未登录则返回notLoginView登录视图→ 解析请求参数构建RequestAuthModel→ 执行开发者自定义的授权前置检查 → 校验重定向域名合法性 → 校验 Client 是否签约了请求的 Scope → 判断是否需要用户手动确认授权 → 最终下发code见 SaOAuth2ServerProcessor.java。其中是否需要手动确认由isNeedCarefulConfirm判定其规则为请求权限为空则免确认包含高级权限higherScope则必须手动确认低级权限lowerScope会被剔除后再结合用户近期授权记录判断见 SaOAuth2Template.java。用户确认授权之后会被重定向至redirect_uri并追加code参数与state参数形如redirect_uri?code{code}state{state}Code授权码具有以下特点每次授权产生的Code码都不一样。Code码用完即废不能二次使用。一个Code的有效期默认为五分钟超时自动作废。每次授权产生新Code码会导致旧Code码立即作废即使旧Code码尚未使用。授权码的默认有效期由 SaOAuth2ServerConfig.java 中的codeTimeout字段控制默认60 * 5秒可全局调整新 Code 作废旧 Code的特性加上 Code 只能使用一次共同避免了授权码被重放攻击的风险。2.2、RestAPI 登录接口/oauth2/doLogin如果用户在 OAuth-Server 端尚未登录则会被阻塞在登录界面开始登录需要在页面上调用/oauth2/doLogin完成登录此接口非 OAuth2 标准协议接口http://{host}:{port}/oauth2/doLogin ?name{name} pwd{pwd}参数详解参数是否必填说明name否账号pwd否密码访问此接口将进入自定义的cfg.doLoginHandle函数开始登录你只要在此函数内调用StpUtil.login(xxx)即代表登录成功。另外需要注意此接口并非只能携带name、pwd参数因为你可以在方法里通过SaHolder.getRequest().getParam(xxx)来获取前端提交的其它参数。官方 demo 中给出了该策略函数的标准写法见 SaOAuth2ServerController.java// 登录处理函数 SaOAuth2Strategy.instance.doLoginHandle (name, pwd) - { if(sa.equals(name) 123456.equals(pwd)) { StpUtil.login(10001); return SaResult.ok().set(satoken, StpUtil.getTokenValue()); } return SaResult.error(账号名或密码错误); };与之配套的还有未登录视图notLoginView与确认授权视图confirmView均在同文件中以策略函数方式定制例如未登录时返回login.html页面。2.3、RestAPI 确认授权接口/oauth2/doConfirm如果 oauth-client 端申请的 scope 在 OAuth-Server 端需要用户手动确认授权则会被阻塞在授权界面需要在页面上调用/oauth2/doConfirm完成授权此接口非 OAuth2 标准协议接口http://{host}:{port}/oauth2/doConfirm ?client_id{value} scope{value} build_redirect_uri{true|false} response_type{value} redirect_uri{value} state{value}参数详解参数是否必填说明client_id是应用 idscope是具体确认的权限多个用逗号(或空格)隔开response_type是取 url 上的response_type参数来提交redirect_uri是取 url 上的redirect_uri参数来提交build_redirect_uri否是否立即构建redirect_uri授权地址取值true / false (默认)state否取 url 上的state参数来提交此接口有两种调用方式均需提交client_id、scope、response_type、redirect_uri参数可直接取当前授权页 URL 上的 query 参数区别仅在于是否立即构建授权地址方式一不提供build_redirect_uri或设为false仅确认授权返回结果代表是否确认授权成功{ code: 200, msg: ok, data: null, }确认成功后需再访问/oauth2/authorize完成授权跳转。方式二指定build_redirect_uri: true并同时提供state等参数此时返回结果包括最终的 code 授权地址{ code: 200, msg: ok, data: null, redirect_uri: http://sa-oauth-client.com:8002/?coden12TTc1M9REfJVqKm0wewDz0tNZDBhE1A90irOJmxD0zb92pdhUK8NghJfuC }前端在 ajax 回调函数中直接使用location.hrefres.redirect_uri跳转即可无需再重复访问/oauth2/authorize接口。源码实现上doConfirm只允许 POST 方式提交见 SaOAuth2ServerProcessor.java随后会依次校验 Client 是否存在、Scope 是否已签约、授权模式是否开启、重定向域名是否合法校验通过后调用saveGrantScope持久化用户授权记录。官方 H5 页面 login.js 中确认授权按钮即按方式二拼接location.search上的全部 query 参数并追加build_redirect_uritrue后提交。2.4、根据授权码获取 Access-Token/oauth2/token获得Code码后我们可以通过以下接口获取到用户的Access-Token、Refresh-Token等信息http://{host}:{port}/oauth2/token ?grant_typeauthorization_code client_id{client_id} client_secret{client_secret} code{code}参数详解参数是否必填说明grant_type是授权类型这里请填写authorization_codeclient_id是应用 idclient_secret是应用秘钥code是步骤 1.1 中获取到的授权码也可以通过Basic Authorization方式提交client信息格式为在请求header头添加Authorization参数header[Authorization] base64(${client_id}:${client_secret});接口返回示例{ code: 200, // 200表示请求成功非200标识请求失败, 以下不再赘述 msg: ok, data: null, token_type: Bearer, access_token: Gly7mnnXSdCxkOqmOwcA5SbG6ZtPmJVX7ZgSn1pidhRmnenBEgxbWJS8VWxA, // Access-Token值 refresh_token: EuYNwpxdc18MpaZLPyhFeyAyzr2IOWEr4q3QUGgPWqdJujQqvohjQEDJpwOm, // Refresh-Token值 expires_in: 7199, // Access-Token剩余有效期单位秒 refresh_expires_in: 2591999, // Refresh-Token剩余有效期单位秒 client_id: 1001, // 应用 id scope: userinfo // 此令牌包含的权限 }从源码看使用 Code 换 Token 时会执行严格的参数校验Code 是否存在、client_id 是否与签发 Code 时一致、client_secret 是否正确、若提交了 redirect_uri 则必须与请求 Code 时提供的一致见 SaOAuth2Template.java。默认情况下Access-Token有效期两个小时accessTokenTimeoutRefresh-Token有效期 30 天refreshTokenTimeout均可在全局配置中调整也可在SaClientModel中按应用单独覆盖。2.5、根据 Refresh-Token 刷新 Access-Token/oauth2/refreshAccess-Token 的有效期较短如果每次过期都需要重新授权的话会比较影响用户体验因此我们可以在后台通过Refresh-Token刷新Access-Tokenhttp://{host}:{port}/oauth2/refresh ?grant_typerefresh_token client_id{client_id} client_secret{client_secret} refresh_token{refresh_token}参数详解参数是否必填说明grant_type是授权类型这里请填写refresh_tokenclient_id是应用 idclient_secret是应用秘钥refresh_token是步骤 1.2 中获取到的Refresh-Token值接口返回值同章节 1.2返回新的access_token、expires_in等字段此处不再赘述。源码中refresh()会先强制校验grant_type必须为refresh_token再走统一的授权处理见 SaOAuth2ServerProcessor.java并同样校验 Refresh-Token 是否存在、client_id 与 client_secret 是否匹配见 SaOAuth2Template.java。框架默认在刷新时复用原 Refresh-Token若需要每次刷新都产生新 Refresh-Token可将配置项isNewRefresh置为true。2.6、回收 Access-Token/oauth2/revoke可以在 Access-Token 过期之前主动将其回收http://{host}:{port}/oauth2/revoke ?client_id{client_id} client_secret{client_secret} access_token{access_token}参数详解参数是否必填说明client_id是应用 idclient_secret是应用秘钥access_token是步骤 1.2 中获取到的Access-Token值返回值样例{ code: 200, msg: ok, data: null }回收时框架会同时删除 Token 本体及其按clientId loginId维护的索引见 SaOAuth2Template.javarevoke()接口还内置了容错若提交的 Access-Token 不存在会直接返回ok而不抛异常见 SaOAuth2ServerProcessor.java。2.7、根据 Access-Token 获取相应用户的账号信息/oauth2/userinfo注此接口非 OAuth2 标准协议接口为官方仓库 demo 模拟接口正式项目中大家可以根据此样例自定义需要的接口及参数http://{host}:{port}/oauth2/userinfo?access_token{access_token}返回值样例{ code: 200, msg: ok, nickname: shengzhang_, // 账号昵称 avatar: http://xxx.com/1.jpg, // 头像地址 age: 18, // 年龄 sex: 男, // 性别 address: 山东省 青岛市 城阳区 // 所在城市 }除了直接在 url 中以 query 参数方式提交access_token你也可以在Authorization请求头以Bearer Token方式提交header[Authorization] Bearer access_token;三、模式二隐藏式Implicit根据以下格式构建 URL引导用户访问http://{host}:{port}/oauth2/authorize ?response_typetoken client_id{client_id} redirect_uri{redirect_uri} scope{scope} state{state}参数详解参数是否必填说明response_type是返回类型这里请填写tokenclient_id是应用 idredirect_uri是用户确认授权后重定向的 url 地址scope否具体请求的权限多个用逗号(或空格)隔开state否随机值此参数会在重定向时追加到 url 末尾不填不追加如果填写则每次填写的值不可以重复此模式会越过授权码的步骤直接返回Access-Token到前端页面形如redirect_uri#tokenxxxx-xxxx-xxxx-xxxx注意 token 是以#锚参数的形式拼接到 url 上的。对应源码中当response_type为token时框架直接调用generateAccessToken生成令牌并通过buildImplicitRedirectUri构建以#锚点携带 token 的重定向地址见 SaOAuth2ServerProcessor.java。由于 token 直接暴露在浏览器端此模式安全性相对较弱一般建议仅在无法服务端存储令牌的场景使用。四、模式三密码式Password首先在 Client 端构建表单让用户输入 Server 端的账号和密码然后在 Client 端访问接口http://{host}:{port}/oauth2/token ?grant_typepassword client_id{client_id} client_secret{client_secret} username{username} password{password} scope{scope}参数详解参数是否必填说明grant_type是返回类型这里请填写passwordclient_id是应用 idclient_secret是应用秘钥username是用户的OAuth2-Server端账号password是用户的OAuth2-Server端密码scope否具体请求的权限多个用逗号(或空格)隔开接口返回示例{ code: 200, // 200表示请求成功非200标识请求失败, 以下不再赘述 msg: ok, access_token: 7Ngo1Igg6rieWwAmWMe4cxT7j8o46mjyuabuwLETuAoN6JpPzPO2i3PVpEVJ, // Access-Token 值 refresh_token: ZMG7QbuCVtCIn1FAJuDbgEjsoXt5Kqzii9zsPeyahAmoir893ARA4rbmeR66, // Refresh-Token 值 expires_in: 7199, // Access-Token 剩余有效期单位秒 refresh_expires_in: 2591999, // Refresh-Token 剩余有效期单位秒 client_id: 1001, // 应用 id scope: , // 此令牌包含的权限 }[!WARNING| label:重写认证处理器] 在正式项目中password 认证模式需要重写PasswordGrantTypeHandler处理器在后面的 自定义 grant_type 章节我们会详细介绍。密码式的默认实现在 PasswordGrantTypeHandler.java 中框架先取出username、password参数调用loginByUsernamePassword完成登录并取得loginId再为其生成 Access-Token。值得注意的是该默认实现仅复用了doLoginHandle策略函数并打印仅供开发测试的警告因此正式项目必须重写此处理器例如官方 demo 中的 CustomPasswordGrantTypeHandler.java同一目录下还有自定义PhoneCodeGrantTypeHandler可作为参考。五、模式四凭证式Client Credentials以上三种模式获取的都是用户的Access-Token代表用户对第三方应用的授权在 OAuth2.0 中还有一种针对 Client 级别的授权即Client-Token代表应用自身的资源授权。在 Client 端的后台访问以下接口http://{host}:{port}/oauth2/client_token ?grant_typeclient_credentials client_id{client_id} client_secret{client_secret} scope{scope}参数详解参数是否必填说明grant_type是返回类型这里请填写client_credentialsclient_id是应用 idclient_secret是应用秘钥scope否具体请求的权限多个用逗号(或空格)隔开接口返回值样例{ code: 200, msg: ok, client_token: HmzPtaNuIqGrOdudWLzKJRSfPadN497qEJtanYwE7ZvHQWDy0jeoZJuDIiqO, // Client-Token 值 expires_in: 7199, // Token剩余有效时间单位秒 client_id: 1001, // 应用 id scope: null // 包含权限 }注Client-Token具有延迟作废特性即在每次获取最新Client-Token的时候旧Client-Token不会立即过期而是作为Lower-Client-Token再次储存起来资源请求方只要携带其中之一便可通过 Token 校验这种特性保证了在大量并发请求时不会出现新旧 Token 交替造成的授权失效保证了服务的高可用。凭证式的处理入口为 SaOAuth2ServerProcessor.java 中的clientToken()它会依次校验grant_type、系统是否开启凭证式模式enableClientCredentials、当前 Client 的allowGrantTypes是否包含该模式、Scope 是否签约、client_secret 是否正确最后生成并返回ClientTokenModel。与用户令牌不同Client-Token 默认有效期同样为两小时clientTokenTimeout但只与 Client 绑定、不与具体用户绑定。若希望模式四的返回结果更贴合 OAuth2 RFC 规范同时返回access_token字段而非client_token可开启配置mode4ReturnAccessToken。六、源码视角这些 API 背后的实现要点6.1、统一路由分发所有 OAuth2 接口由SaOAuth2ServerProcessor.dister()按请求路径统一分发见 SaOAuth2ServerProcessor.java未匹配到任何接口时返回{msg: not handle}。这意味着你可以在自己的框架Spring Boot、Solon、JFinal 等中仅用一个通配路由即可挂载全部 OAuth2 能力。6.2、授权模式的开关控制四种授权模式默认全部开启均可在 SaOAuth2ServerConfig.java 中关闭配置字段控制模式默认值enableAuthorizationCode授权码式Authorization CodetrueenableImplicit隐藏式ImplicittrueenablePassword密码式PasswordtrueenableClientCredentials凭证式Client Credentialstrue即使系统层面开启了某模式每个 Client 还可以通过SaClientModel.allowGrantTypes单独控制自己支持哪些授权模式GrantType常量定义在 GrantType.java。双重校验不通过时会分别抛出CODE_30141系统未开放此模式或CODE_30142应用未开放此模式异常。6.3、redirect_uri 安全校验checkRedirectUri会对回调地址做多重校验见 SaOAuth2Template.java必须是一个有效的 URL截取掉?后面的部分后再参与匹配不允许出现字符含 URL 编码形式%40、%2540防止攻击者构造http://sa-oauth-client.com:123evil.com之类地址绕过白名单造成 code 参数劫持必须命中SaClientModel.allowRedirectUris允许地址列表。同时allowRedirectUris中的*通配符也有严格限制只能出现在末尾且其前一位必须是/或:如http://sa-oauth-client.com:*从源头堵住了子域名 通配符类绕过攻击见 SaOAuth2Template.java。更详细的域名白名单配置可参考 oauth2-check-domain.md。6.4、Client 应用信息模型SaClientModel见 SaClientModel.java是 Client 应用在 Server 端的身份证核心字段包括字段含义clientId/clientSecret应用 id 与应用秘钥contractScopes应用签约的所有权限allowRedirectUris应用允许授权的所有回调地址allowGrantTypes应用允许的授权模式列表isAutoConfirm是否允许自动确认授权高危配置默认 false禁止向不信任的第三方开启accessTokenTimeout等可按应用覆盖全局的 Token 有效期maxAccessTokenCount等单个应用/用户最多同时存在的 Token 数量默认 12应用信息由SaOAuth2DataLoader.getClientModel(clientId)提供官方 demo 通过 SaOAuth2DataLoaderImpl.java 实现当前为 Mock 数据真实环境需改为数据库查询。数据加载器相关扩展可参考 oauth2-data-loader.md。七、Client 端对接实战以官方 demo 为例官方仓库提供了完整的 Client 端对接样例 SaOAuthClientController.java其中约定的对接参数为private final String clientId 1001; // 应用id private final String clientSecret aaaa-bbbb-cccc-dddd-eeee; // 应用秘钥 private final String serverUrl http://sa-oauth-server.com:8000; // 服务端接口授权码登录/codeLogin携带grant_typeauthorization_code、code、client_id、client_secret调用 Server 端/oauth2/token成功后用返回的openid换取本地uid并调用StpUtil.login(uid)建立本地会话刷新令牌/refresh携带grant_typerefresh_token与refresh_token调用/oauth2/refresh密码式登录/passwordLogin携带grant_typepassword、username、password调用/oauth2/token凭证式/clientToken携带grant_typeclient_credentials调用/oauth2/client_token用户信息/getUserinfo携带access_token调用/oauth2/userinfo换取账号昵称、头像、性别等开放资源。前端 H5 授权页login.js则完整演示了授权流程的三态切换页面加载后先调用tryJump()探测——未登录返回 401展示登录框并调用/oauth2/doLogin申请的 scope 需要手动确认返回 411展示确认框并调用/oauth2/doConfirm追加build_redirect_uritrue已登录且已授权返回 200则直接用返回的redirect_uri完成跳转。该 H5 页面通过satokenheader 携带本地登录态Server 端 SaTokenConfigure.java 中配置了全局 CORS 支持与 OPTIONS 预检放行保证跨域调用可用。八、延伸阅读oauth2-server.mdOAuth2-Server 端搭建与配置总览oauth2-custom-grant_type.md自定义 grant_type含重写PasswordGrantTypeHandler的完整示例oauth2-custom-scope.md自定义 scope 权限处理器oauth2-scope-level.md高级权限 / 低级权限的确认规则oauth2-check-domain.md回调域名白名单配置oauth2-data-loader.md自定义数据加载器oauth2-apidoc.md本文对应的原始 API 文档【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考