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

如何通过Spring Boot API对接Keycloak实现用户登录(授权码模式)

Spring Boot + React 基于Keycloak授权码模式的后端中心化认证实现流程

一、Keycloak 端前置配置

  1. 创建专属Realm(例如命名为my-app-realm)
  2. 创建Confidential类型客户端
    • 客户端ID:backend-client
    • 访问类型:authorization_code
    • 有效重定向URI:填写后端回调地址,例如http://localhost:8080/api/auth/callback
    • Web Origins:填写前端域名,例如http://localhost:3000
    • 禁用"直接访问授权"(Direct Access Grants Enabled),强制走授权码流程
  3. 创建测试用户,设置用户名、密码并启用

二、Spring Boot 后端实现

1. 依赖引入(pom.xml)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-core</artifactId>
    </dependency>
</dependencies>

2. 配置文件(application.yml)

spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: backend-client
            client-secret: <你的客户端密钥>
            scope: openid,profile,email
            authorization-grant-type: authorization_code
        provider:
          keycloak:
            issuer-uri: http://localhost:8080/realms/my-app-realm
  session:
    store-type: redis # 生产环境建议用Redis,开发环境可默认内存
server:
  port: 8080

3. 认证控制器实现

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    // 前端触发登录:跳转到Keycloak授权页(后端处理重定向)
    @GetMapping("/login")
    public RedirectView login() {
        return new RedirectView("/oauth2/authorization/keycloak");
    }

    // Keycloak授权回调:完成认证后重定向到前端首页,携带会话标识
    @GetMapping("/callback")
    public RedirectView callback(@AuthenticationPrincipal OAuth2User oAuth2User, HttpServletRequest request) {
        String sessionId = request.getSession().getId();
        return new RedirectView("http://localhost:3000?sessionId=" + sessionId);
    }

    // 前端获取当前用户信息
    @GetMapping("/me")
    public Map<String, Object> getCurrentUser(@AuthenticationPrincipal OAuth2User oAuth2User) {
        if (oAuth2User == null) {
            throw new RuntimeException("未登录");
        }
        return oAuth2User.getAttributes();
    }

    // 登出:清除后端会话并调用Keycloak登出接口
    @GetMapping("/logout")
    public RedirectView logout(HttpServletRequest request) {
        request.getSession().invalidate();
        String logoutUrl = "http://localhost:8080/realms/my-app-realm/protocol/openid-connect/logout?redirect_uri=http://localhost:3000";
        return new RedirectView(logoutUrl);
    }
}

4. 安全配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .antMatchers("/api/auth/login", "/api/auth/callback").permitAll()
                        .anyRequest().authenticated()
                )
                .oauth2Login(oauth2 -> oauth2
                        .loginPage("/api/auth/login")
                        .defaultSuccessUrl("/api/auth/callback")
                )
                .logout(logout -> logout
                        .logoutUrl("/api/auth/logout")
                        .invalidateHttpSession(true)
                );
        return http.build();
    }
}

三、React 前端对接

前端无需集成Keycloak JS SDK,仅需对接后端接口:

1. 登录逻辑

// 登录按钮点击事件
const handleLogin = () => {
    window.location.href = 'http://localhost:8080/api/auth/login';
};

2. 处理登录回调(首页组件)

import { useEffect, useState } from 'react';

function Home() {
    const [userInfo, setUserInfo] = useState(null);

    useEffect(() => {
        // 从URL参数获取sessionId
        const params = new URLSearchParams(window.location.search);
        const sessionId = params.get('sessionId');
        if (sessionId) {
            // 存储sessionId到localStorage
            localStorage.setItem('sessionId', sessionId);
            // 获取用户信息
            fetch('http://localhost:8080/api/auth/me', {
                credentials: 'include' // 携带会话Cookie
            })
            .then(res => res.json())
            .then(data => setUserInfo(data));
        }
    }, []);

    // 登出逻辑
    const handleLogout = () => {
        window.location.href = 'http://localhost:8080/api/auth/logout';
    };

    return (
        <div>
            {userInfo ? (
                <>
                    <h1>欢迎,{userInfo.name}</h1>
                    <button onClick={handleLogout}>登出</button>
                </>
            ) : (
                <button onClick={handleLogin}>登录</button>
            )}
        </div>
    );
}

export default Home;

四、常见问题说明

  • 不要使用admin-cli做用户认证:admin-cli是Keycloak的管理工具接口,用于Realm/用户管理,不适合前端用户认证场景,必须用OAuth2客户端的授权码模式流程。
  • 跨域问题:后端需配置CORS,允许前端域名的请求携带凭证,可在Spring Boot中添加CorsFilter配置。
  • 会话管理:开发环境可使用内存会话,生产环境建议用Redis等分布式存储,避免单点故障。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:22:44