Keycloak 的架构与应用

Table of Contents

Keycloak 的架构与应用

一、什么是 Keycloak?

Keycloak 是一个开源的身份与访问管理(IAM)解决方案,它帮我们搞定登录、单点登录(SSO)、注册、社交登录、MFA、多租户等等——简单说,就是把“怎么登录”的锅交给它,我们专注于写业务代码就行了。

你可以把它想象成“统一认证中心”,你应用里所有关于“用户是谁”“有没有权限”的问题,都可以丢给它来处理。

Keycloak 支持哪些协议?

  • OAuth 2.0:现代认证的主流协议,像 GitHub 登录那套就是它。
  • OIDC(OpenID Connect):在 OAuth 2.0 上封装一层,用于获取“用户是谁”的信息。
  • SAML 2.0:主要是企业、老系统在用。

二、Keycloak 架构一图流

+-------------+          +--------------------+          +-------------+
|   浏览器    |  <--->   |    前端 Vue 应用     |  <--->   |             |
+-------------+          +--------------------+          |             |
                                                           |  Keycloak   |
+-------------+          +--------------------+          |             |
|   Python    |  <--->   |     后端 FastAPI     |  <--->   |             |
+-------------+          +--------------------+          +-------------+
  • 前端跳转 Keycloak 登录;
  • 登录成功后,拿到 token;
  • 后端用这个 token 验证身份、获取用户信息。

三、安装 Keycloak(容器版)

用 Docker 安装最简单,以下是快速上手命令:

docker run -d \
  --name keycloak \
  -p 8080:8080 \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:24.0.1 \
  start-dev

然后访问:http://localhost:8080
使用 admin/admin 登录 Keycloak 管理控制台。

四、配置 Realm、Client 和用户

  1. 登录控制台,创建一个 Realm(可以理解成一个“用户空间”);
  2. 在 Realm 下创建一个 Client(比如叫 my-app);
    • 类型:OpenID Connect
    • Client ID: my-app
    • Root URL: http://localhost:5173
    • Valid Redirect URIs: http://localhost:5173/*
  3. 创建用户并设置密码;
  4. 给 Client 启用 标准流(Authorization Code Flow)
  5. 打开 Credentials 记下 Client Secret

五、后端(Python FastAPI 示例)

安装依赖:

pip install fastapi uvicorn python-dotenv httpx

创建 .env 文件:

KEYCLOAK_CLIENT_ID=my-app
KEYCLOAK_CLIENT_SECRET=你的密钥
KEYCLOAK_REALM=your-realm
KEYCLOAK_URL=http://localhost:8080
REDIRECT_URI=http://localhost:8000/auth/callback

创建 main.py

import os
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import RedirectResponse, JSONResponse
from dotenv import load_dotenv
import httpx

load_dotenv()

app = FastAPI()

CLIENT_ID = os.getenv("KEYCLOAK_CLIENT_ID")
CLIENT_SECRET = os.getenv("KEYCLOAK_CLIENT_SECRET")
REALM = os.getenv("KEYCLOAK_REALM")
KEYCLOAK_URL = os.getenv("KEYCLOAK_URL")
REDIRECT_URI = os.getenv("REDIRECT_URI")

@app.get("/auth/login")
def login():
    return RedirectResponse(
        f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/auth"
        f"?client_id={CLIENT_ID}&response_type=code&redirect_uri={REDIRECT_URI}"
    )

@app.get("/auth/callback")
async def callback(code: str):
    async with httpx.AsyncClient() as client:
        token_resp = await client.post(
            f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/token",
            data={
                "client_id": CLIENT_ID,
                "client_secret": CLIENT_SECRET,
                "code": code,
                "grant_type": "authorization_code",
                "redirect_uri": REDIRECT_URI,
            },
            headers={"Content-Type": "application/x-www-form-urlencoded"},
        )
        token_json = token_resp.json()
        access_token = token_json.get("access_token")
        return RedirectResponse(f"http://localhost:5173?access_token={access_token}")

@app.get("/api/me")
async def get_user(request: Request):
    auth = request.headers.get("Authorization")
    if not auth:
        raise HTTPException(status_code=401, detail="Missing token")

    token = auth.split(" ")[1]
    async with httpx.AsyncClient() as client:
        userinfo_resp = await client.get(
            f"{KEYCLOAK_URL}/realms/{REALM}/protocol/openid-connect/userinfo",
            headers={"Authorization": f"Bearer {token}"}
        )
        return userinfo_resp.json()

六、前端(Vue.js)

安装 Vue 项目

npm init vue@latest
cd your-project
npm install axios

修改 App.vue

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'

const user = ref(null)

const login = () => {
  window.location.href = 'http://localhost:8000/auth/login'
}

const logout = () => {
  localStorage.removeItem('token')
  user.value = null
}

const fetchUser = async () => {
  const token = localStorage.getItem('token')
  if (!token) return

  try {
    const res = await axios.get('http://localhost:8000/api/me', {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    })
    user.value = res.data
  } catch {
    logout()
  }
}

onMounted(() => {
  const params = new URLSearchParams(window.location.search)
  const token = params.get('access_token')
  if (token) {
    localStorage.setItem('token', token)
    window.history.replaceState({}, '', '/')
  }
  fetchUser()
})
</script>

<template>
  <div>
    <h1>Keycloak 登录示例</h1>
    <div v-if="user">
      <p>你好,{{ user.name || user.preferred_username }}!</p>
      <button @click="logout">退出登录</button>
    </div>
    <div v-else>
      <button @click="login">使用 Keycloak 登录</button>
    </div>
  </div>
</template>

七、总结

Keycloak 就像你项目的“门神”,它守住大门,确保“什么人能进”“进来了是谁”。通过标准协议(OAuth、OIDC)对接,你不需要重复造轮子,就能让你的系统拥有现代化的用户身份管理功能。

本文通过一个 Vue + FastAPI 的小例子,带你跑通了 Keycloak 的一个最基本应用场景。后续你可以探索更多特性,比如:

  • 社交登录(Google、GitHub 一键接入)
  • 多因素认证(MFA)
  • 自定义用户界面
  • 与企业 AD、LDAP 集成

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

Comments |0|

Legend *) Required fields are marked
**) You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Category: cloud