Play-pac4j集成Keycloak OIDC直接访问授权时redirect_uri参数无效问题排查
Play-pac4j集成Keycloak OIDC直接访问授权时redirect_uri参数无效问题排查
嗨,我来帮你搞定这个问题~
首先咱们得搞清楚为啥会出现这个错误:你要用到的**直接访问授权(也就是OIDC里的Resource Owner Password Credentials Grant)**是一种非交互式的授权流程,后端直接向Keycloak的令牌端点请求令牌,本来就不需要redirect_uri参数。但play-pac4j的OidcClient默认逻辑里,可能还是会把这个参数带到请求里,而Keycloak在处理密码授权时会严格校验参数,发现不该有的redirect_uri就直接抛出了< Invalid parameter: redirect_uri>错误。
接下来给你一步步的解决办法,结合你贴的代码来调整:
1. 修改play-pac4j的OIDC配置,适配密码授权
你需要在OidcConfiguration里明确启用密码授权模式,并且禁用redirect_uri的自动传递,具体代码调整如下:
final OidcConfiguration oidcConfiguration = new OidcConfiguration(); oidcConfiguration.setDiscoveryURI(configuration.getString("oidc.discoveryUri")); oidcConfiguration.setClientId(configuration.getString("oidc.clientId")); // 密码授权模式下,Keycloak一般需要客户端密钥,所以把注释打开 oidcConfiguration.setSecret(configuration.getString("oidc.clientSecret")); oidcConfiguration.setClientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); oidcConfiguration.setExpireSessionWithToken(true); oidcConfiguration.setLogoutUrl("/logout"); // 关键配置1:启用密码授权模式 oidcConfiguration.setUsePasswordGrant(true); // 关键配置2:清空redirect_uri,避免请求中携带这个参数 oidcConfiguration.setRedirectUri(null); // PKCE在密码授权里本来就不需要,保留之前的配置没问题 oidcConfiguration.setDisablePkce(true); final OidcClient oidcClient = new OidcClient(oidcConfiguration); // 后面的AuthorizationGenerator配置可以保留 oidcClient.addAuthorizationGenerator((CallContext ctx, UserProfile profile) -> { // 你的授权逻辑 return profile; });
2. 检查Keycloak客户端的配置
光改代码还不够,得确保Keycloak那边允许用密码授权:
- 登录Keycloak后台,找到你的客户端,把Access Type设置为
confidential(因为密码授权需要客户端密钥做认证); - 切换到Advanced Settings -> Grant Types,确保勾选了
Password选项,这样Keycloak才会接受密码授权的请求; - 确认客户端没有强制要求
redirect_uri(不过密码授权场景下Keycloak本来就不会校验这个参数,只要咱们不传递就没事)。
3. 用密码授权的方式处理登录请求
因为密码授权是后端直接请求令牌,不需要前端跳转,所以你得在Play框架里配置对应的登录接口,用PasswordAuthenticator来处理用户名密码的校验:
比如在路由文件里加:
POST /login/password controllers.SecurityController.passwordLogin()
然后在SecurityController里实现这个方法:
public Result passwordLogin() { // 从请求里获取用户名和密码 Map<String, String[]> formData = request().body().asFormUrlEncoded(); String username = formData.get("username")[0]; String password = formData.get("password")[0]; UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); WebContext context = new PlayWebContext(request(), response()); try { // 调用play-pac4j的认证器,向Keycloak请求令牌 UserProfile profile = oidcClient.getCredentialsAuthenticator().validate(credentials, context); // 把用户信息存到会话里 new ProfileManager(context).save(profile); return redirect("/"); // 登录成功后跳转的页面 } catch (HttpAction e) { // 认证失败,返回错误 return unauthorized("用户名或密码错误"); } }
这样调整之后,请求就会直接向Keycloak的令牌端点发送密码授权请求,不会再携带redirect_uri参数,Keycloak也就不会报错啦~
备注:内容来源于stack exchange,提问作者Kaushal Senevirathne
相关产品推荐
相关产品推荐

