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

基于Keycloak实现第三方应用SSO的方案咨询及最佳实践建议

基于Keycloak实现我方应用与第三方REST接口的SSO方案(Spring Boot + Next.js + Docker栈)

核心流程说明

用户在我方应用(Spring Boot后端+Next.js前端)通过Keycloak完成登录后,我方应用获取Keycloak颁发的Access Token,调用第三方REST接口时将该Token放在请求头中,第三方应用验证Token有效性后即可授权访问,无需用户再次注册登录。


实现步骤

1. Keycloak配置

1.1 配置第三方应用的资源服务器客户端

  • 在Keycloak控制台创建新客户端,客户端类型选择bearer-only(该类型仅负责验证Token,不处理登录跳转)。
  • 设置客户端ID(如third-party-api),并在设置中配置受众(Audience)为该客户端ID,确保Token的aud字段匹配。
  • 保存后,该客户端将用于第三方应用验证Token的合法性。

1.2 配置我方应用的客户端

  • Next.js前端客户端:
    • 创建类型为public的客户端,配置有效重定向URI(如http://your-nextjs-domain/*,适配Docker部署的实际域名)、Web Origins(允许跨域的地址,如http://your-nextjs-domain)。
    • 为客户端添加必要的客户端范围(Client Scopes):至少包含openid、profile,若第三方需要自定义用户信息,可添加自定义范围并配置对应的声明。
  • Spring Boot后端客户端:
    • 创建类型为confidential的客户端,生成并保存客户端密钥。
    • 配置有效重定向URI(如http://your-springboot-domain/login/oauth2/code/keycloak),同样添加必要的客户端范围。

1.3 Docker部署下的Keycloak额外配置

  • 启动Keycloak容器时,设置KEYCLOAK_FRONTEND_URL为外部可访问的地址(如https://your-keycloak-domain/auth),避免容器内部地址导致前端登录跳转失败。
  • 确保Keycloak与我方应用容器在同一Docker网络内,或配置正确的端口映射与域名解析。

2. 我方应用改造

2.1 Next.js前端实现

使用keycloak-js库完成登录与Token获取,调用第三方接口时携带Token:

import Keycloak from 'keycloak-js';

// 初始化Keycloak实例
const keycloak = new Keycloak({
  url: 'https://your-keycloak-domain/auth',
  realm: 'your-realm-name',
  clientId: 'your-nextjs-client-id'
});

// 页面加载时自动完成登录
(async () => {
  const authenticated = await keycloak.init({ onLoad: 'login-required' });
  if (authenticated) {
    console.log('用户已登录,Token:', keycloak.token);
  }
})();

// 调用第三方REST接口
const fetchThirdPartyData = async () => {
  try {
    const response = await fetch('https://third-party-api-domain/your-endpoint', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${keycloak.token}`
      }
    });
    return await response.json();
  } catch (error) {
    console.error('调用第三方接口失败:', error);
  }
};

2.2 Spring Boot后端实现

通过Spring Security OAuth2客户端获取Token,转发给第三方接口:

  1. 引入依赖(pom.xml):
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 配置application.yml:
spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            issuer-uri: https://your-keycloak-domain/auth/realms/your-realm-name
        registration:
          keycloak:
            client-id: your-springboot-client-id
            client-secret: your-springboot-client-secret
            scope: openid,profile
  1. 调用第三方接口的代码示例:
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;

@RestController
public class ThirdPartyApiController {

    private final OAuth2AuthorizedClientService authorizedClientService;
    private final RestTemplate restTemplate;

    public ThirdPartyApiController(OAuth2AuthorizedClientService authorizedClientService, RestTemplate restTemplate) {
        this.authorizedClientService = authorizedClientService;
        this.restTemplate = restTemplate;
    }

    @GetMapping("/call-third-party")
    public ResponseEntity<String> callThirdPartyApi(OAuth2AuthenticationToken authentication) {
        // 获取当前用户的Keycloak授权客户端
        OAuth2AuthorizedClient client = authorizedClientService.loadAuthorizedClient(
                "keycloak", authentication.getName());
        String accessToken = client.getAccessToken().getTokenValue();

        // 设置请求头携带Token
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(accessToken);
        HttpEntity<String> entity = new HttpEntity<>(headers);

        // 调用第三方接口
        return restTemplate.exchange(
                "https://third-party-api-domain/your-endpoint",
                org.springframework.http.HttpMethod.GET,
                entity,
                String.class);
    }
}

3. 第三方应用对接

第三方应用需要验证Keycloak颁发的Token合法性,以下是两种常见场景:

3.1 第三方为Spring Boot应用

直接配置为OAuth2资源服务器:

spring:
  security:
    oauth2:
      resource-server:
        jwt:
          issuer-uri: https://your-keycloak-domain/auth/realms/your-realm-name
          audience: third-party-api # 需与Keycloak中第三方客户端的ID一致

Spring Security会自动验证Token的签名、issuer、audience、过期时间等。

3.2 第三方为非Java栈应用

手动实现JWT验证逻辑:

  • 从Keycloak的https://your-keycloak-domain/auth/realms/your-realm-name/protocol/openid-connect/certs端点获取公钥。
  • 验证Token的签名是否有效,检查iss字段是否为我方Keycloak的Realm地址,aud字段是否匹配第三方的客户端ID,同时检查Token是否过期。

最佳实践建议

  • 强制HTTPS通信:Keycloak、我方应用、第三方接口全部使用HTTPS,防止Token在传输过程中被窃取。
  • 最小权限原则:为我方客户端分配仅满足第三方接口需求的Scope和角色,避免在Token中携带多余的用户敏感信息。
  • 缓存Keycloak公钥:第三方应用不要每次验证Token都请求公钥,缓存公钥并定期更新,提升验证性能。
  • 合理设置Token生命周期:将Access Token过期时间设为15-30分钟,使用Refresh Token自动刷新,平衡安全性与用户体验。
  • Docker网络隔离:将Keycloak容器放在内部私有网络,仅暴露必要端口,避免直接暴露到公网。
  • 角色映射与权限控制:若第三方应用需要特定权限,在Keycloak中配置角色映射,将我方用户的角色转换为第三方认可的角色,通过Token传递。
  • 开启审计日志:启用Keycloak的审计日志功能,记录Token发放、验证等操作,便于后续问题排查与安全审计。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 11:35:39