You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Pac4j 5.0.x中每次认证前动态更新Token Endpoint参数?

解决方案

在Pac4j 5.0.x的OIDC流程中,Token请求的构建逻辑位于OidcTokenGenerator类中,要动态更新Token Endpoint的fieldA参数,推荐通过自定义OidcTokenGenerator拦截Token请求构建过程,以下是具体实现步骤:

1. 自定义OidcTokenGenerator重写Token请求构建方法

创建自定义的Token生成器,在构建Token请求时动态修改Endpoint URI,添加或更新fieldA参数:

import org.pac4j.core.context.WebContext;
import org.pac4j.core.exception.TechnicalException;
import org.pac4j.core.http.HttpRequest;
import org.pac4j.oidc.token.OidcTokenGenerator;
import org.pac4j.oidc.config.OidcConfiguration;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

public class DynamicFieldAOidcTokenGenerator extends OidcTokenGenerator {

    public DynamicFieldAOidcTokenGenerator(OidcConfiguration configuration) {
        super(configuration);
    }

    @Override
    protected HttpRequest buildTokenRequest(String authorizationCode, WebContext context) {
        // 先调用父类方法构建原始请求
        HttpRequest tokenRequest = super.buildTokenRequest(authorizationCode, context);
        // 从当前请求上下文获取动态的fieldA值(示例从会话中取,可根据实际场景调整)
        String dynamicFieldA = getDynamicFieldAFromSession(context);
        
        try {
            URI originalUri = tokenRequest.getUri();
            String uriStr = originalUri.toString();
            // 处理URI参数:替换原有fieldA或新增参数
            if (uriStr.contains("fieldA=")) {
                uriStr = uriStr.replaceFirst("fieldA=[^&]*", "fieldA=" + URLEncoder.encode(dynamicFieldA, StandardCharsets.UTF_8.name()));
            } else {
                String separator = originalUri.getQuery() == null ? "?" : "&";
                uriStr += separator + "fieldA=" + URLEncoder.encode(dynamicFieldA, StandardCharsets.UTF_8.name());
            }
            // 更新请求URI
            tokenRequest.setUri(new URI(uriStr));
        } catch (URISyntaxException e) {
            throw new TechnicalException("Failed to update token endpoint URI", e);
        }
        
        return tokenRequest;
    }

    // 从会话中获取之前存储的动态fieldA值
    private String getDynamicFieldAFromSession(WebContext context) {
        return (String) context.getSessionStore().get(context, "dynamicFieldA");
    }
}

2. 在授权请求阶段存储动态fieldA值

如果fieldA是在用户发起认证请求时确定的,需要在授权跳转前将其存入会话,确保Token请求时能匹配到对应值。可以通过自定义OidcClient实现:

import org.pac4j.core.context.WebContext;
import org.pac4j.core.redirect.RedirectAction;
import org.pac4j.oidc.client.OidcClient;
import org.pac4j.oidc.config.OidcConfiguration;

public class DynamicFieldAOidcClient extends OidcClient {

    public DynamicFieldAOidcClient(OidcConfiguration configuration) {
        super(configuration);
    }

    @Override
    public RedirectAction redirect(WebContext context) {
        // 从当前请求参数中获取动态fieldA(可根据实际来源调整,比如请求头、业务参数等)
        String dynamicFieldA = context.getRequestParameter("dynamicFieldA");
        // 将值存入会话
        context.getSessionStore().set(context, "dynamicFieldA", dynamicFieldA);
        // 执行父类的授权跳转逻辑
        return super.redirect(context);
    }
}

3. 替换默认客户端和Token生成器

在项目的Pac4j配置代码中,使用自定义的客户端和Token生成器替换默认实现:

// 初始化OIDC配置
OidcConfiguration oidcConfiguration = new OidcConfiguration();
// 配置基础的Token Endpoint(不带fieldA参数)
oidcConfiguration.setTokenEndpointURI(new URI(appConfig.getTokenEndpoint()));
// 其他OIDC配置(客户端ID、密钥等)
// oidcConfiguration.setClientId("your-client-id");
// oidcConfiguration.setSecret("your-client-secret");

// 使用自定义客户端
DynamicFieldAOidcClient oidcClient = new DynamicFieldAOidcClient(oidcConfiguration);
// 设置自定义Token生成器
oidcClient.setTokenGenerator(new DynamicFieldAOidcTokenGenerator(oidcConfiguration));

// 将客户端添加到客户端工厂
Clients clients = new Clients("callback-url", oidcClient);
// 后续配置Config、Filter等流程保持不变

备选方案:动态生成ProviderMetadata的TokenEndpointURI

如果不想修改Token生成器,也可以通过扩展OidcProviderMetadata,重写getTokenEndpointURI()方法动态生成带参数的URI,但需要借助ThreadLocal传递请求上下文,注意线程安全:

import org.pac4j.oidc.metadata.OidcProviderMetadata;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.function.Supplier;

public class DynamicFieldAProviderMetadata extends OidcProviderMetadata {
    private final Supplier<String> fieldASupplier;
    private final URI originalTokenEndpoint;

    public DynamicFieldAProviderMetadata(OidcProviderMetadata original, Supplier<String> fieldASupplier) {
        // 复制原始元数据的所有属性
        this.setAuthorizationEndpointURI(original.getAuthorizationEndpointURI());
        this.setTokenEndpointURI(original.getTokenEndpointURI());
        this.setUserInfoEndpointURI(original.getUserInfoEndpointURI());
        this.setJwksURI(original.getJwksURI());
        // 其他元数据字段按需复制
        this.originalTokenEndpoint = original.getTokenEndpointURI();
        this.fieldASupplier = fieldASupplier;
    }

    @Override
    public URI getTokenEndpointURI() {
        try {
            String dynamicFieldA = fieldASupplier.get();
            String uriStr = originalTokenEndpoint.toString();
            String separator = originalTokenEndpoint.getQuery() == null ? "?" : "&";
            uriStr += separator + "fieldA=" + URLEncoder.encode(dynamicFieldA, StandardCharsets.UTF_8.name());
            return new URI(uriStr);
        } catch (URISyntaxException e) {
            throw new TechnicalException("Failed to generate dynamic token endpoint URI", e);
        }
    }
}

配置时需要结合ThreadLocal存储请求上下文,比如通过Filter将WebContext存入ThreadLocal,再在Supplier中获取。这种方式相对复杂,推荐优先使用第一种方案。

内容的提问来源于stack exchange,提问作者rithvik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 13:38:16