Spring-Boot+Angular+Keycloak PKCE授权码流重复获取AccessToken问题
问题分析与解决方案
先明确核心结论:OAuth2的授权码规范要求同一个授权码只能用一次,所以你说的“重复用同一授权码获取新token”绝对不是正常行为——要么是你误解了Keycloak适配器的操作流程,要么是配置出了问题。
1. 刷新/跳转页面重复走第三步的原因
你当前配置的onLoad: 'login-required',会让Keycloak的JS适配器在页面加载时强制校验认证状态:
- 如果适配器在本地找不到有效的token(比如没开持久化、token过期),就会触发认证流程;
- 但因为Keycloak已经通过会话Cookie识别了你是已登录状态,所以会直接跳过登录页,返回新的授权码(不是旧的),再用这个新码去换token。
你误以为是同一授权码,大概率是没注意每次的code参数都是不一样的——抓包看一下就清楚了。如果真的是同一个授权码被重复使用,Keycloak应该直接返回invalid_grant错误,那你就得检查Keycloak客户端的安全配置了。
2. 实现“一次获取token重复使用”的配置
要让适配器把token存在本地,避免每次刷新都重新走授权流程,需要给initOptions加两个关键配置:
function initializeKeycloak(keycloak: KeycloakService) { return () => keycloak.init({ config: { url: environment.keycloakurl, realm: 'realm', clientId: 'client', }, initOptions: { onLoad: 'login-required', // 把token存在sessionStorage里,浏览器关闭就清除,也可以选localStorage持久化 tokenStorage: 'sessionStorage', // 配置静默刷新的跳转页面,token快过期时自动刷新,不用用户操作 silentCheckSsoRedirectUri: window.location.origin + '/assets/silent-check-sso.html' } }); }
另外要在Angular项目的assets文件夹下创建silent-check-sso.html文件,内容如下:
<!DOCTYPE html> <html> <body> <script> parent.postMessage(location.href, location.origin); </script> </body> </html>
3. 必须注意的安全细节
- 授权码一次性验证:Keycloak默认是开着这个机制的,绝对别关,不然授权码被别人拿到就能重复换token,完全违反OAuth2的安全设计;
- token存储选择:
sessionStorage比localStorage更安全,因为关闭浏览器就清掉了,适合不需要“记住我”的场景;如果要用户下次打开浏览器还能自动登录,再选localStorage; - 静默刷新:配了
silentCheckSsoRedirectUri之后,适配器会在token快过期时自动发起静默授权,偷偷换好新token,用户完全没感知。
如果确认真的是同一授权码被重复用,去Keycloak控制台检查:
进入你的Realm → 客户端 → 你的客户端 → 高级设置 → 授权码策略,确保选的是「一次性」(One Time Use)。
内容的提问来源于stack exchange,提问作者kawlorridor
相关产品推荐
相关产品推荐

