如何通过Spring Boot API对接Keycloak实现用户登录(授权码模式)
Spring Boot + React 基于Keycloak授权码模式的后端中心化认证实现流程
一、Keycloak 端前置配置
- 创建专属Realm(例如命名为
my-app-realm) - 创建Confidential类型客户端
- 客户端ID:
backend-client - 访问类型:
authorization_code - 有效重定向URI:填写后端回调地址,例如
http://localhost:8080/api/auth/callback - Web Origins:填写前端域名,例如
http://localhost:3000 - 禁用"直接访问授权"(Direct Access Grants Enabled),强制走授权码流程
- 客户端ID:
- 创建测试用户,设置用户名、密码并启用
二、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
相关产品推荐
相关产品推荐

