Sa-Token OAuth2 Server 端二次开发完全指南:SaOAuth2Util 全量 API 与 SaOAuth2Strategy 可重写策略详解
【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token
本篇指南面向需要在 Sa-Token OAuth2 Server 端(即认证中心/授权服务器)进行二次开发的开发者。官方示例只提供了最基础的授权流程和userinfo资源开放,当你需要开放更多资源接口、校验 Access-Token、回收凭证、自定义授权页面、扩展 grant_type 或自定义 scope 处理逻辑时,就需要用到本文将要系统讲解的SaOAuth2Util全量 API 与SaOAuth2Strategy全部可重写策略。读完本文,你将能够熟练使用工具类完成凭证查询、校验与回收,并通过重写策略深度定制认证中心的每一处行为。
本文对应的原始官方文档为 sa-token-doc/oauth2/oauth2-dev.md,核心源码位于 SaOAuth2Util.java 与 SaOAuth2Strategy.java,读者可对照阅读。
一、二次开发的两大入口:工具类与策略类
在深入 API 细节之前,先明确 Sa-Token-OAuth2 模块提供给二次开发者的两个核心入口,它们的职责截然不同:
| 入口 | 类名 | 职责 | 源码位置 |
|---|---|---|---|
| 查询/校验/回收 API | SaOAuth2Util | 提供全部静态方法,对模板类SaOAuth2Template的薄封装 | template/SaOAuth2Util.java |
| 可重写策略 | SaOAuth2Strategy | 提供全部默认行为实现,开发者可整体替换 | strategy/SaOAuth2Strategy.java |
从源码结构看,SaOAuth2Util中的每个静态方法都只是将调用委托给SaOAuth2Manager.getTemplate()返回的模板对象,真正的业务逻辑集中在SaOAuth2Template中;而SaOAuth2Template又进一步委托给四个可替换的组件:SaOAuth2Dao(数据持久化)、SaOAuth2DataLoader(应用信息加载)、SaOAuth2DataResolver(请求数据解析)、SaOAuth2DataGenerate(凭证生成)。理解这条委托链,有助于判断"某个能力应该在哪一层扩展":
二次开发代码 │ ├── SaOAuth2Util(静态工具方法,查、校验、回收) │ └── SaOAuth2Manager.getTemplate() → SaOAuth2Template │ └── SaOAuth2Manager.getDao() / getDataLoader() / getDataResolver() / getDataGenerate() │ └── SaOAuth2Strategy.instance(重写默认行为,鉴权加工、凭证生成、认证流程回调) └── 被 SaOAuth2Template 与 DataGenerate 在各关键节点回调委托链的完整实现可参考 SaOAuth2Template.java 与 SaOAuth2Manager.java。
二、Client 信息相关 API:应用注册信息的查询与校验
Client(客户端应用)是 OAuth2 体系中的"第三方应用"。二次开发中几乎所有接口的第一步都是拿到并校验 Client 信息。
// 获取 ClientModel,根据 clientId(查不到时返回 null,不抛异常) SaOAuth2Util.getClientModel(clientId); // 校验 clientId 信息并返回 ClientModel,如果找不到对应 Client 信息则抛出异常 SaOAuth2Util.checkClientModel(clientId); // 校验:clientId 与 clientSecret 是否正确(不正确则抛出异常) SaOAuth2Util.checkClientSecret(clientId, clientSecret); // 校验:clientId 与 clientSecret 是否正确,并且是否签约了指定 scopes(任一条件不满足则抛出异常) SaOAuth2Util.checkClientSecretAndScope(clientId, clientSecret, scopes); // 判断:该 Client 是否签约了指定的 Scope,返回 true 或 false(不抛异常) SaOAuth2Util.isContractScope(clientId, scopes); // 校验:该 Client 是否签约了指定的 Scope,如果没有则抛出异常(按 clientId 查) SaOAuth2Util.checkContractScope(clientId, scopes); // 校验:该 Client 是否签约了指定的 Scope,如果没有则抛出异常(直接传 ClientModel,省一次查询) SaOAuth2Util.checkContractScope(clientModel, scopes); // 校验:该 Client 使用指定 url 作为回调地址,是否合法(不合法则抛出异常) SaOAuth2Util.checkRedirectUri(clientId, url); // 判断:指定 loginId 是否对一个 Client 授权给了指定 Scope(返回布尔值) SaOAuth2Util.isGrantScope(loginId, clientId, scopes); // 删除:指定 loginId 针对指定 Client 的授权信息(即"解除授权") SaOAuth2Util.deleteGrantScope(loginId, clientId);2.1 底层校验逻辑与异常码
从 SaOAuth2Template.java 源码可以看到这些方法的真实行为:
getClientModel直接委托SaOAuth2Manager.getDataLoader().getClientModel(clientId),数据来源由开发者实现的SaOAuth2DataLoader决定(示例实现见 demo 中的SaOAuth2DataLoaderImpl);checkClientModel在查不到时抛出SaOAuth2ClientModelException,错误码CODE_30105("无效 client_id");checkClientSecret在秘钥不匹配时抛出SaOAuth2ClientModelException,错误码CODE_30115("无效 client_secret");checkContractScope遍历 scopes 逐个检查是否包含在clientModel.contractScopes中,缺失时抛出SaOAuth2ClientModelScopeException,错误码CODE_30112("该 client 暂未签约 scope");isContractScope内部通过 try/catch 捕获checkContractScope的异常来返回布尔值;isGrantScope读取SaOAuth2Manager.getDao().getGrantScope(clientId, loginId)的授权记录,再用SaFoxUtil.list1ContainList2AllElement判断请求的 scopes 是否全部已授权;deleteGrantScope删除 DAO 层保存的用户授权记录。
各异常码的完整定义可查阅 SaOAuth2ErrorCode.java。
2.2 checkRedirectUri 的安全校验细节(建议精读)
checkRedirectUri是 OAuth2 安全的关键防线,源码中包含了四道校验(SaOAuth2Template.java):
- 先通过
SaFoxUtil.isUrl(url)校验是否是一个有效 URL; - 截取掉
?之后的查询参数部分; - 禁止 URL 中出现
@字符及其编码形式%40、%2540——否则攻击者可通过http://sa-oauth-client.com:123@evil.com之类的构造绕过 allow-url 白名单,造成 code 参数劫持; - 判断 URL 是否命中
clientModel.allowRedirectUris允许列表。
同时,checkRedirectUriListNormal静态方法会对开发者配置的 allow-url 列表本身做合规性检查(SaOAuth2Template.java):
*通配符只允许出现在地址最后一位(如http://sa-oauth-client.com:*),出现在中间位置(如http://*.sa-oauth-client.com/)会被判定为无效配置——否则攻击者可构造http://sa-token.com/a.sa-oauth-client.com/绕过校验;*出现在末尾时,其前一位必须是/或:,禁止写成http://domain*的形式——否则攻击者可构造http://sa-oauth-client.com.evil.com/callback绕过校验。
这两处安全设计都带有详尽的注释与攻击示例,值得二次开发者阅读原文注释,避免在自定义回调校验逻辑时引入类似漏洞。
三、Code 相关 API:授权码的查询与校验
授权码(Code)是授权码模式下"第一步授权"的产物,有效期默认为 5 分钟(codeTimeout),用于换取 Access-Token。
// 获取 CodeModel,无效的 code 会返回 null(不抛异常) SaOAuth2Util.getCode(code); // 校验 Code,成功返回 CodeModel,失败则抛出异常 SaOAuth2Util.checkCode(code); // 获取 Code,根据索引:clientId、loginId(即某应用下某用户当前有效的授权码) SaOAuth2Util.getCodeValue(clientId, loginId);getCode/checkCode底层调用SaOAuth2Manager.getDao().getCode(code);checkCode在查不到时抛出SaOAuth2AuthorizationCodeException,错误码CODE_30110("无效 code");CodeModel承载的字段包括:code(授权码)、clientId(应用 id)、scopes(授权范围)、loginId(对应账号 id)、redirectUri(重定向地址)、nonce(随机数)、createTime(13 位时间戳创建时间),完整定义见 CodeModel.java。
四、Access-Token 相关 API:访问凭证的全生命周期管理
Access-Token 是 OAuth2 中客户端调用资源接口时携带的核心凭证,其默认有效期 2 小时(accessTokenTimeout)。二次开发开放自定义资源接口时,这一组 API 使用频率最高。
// 获取 AccessTokenModel,无效的 AccessToken 会返回 null(不抛异常) SaOAuth2Util.getAccessToken(accessToken); // 校验 Access-Token,成功返回 AccessTokenModel,失败则抛出异常 SaOAuth2Util.checkAccessToken(accessToken); // 获取 Access-Token 列表:此应用下对某个用户签发的所有 Access-Token SaOAuth2Util.getAccessTokenValueList(clientId, loginId); // 判断:指定 Access-Token 是否具有指定 Scope 列表,返回 true 或 false(不抛异常) SaOAuth2Util.hasAccessTokenScope(accessToken, ...scopes); // 校验:指定 Access-Token 是否具有指定 Scope 列表,如果不具备则抛出异常 SaOAuth2Util.checkAccessTokenScope(accessToken, ...scopes); // 获取 Access-Token 所代表的 LoginId(登录账号 id) SaOAuth2Util.getLoginIdByAccessToken(accessToken); // 获取 Access-Token 所代表的 clientId(应用 id) SaOAuth2Util.getClientIdByAccessToken(accessToken); // 回收一个 Access-Token SaOAuth2Util.revokeAccessToken(accessToken); // 回收全部 Access-Token:指定应用下指定用户的全部 Access-Token SaOAuth2Util.revokeAccessTokenByIndex(clientId, loginId);4.1 各方法底层实现要点
checkAccessToken查不到时抛出SaOAuth2AccessTokenException,错误码CODE_30106;checkAccessTokenScope内部先checkAccessToken再逐个检查at.scopes.contains(scope),缺失时抛出SaOAuth2AccessTokenScopeException,错误码CODE_30108;hasAccessTokenScope同样通过捕获异常返回布尔值;getLoginIdByAccessToken/getClientIdByAccessToken都是对checkAccessToken结果字段的直接读取(at.loginId、at.clientId),因此传入无效 token 时同样会抛异常;revokeAccessToken会先删除凭证本体deleteAccessToken,再删除索引deleteAccessTokenIndex_BySingleData(SaOAuth2Template.java);revokeAccessTokenByIndex通过getAccessTokenValueList拿到该用户在该应用下的全部 token 列表,逐个删除后删除索引(对应源码 L547-L559);AccessTokenModel提供了两个实用的剩余有效期方法:getExpiresIn()(Access-Token 剩余秒数,-1表示永不过期)、getRefreshExpiresIn()(Refresh-Token 剩余秒数)。完整字段还包括accessToken、refreshToken、expiresTime、refreshExpiresTime、clientId、loginId、scopes、tokenType、grantType、extraData、createTime,见 AccessTokenModel.java。
五、Refresh-Token 相关 API:刷新凭证的查询与回收
Refresh-Token 用于在 Access-Token 过期后静默换取新令牌,默认有效期 30 天(refreshTokenTimeout)。
// 获取 RefreshTokenModel,无效的 RefreshToken 会返回 null(不抛异常) SaOAuth2Util.getRefreshToken(refreshToken); // 校验 Refresh-Token,成功返回 RefreshTokenModel,失败则抛出异常 SaOAuth2Util.checkRefreshToken(refreshToken); // 获取 Refresh-Token 列表:此应用下对某个用户签发的所有 Refresh-Token SaOAuth2Util.getRefreshTokenValueList(clientId, loginId); // 回收一个 Refresh-Token SaOAuth2Util.revokeRefreshToken(refreshToken); // 回收全部 Refresh-Token:指定应用下指定用户的全部 Refresh-Token SaOAuth2Util.revokeRefreshTokenByIndex(clientId, loginId); // 根据 RefreshToken 刷新出一个 AccessToken(核心方法,返回新的 AccessTokenModel) SaOAuth2Util.refreshAccessToken(refreshToken);checkRefreshToken查不到时抛出SaOAuth2RefreshTokenException,错误码CODE_30111;refreshAccessToken委托给SaOAuth2Manager.getDataGenerate().refreshAccessToken(refreshToken),最终由SaOAuth2DataGenerate的默认实现完成"销毁旧令牌、签发新令牌"的整套流程,其中是否在刷新时同时产生新的 Refresh-Token 由isNewRefresh配置控制;- 回收逻辑与 Access-Token 完全对称:先删凭证本体、再删单条索引(
revokeRefreshToken),或按 clientId + loginId 批量回收(revokeRefreshTokenByIndex)。
六、Client-Token 相关 API:凭证式(Client Credentials)专用凭证
Client-Token 服务于 OAuth2 凭证式模式(对应配置项enableClientCredentials),它只代表"客户端应用"本身,不绑定具体用户,因此按 clientId 维度管理,默认有效期 2 小时(clientTokenTimeout)。
// 获取 ClientTokenModel,无效的 ClientToken 会返回 null(不抛异常) SaOAuth2Util.getClientToken(clientToken); // 校验 Client-Token,成功返回 ClientTokenModel,失败则抛出异常 SaOAuth2Util.checkClientToken(clientToken); // 获取 Client-Token 列表:此应用下签发的所有 Client-Token(注意:不需要 loginId 参数) SaOAuth2Util.getClientTokenValueList(clientId); // 判断:指定 Client-Token 是否具有指定 Scope 列表,返回 true 或 false SaOAuth2Util.hasClientTokenScope(clientToken, ...scopes); // 校验:指定 Client-Token 是否具有指定 Scope 列表,如果不具备则抛出异常 SaOAuth2Util.checkClientTokenScope(clientToken, ...scopes); // 回收一个 ClientToken SaOAuth2Util.revokeClientToken(clientToken); // 回收全部 Client-Token:指定应用下的全部 Client-Token SaOAuth2Util.revokeClientTokenByIndex(clientId);checkClientToken查不到时抛出SaOAuth2ClientTokenException,错误码CODE_30107;checkClientTokenScope缺失 scope 时抛出SaOAuth2ClientTokenScopeException,错误码CODE_30109;- 索引维护按
clientId单维度进行(deleteClientTokenIndex_BySingleData、deleteClientTokenIndex),与 Access-Token / Refresh-Token 的"clientId + loginId"双维度索引形成对比。
七、请求查询 API:从当前请求中直接读取凭证
当资源接口需要直接从当前 HTTP 请求中解析凭证时,可以使用这两个方法,它们会依次尝试"请求参数"与"请求头"两种携带方式:
// 数据读取:从当前请求对象中读取 access_token,并查询到 AccessTokenModel 信息,无效 access_token 抛出异常 // 1、请求参数 access_token 2、请求头 Authorization: Bearer access_token SaOAuth2Util.currentAccessToken(); // 数据读取:从当前请求对象中读取 client_token,并查询到 ClientTokenModel 信息,无效 client_token 抛出异常 // 1、请求参数 client_token 2、请求头 Authorization: Bearer client_token SaOAuth2Util.currentClientToken();其实现(SaOAuth2Template.java)是:先用SaOAuth2Manager.getDataResolver().readAccessToken(SaHolder.getRequest())从请求对象中解析出 token 字符串,再交给checkAccessToken/checkClientToken完成查询与校验。也就是说,解析规则(参数名、请求头格式)由SaOAuth2DataResolver决定,校验规则由模板方法决定,两部分都可以按需定制。
实战示例:开放一个自定义 userinfo 资源接口
下面这段代码取自官方示例 SaOAuth2ResourcesController.java,完整演示了"读取凭证 → 获取账号 → 校验 scope → 返回用户数据"的标准流程,是二次开发开放新资源接口的模板:
@RestController public class SaOAuth2ResourcesController { // 示例:获取 userinfo 信息:昵称、头像、性别等等 @RequestMapping("/oauth2/userinfo") public SaResult userinfo() { // 获取 Access-Token 对应的账号id String accessToken = SaOAuth2Manager.getDataResolver().readAccessToken(SaHolder.getRequest()); Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken); System.out.println("-------- 此Access-Token对应的账号id: " + loginId); // 校验 Access-Token 是否具有权限: userinfo SaOAuth2Util.checkAccessTokenScope(accessToken, "userinfo"); // 模拟账号信息(真实环境需要查询数据库获取信息) Map<String, Object> map = new LinkedHashMap<>(); // map.put("userId", loginId); 一般原则下,oauth2-server 不能把 userId 返回给 oauth2-client map.put("nickname", "林小林"); map.put("avatar", "http://xxx.com/1.jpg"); map.put("age", "18"); map.put("sex", "男"); map.put("address", "山东省 青岛市 城阳区"); return SaResult.ok().setMap(map); } }参照此模式,开放任意新接口只需三步:checkAccessTokenScope校验权限 → 从AccessTokenModel或 DAO 中读取业务数据 → 返回结果。
八、SaOAuth2Strategy:OAuth2 Server 端全部可重写策略
SaOAuth2Strategy.instance是全局单例,包含全部默认行为实现。重写某个字段,即可替换对应环节的默认逻辑。下面按原文档的分类逐一说明。
8.1 权限处理器(Scope 加工)
这一组策略负责"根据 token 携带的 scope 列表,对凭证模型进行加工",典型场景是给 token 填充 openid、unionid、userid 等派生字段:
// 根据 scope 信息对一个 AccessTokenModel 进行加工处理(签发 Access-Token 时执行) SaOAuth2Strategy.instance.workAccessTokenByScope = at -> { // at 为 AccessTokenModel,可通过 at.scopes 判断后向 at.extraData 填充扩展数据 // ... }; // 当使用 RefreshToken 刷新 AccessToken 时,根据 scope 信息对一个 AccessTokenModel 进行加工处理 SaOAuth2Strategy.instance.refreshAccessTokenWorkByScope = at -> { // ... }; // 根据 scope 信息对一个 ClientTokenModel 进行加工处理 SaOAuth2Strategy.instance.workClientTokenByScope = ct -> { // ct 为 ClientTokenModel // ... };从源码看,默认实现并不是简单地"空操作",而是维护了一个scopeHandlerMap权限处理器集合(SaOAuth2Strategy.java):遍历 token 的 scopes,从scopeHandlerMap中取出对应的SaOAuth2ScopeHandlerInterface处理器并调用。默认注册了四个处理器(对应CommonScope常量):
OPENID→OpenIdScopeHandlerUNIONID→UnionIdScopeHandlerUSERID→UserIdScopeHandlerOIDC→OidcScopeHandler
同时框架预留了_FINALLY_WORK_SCOPE特殊 key 作为"最终兜底处理器"。开发者既可以直接重写上面三个策略字段实现整体替换,也可以调用registerScopeHandler(...)/removeScopeHandler(...)注册或移除单个 scope 处理器(demo 中提供了自定义UserinfoScopeHandler、CustomOidcScopeHandler的示例,位于sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/custom_scope/目录)。
注意refreshAccessTokenWorkByScope与workAccessTokenByScope的默认行为存在差异:前者只会调用refreshAccessTokenIsWork()返回 true 的处理器,用于区分"首次签发"与"刷新签发"两种场景下的加工差异。
8.2 grant_type 处理器
grantTypeAuth是 OAuth2 Server 处理"换取 token"请求的总入口,默认实现已经完成了相当完整的校验链路:
// 根据 grantType 构造一个 AccessTokenModel SaOAuth2Strategy.instance.grantTypeAuth = req -> { // req 为 RequestAuthModel,包含 grant_type、client_id、client_secret、scope 等参数 // 默认实现执行流程(见源码 L186-L215): // 1. 校验 grant_type 是否在 grantTypeHandlerMap 中注册(否则抛 CODE_30126) // 2. 校验全局开关 enableAuthorizationCode / enablePassword 是否打开 // 3. 校验 clientSecret 与 scope 是否合法(checkClientSecretAndScope) // 4. 检测该应用 allowGrantTypes 是否包含此 grant_type(否则抛 CODE_30141) // 5. 调用对应的 grantTypeHandler.getAccessToken(req, clientId, scopes) 构建 token // ... };与 scope 处理器类似,默认注册了三个 grant_type 处理器(见registerDefaultGrantTypeHandler):authorization_code(授权码)、password(密码式)、refresh_token(刷新)。二次开发可通过registerGrantTypeHandler(...)/removeGrantTypeHandler(...)扩展自定义 grant_type,官方 demo 的sa-token-demo-oauth2-server中提供了PhoneCodeGrantTypeHandler(手机验证码登录)与CustomPasswordGrantTypeHandler两个可直接参考的自定义示例。
8.3 凭证创建(自定义凭证值生成算法)
默认情况下,code、Access-Token、Refresh-Token、Client-Token 的值均由SaFoxUtil.getRandomString(60)生成 60 位随机字符串。如果你的业务要求特定的 token 格式(如 JWT 风格、带业务前缀),可重写这四个策略:
// 创建一个 code value SaOAuth2Strategy.instance.createCodeValue = (clientId, loginId, scopes) -> { // 自定义生成逻辑,例如:return "CODE-" + 自定义随机串; // ... }; // 创建一个 AccessToken value SaOAuth2Strategy.instance.createAccessToken = (clientId, loginId, scopes) -> { // ... }; // 创建一个 RefreshToken value SaOAuth2Strategy.instance.createRefreshToken = (clientId, loginId, scopes) -> { // ... }; // 创建一个 ClientToken value(注意参数没有 loginId,因为 Client-Token 不绑定用户) SaOAuth2Strategy.instance.createClientToken = (clientId, scopes) -> { // ... };需要说明的是:Sa-Token 的 JWT 集成能力由sa-token-jwt插件提供,并可通过SaOAuth2Strategy凭证创建策略与 OAuth2 模块结合使用(相关插件文档见 sa-token-doc/plugin/jwt-extend.md)。
8.4 认证流程回调(自定义登录与授权页面)
这一组策略控制"未登录跳转"、"授权确认页"、"登录校验"、"授权前检查"四个交互节点,是定制认证中心前端体验的核心:
// OAuth-Server端:未登录时返回的View // 默认返回字符串:"当前会话在 OAuth-Server 认证中心尚未登录" SaOAuth2Strategy.instance.notLoginView = () -> { // 可返回自定义页面内容,或通过 SaHolder.getResponse().redirect(...) 跳转登录页 // ... }; // OAuth-Server端:确认授权时返回的View // 默认返回字符串:"本次操作需要用户授权" SaOAuth2Strategy.instance.confirmView = (clientId, scopes) -> { // 可读取 clientId、scopes 渲染动态授权确认页面 // ... }; // OAuth-Server端:登录函数 // 默认返回 SaResult.error(),即默认拒绝一切登录 SaOAuth2Strategy.instance.doLoginHandle = (name, pwd) -> { // 示例:校验用户名密码后执行 StpUtil.login(账号id),并返回 SaResult.ok() // ... }; // OAuth-Server端:用户在授权指定 client 前的检查,如果检查不通过,请直接抛出异常 SaOAuth2Strategy.instance.userAuthorizeClientCheck = (loginId, clientId) -> { // 示例:if(被禁止的应用) throw new SaOAuth2Exception("该应用已被禁止授权"); // ... };官方示例的SaOAuth2ServerH5Controller与 H5 前端页面(位于sa-token-demo/sa-token-demo-oauth2/下)完整演示了如何重写notLoginView、confirmView、doLoginHandle构建一套可用的登录/授权 H5 界面,二次开发时可直接对照改造。
8.5 其它:SaClientModel 默认字段设置
最后一个策略用于在创建SaClientModel时填充其默认字段值,默认实现从全局配置SaOAuth2ServerConfig读取各类超时时间与数量上限:
// 在创建 SaClientModel 时,设置其默认字段 SaOAuth2Strategy.instance.setSaClientModelDefaultFields = (clientModel) -> { // 默认实现(源码 L279-L288): // clientModel.accessTokenTimeout = config.getAccessTokenTimeout(); // clientModel.refreshTokenTimeout = config.getRefreshTokenTimeout(); // clientModel.clientTokenTimeout = config.getClientTokenTimeout(); // clientModel.maxAccessTokenCount = config.getMaxAccessTokenCount(); // clientModel.maxRefreshTokenCount = config.getMaxRefreshTokenCount(); // clientModel.maxClientTokenCount = config.getMaxClientTokenCount(); // clientModel.isNewRefresh = config.getIsNewRefresh(); // ... };SaClientModel的完整字段集(SaClientModel.java)包括:clientId、clientSecret、contractScopes(签约权限)、allowRedirectUris(允许回调地址)、allowGrantTypes(允许的授权模式)、subjectId(主体 id)、各类超时与数量上限字段,以及isAutoConfirm(是否自动确认授权,源码注释明确标注为"高危配置,禁止向不被信任的第三方开启")。这些字段正是上一节中checkContractScope、checkRedirectUri、grantTypeAuth等校验所依赖的数据。
九、配置项默认值速查表
SaOAuth2ServerConfig(SaOAuth2ServerConfig.java)中与本篇 API 相关的默认值汇总如下,二次开发时应明确这些默认行为:
| 配置项 | 默认值 | 说明 |
|---|---|---|
codeTimeout | 300 秒(5 分钟) | Code 授权码有效期 |
accessTokenTimeout | 7200 秒(2 小时) | Access-Token 全局默认有效期 |
refreshTokenTimeout | 2592000 秒(30 天) | Refresh-Token 全局默认有效期 |
clientTokenTimeout | 7200 秒(2 小时) | Client-Token 全局默认有效期 |
maxAccessTokenCount | 12 | 单应用单用户最多并存 Access-Token 数 |
maxRefreshTokenCount | 12 | 单应用单用户最多并存 Refresh-Token 数 |
maxClientTokenCount | 12 | 单应用最多并存 Client-Token 数 |
isNewRefresh | false | 刷新 Access-Token 时是否同时生成新 Refresh-Token |
enableAuthorizationCode/enableImplicit/enablePassword/enableClientCredentials | 均 true | 四种授权模式的总开关 |
十、总结
Sa-Token OAuth2 Server 端的二次开发能力可以归纳为两条主线:
- 查、校验、回收走
SaOAuth2Util静态 API:覆盖 Client 信息、Code、Access-Token、Refresh-Token、Client-Token 五大对象的完整生命周期管理,配合currentAccessToken()/currentClientToken()可直接从当前请求解析凭证。这些方法底层统一委托给SaOAuth2Template,再由 DAO / DataLoader / DataResolver / DataGenerate 四个可替换组件分工实现,因此既可以直接调用,也可以深入到组件层做更大范围的定制。 - 改行为走
SaOAuth2Strategy.instance策略字段:从 scope 加工、grant_type 处理、凭证值生成,到登录/授权页面回调,共 11 个可重写策略覆盖了授权服务器的全部关键行为节点;scope 与 grant_type 还支持通过registerScopeHandler/registerGrantTypeHandler进行模块化扩展(demo 中即提供了自定义 scope 处理器与自定义 grant_type 处理器的完整示例)。
建议二次开发前先通读 SaOAuth2Util.java 与 SaOAuth2Strategy.java 的源码注释,再结合 SaOAuth2ServerController.java 与 SaOAuth2ResourcesController.java 两个官方示例对照实践,即可快速构建出自定义程度较高的 OAuth2 认证中心。OAuth2 Server 的整体架构与部署说明可进一步参考 sa-token-doc/oauth2/oauth2-server.md 与 sa-token-doc/oauth2/oauth2-apidoc.md。
【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考