ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Sa-Token OAuth2 集成常见问题排查指南:not handle、code 无效、多账号与 state 安全

Sa-Token OAuth2 集成常见问题排查指南:not handle、code 无效、多账号与 state 安全 Sa-Token OAuth2 集成常见问题排查指南not handle、code 无效、多账号与 state 安全【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token导读本文是 Sa-Token 官方 OAuth2 模块集成 FAQ 的深度展开版围绕开发者接入sa-token-oauth2时最常遇到的四类问题访问开放接口返回{msg: not handle}、授权码code提示无效、多账号体系下如何指定 OAuth2 使用的登录逻辑、授权码流程中state参数的安全作用。每个问题均结合 sa-token-plugin/sa-token-oauth2 模块的真实源码与路由实现进行解析读完即可对照排查自己的集成代码。一、访问返回{msg: not handle}路由地址写错了1.1 问题现象与直接原因搭建好 OAuth2-Server 服务后访问接口返回{msg: not handle}这代表你访问的路由未被 OAuth2 框架识别和处理。框架只会处理它注册过的固定开放地址地址一旦写错例如路径多了个字符、大小写不一致、或把?与参数拼错位置请求就不会进入任何处理逻辑。典型错误示例统一认证登录地址应为http://{host}:{port}/oauth2/authorize而实际访问的却是http://{host}:{port}/oauth2/authorize21.2 源码印证路由分发逻辑从源码看not handle响应并不是偶发错误而是请求处理器的“默认返回分支”。在 SaOAuth2ServerProcessor.java 的dister()方法中框架按固定路径依次分发// 模式一Code授权码 || 模式二隐藏式 if(req.isPath(Api.authorize)) { return authorize(); } // Code 换 Access-Token || 模式三密码式 if(req.isPath(Api.token)) { return token(); } // Refresh-Token 刷新 Access-Token if(req.isPath(Api.refresh)) { return refresh(); } // 回收 Access-Token if(req.isPath(Api.revoke)) { return revoke(); } // doLogin 登录接口 if(req.isPath(Api.doLogin)) { return doLogin(); } // doConfirm 确认授权接口 if(req.isPath(Api.doConfirm)) { return doConfirm(); } // 模式四凭证式 if(req.isPath(Api.client_token)) { return clientToken(); } // 默认返回 return SaOAuth2Consts.NOT_HANDLE;可以看到所有已注册路径都经过isPath(...)精确匹配匹配不上则落入最后的默认分支。而NOT_HANDLE常量的定义位于 SaOAuth2Consts.java/** 表示请求没有得到任何有效处理 {msg: not handle} */ public static final String NOT_HANDLE {\msg\: \not handle\};因此排查方向非常明确先核对访问地址是否与框架开放接口完全一致。1.3 完整开放接口清单OAuth2-Server 端暴露的标准/约定接口如下可对照 OAuth2 开放接口文档接口路径用途对应 OAuth2 模式/oauth2/authorize授权码获取、隐藏式授权模式一 / 模式二/oauth2/tokenCode 换 Access-Token、密码式认证、自定义 grant_type模式一 / 模式三/oauth2/refresh用 Refresh-Token 刷新 Access-Token模式一/oauth2/revoke回收 Access-Token模式一/oauth2/doLoginOAuth-Server 端登录非 OAuth2 标准协议接口登录辅助/oauth2/doConfirm用户确认授权非 OAuth2 标准协议接口授权辅助/oauth2/client_token获取 Client-Token模式四提示接口参数response_type、client_id、redirect_uri、scope、state等必须按 OAuth2 开放接口文档 中的参数表拼接缺失必填参数同样不会进入预期流程。在 SaOAuth2DataResolverDefaultImpl.java 中client_id、response_type、redirect_uri均通过getParamNotNull强制获取缺任一参数会直接抛出异常。二、一直提示code无效一个授权码只能用一次2.1 问题现象参照文档搭建 OAuth2-Server 后用获取到的code去换取 Access-Token一直报 code 无效。2.2 原因解析一个 code 码只能使用一次多次使用同一个 code 就会报无效。这是授权码Authorization Code模式的安全设计避免授权码被重放攻击。从 OAuth2 开放接口文档 对 Code 授权码特点的描述可以进一步理解其生命周期每次授权产生的Code码都不一样Code码用完即废不能二次使用一个Code的有效期默认为五分钟超时自动作废每次授权产生新Code码会导致旧Code码立即作废即使旧Code码尚未使用。2.3 排查建议确认是否重复使用了同一个 code例如刷新页面、或把 code 缓存后再次提交确认 code 是否已超过五分钟有效期从授权成功拿到 code 到换取 token 的间隔不宜过长确认流程中是否产生了新的 code例如用户重复发起授权导致旧 code 被新 code 顶掉确认换取 token 时提交的grant_typeauthorization_code、client_id、client_secret是否与授权时完全一致参考 授权码换取 Access-Token 接口。三、多账号模式下如何指定 OAuth2 使用的 StpLogic3.1 问题背景如果项目使用了 Sa-Token 的多账号体系例如用户表使用StpUserUtil管理员表使用StpAdminUtil需要明确 OAuth2 模块在“判断用户是否登录、签发 Token”时到底使用哪一套账号体系。3.2 解决方案setStpLogic在configOAuth2Server中指定 OAuth2 模块使用的StpLogic对象即可// Sa-Token OAuth2 定制化配置 Autowired public void configOAuth2Server(SaOAuth2ServerConfig oauth2Server) { // 其它配置 ... // 指定 oauth2 模块使用的 StpLogic 对象 SaOAuth2Manager.setStpLogic(StpUserUtil.stpLogic); }3.3 源码印证默认值与替换机制从 SaOAuth2Manager.java 可以看到SaOAuth2Manager内部持有全局的StpLogic引用默认值为StpUtil.stpLogic即默认的单账号体系并提供了线程安全的懒加载单例/** * OAuth2 StpLogic */ private static volatile StpLogic stpLogic; public static StpLogic getStpLogic() { if (stpLogic null) { synchronized (SaOAuth2Manager.class) { if (stpLogic null) { setStpLogic(StpUtil.stpLogic); } } } return stpLogic; } public static void setStpLogic(StpLogic stpLogic) { SaOAuth2Manager.stpLogic stpLogic; }该StpLogic在授权流程中承担“当前用户是否已登录”的判断例如 SaOAuth2ServerProcessor.authorize() 中// 如果尚未登录, 则先去登录 Object loginId SaOAuth2Manager.getStpLogic().getLoginIdDefaultNull(); if( loginId null) { return SaOAuth2Strategy.instance.notLoginView.get(); }所以只要通过SaOAuth2Manager.setStpLogic(...)换成目标账号体系的StpLogic整个 OAuth2 授权链路登录判断、确认授权、签发 token 关联的 loginId都会切换到该账号体系。适用前提StpUserUtil代表的是自定义多账号体系之一引入多账号能力后可参考 Sa-Token 的多账号文档在项目中新建StpUserUtil等账号工具类对应仓库中的 多账号文档。本示例以StpUserUtil为多账号体系示例实际可替换为任意账号体系的StpLogic对象。四、授权码流程中state参数是干吗用的4.1 核心作用防 CSRF / 账号伪装攻击state参数用于验证授权码流程的发起端和接受端是否为同一个客户端防止 OAuth-Server 账号伪装攻击即跨站请求伪造 / CSRF 攻击攻击者诱导已登录用户点击恶意链接从而在用户不知情的情况下完成授权。4.2 发起端必须保证的三条铁律随机性state参数必须足够随机不可被预测一一对应state参数与授权码流程发起客户端一一对应授权流程发起时创建的state必须与接受时返回的state值一致一次性安全起见一个state参数只允许使用一次。4.3 源码印证state 在重定向时原样回传从路由处理器 SaOAuth2ServerProcessor.java 可以看到授权成功后框架会把请求中携带的state原样拼接到重定向地址上回传给客户端// 如果是 授权码式则开始重定向授权下放code if(ResponseType.code.equals(ra.responseType)) { CodeModel codeModel dataGenerate.generateCode(ra); String redirectUri dataGenerate.buildRedirectUri(ra.redirectUri, codeModel.code, ra.state); return res.redirect(redirectUri); }state从请求中读取的位置在 SaOAuth2DataResolverDefaultImpl.javara.state req.getParam(Param.state);即发起授权时客户端生成一个随机 state 并随/oauth2/authorize请求提交授权完成后框架将其原样追加到redirect_uri上返回形如redirect_uri?code{code}state{state}。客户端在回调中必须校验返回的 state 与发起时保存的 state 是否一致一致才继续换取 token否则应终止流程。4.4 使用建议在发起授权前生成高强度随机字符串如 UUID、加密随机数作为 state将 state 与当前会话或客户端本地存储绑定授权回调时比对校验通过后立即失效该 state避免重放。五、FAQ 快速排查对照表问题核心原因快速解法访问返回{msg: not handle}访问路径不在框架开放接口清单内对照 OAuth2 开放接口文档 核对地址提示code无效code 一次性使用、五分钟后过期、或已被新 code 顶掉重新走一次完整授权流程及时用 code 换取 token多账号体系下 OAuth2 不生效OAuth2 仍在使用默认StpUtilSaOAuth2Manager.setStpLogic(目标账号体系.stpLogic)授权回调校验失败/被伪装攻击未使用或未校验state参数发起时生成随机 state回调时一一比对并一次性使用延伸阅读OAuth2 开放接口与参数详解四种授权模式的完整 URL 与参数表OAuth2 服务端搭建指南OAuth2-Server 初始化与配置OAuth2 开发详解模块设计原理与扩展点多账号模式StpUserUtil等多账号体系的构建方式官方集成测试可参考 OAuth2HttpIntegrationTest其中覆盖了完整的授权、换取 token、刷新与回收流程可作为行为对照。【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架让鉴权变得简单、优雅—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表