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

在Spring Authorization Server中通过自定义AuthenticationProvider获取用户认证的clientId

获取登录时的ClientId并调用外部服务验证

实现步骤

要在自定义AuthenticationProvider中获取登录时的clientId,并调用外部服务验证关联的用户密码,可按以下方式实现:

1. 从Authentication的Details中获取ClientId

Spring Security会将请求相关信息封装到Authentication的details属性中,类型为WebAuthenticationDetails,通过它可以拿到当前请求对象,进而提取client_id参数:

@Component
public class AuthenticationCallout implements AuthenticationProvider {
    private static final Logger LOG = LoggerFactory.getLogger(AuthenticationCallout.class);

    @Autowired
    private JpaOAuth2AuthorizationService jpaOAuth2AuthorizationService;
    private final WebClient.Builder webClientBuilder;

    // 构造注入WebClient.Builder
    public AuthenticationCallout(WebClient.Builder webClientBuilder) {
        this.webClientBuilder = webClientBuilder;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        LOG.info("authenticate with username and password");

        final String username = authentication.getName();
        final String password = authentication.getCredentials().toString();

        // 获取clientId
        WebAuthenticationDetails authDetails = (WebAuthenticationDetails) authentication.getDetails();
        HttpServletRequest request = authDetails.getHttpServletRequest();
        String clientId = request.getParameter("client_id");

        if (clientId == null || clientId.isBlank()) {
            throw new BadCredentialsException("Client ID is required");
        }

        // 调用外部服务验证用户名、密码与clientId的关联关系
        Boolean isValid = webClientBuilder.build()
                .post()
                .uri("http://your-external-service/api/auth/verify")
                .bodyValue(Map.of(
                        "username", username,
                        "password", password,
                        "clientId", clientId
                ))
                .retrieve()
                .onStatus(HttpStatus::is4xxClientError, response -> 
                        Mono.error(new BadCredentialsException("Invalid authentication request")))
                .onStatus(HttpStatus::is5xxServerError, response -> 
                        Mono.error(new InternalAuthenticationServiceException("External service unavailable")))
                .bodyToMono(Boolean.class)
                .block(); // 若允许异步,可改用subscribe配合Mono返回,需调整方法逻辑

        if (Boolean.TRUE.equals(isValid)) {
            // 构建授权信息
            List<GrantedAuthority> grantedAuths = List.of(new SimpleGrantedAuthority("ROLE_USER"));
            UserDetails principal = new User(username, password, grantedAuths);
            LOG.info("Authentication successful for user: {}", username);
            return new UsernamePasswordAuthenticationToken(principal, password, grantedAuths);
        } else {
            throw new BadCredentialsException("Invalid username, password or client ID");
        }
    }

    // 必须实现supports方法,指定该Provider处理UsernamePasswordAuthenticationToken类型的认证
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

2. 关键注意事项

  • 不要返回null:认证失败时需抛出AuthenticationException的子类(如BadCredentialsException),Spring Security会统一处理失败流程,返回正确的错误响应。
  • 异步优化:若系统允许异步处理,建议将block()改为异步调用(如subscribe()),避免阻塞请求线程,提升服务吞吐量。
  • 参数校验:必须校验clientId是否存在,避免因参数缺失导致后续调用失败。
  • 异常处理:对外部服务的调用需添加异常处理,覆盖客户端错误和服务端错误场景,避免未捕获异常导致请求中断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:18:08