如何使用Keycloak-js在Next.js中实现Keycloak用户注册
实现Next.js自定义页面对接Keycloak自助注册
第一步:Keycloak后台配置(必须完成)
要允许匿名请求创建用户,需先在Keycloak控制台完成以下配置:
- 开启用户自助注册:进入目标Realm(AppName)→ 登录设置 → 勾选「允许用户注册」
- 配置匿名访问用户API:
- 进入客户端「appname-web」→ 权限标签页 → 开启「授权」开关
- 点击「添加权限」→ 选择「用户」资源类型,为
create权限设置策略为「匿名」
- 开启邮件验证:进入Realm设置 → 登录 → 勾选「要求电子邮件验证」,确保注册后自动发送验证邮件
- 配置跨域:进入客户端「appname-web」→ 设置 → Web Origins,添加你的Next.js应用地址(如
http://localhost:3000),开发环境可临时设为*,生产环境务必指定具体域名
第二步:前端实现方案
方案1:直接调用Keycloak REST API(推荐)
在Next.js客户端组件中编写注册逻辑,直接请求Keycloak的用户创建接口:
'use client'; import { useState } from 'react'; export default function RegisterPage() { const [formData, setFormData] = useState({ username: '', email: '', password: '', firstName: '', lastName: '' }); const handleSubmit = async (e) => { e.preventDefault(); try { const res = await fetch('http://localhost:8081/realms/AppName/users', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Origin': 'http://localhost:3000' }, body: JSON.stringify({ username: formData.username, email: formData.email, enabled: true, credentials: [{ type: 'password', value: formData.password, temporary: false // 设为true则用户首次登录需修改密码 }], firstName: formData.firstName, lastName: formData.lastName, emailVerified: false // 交由Keycloak发送验证邮件 }) }); if (res.ok) { alert('注册成功,请查收邮箱完成验证'); // 跳转至登录页 window.location.href = '/login'; } else { const error = await res.json(); alert(`注册失败:${error.errorMessage || '未知错误'}`); } } catch (err) { console.error('注册请求失败:', err); alert('网络异常,请稍后重试'); } }; return ( <div className="register-form"> <form onSubmit={handleSubmit}> <input type="text" placeholder="用户名" value={formData.username} onChange={(e) => setFormData({ ...formData, username: e.target.value })} required /> <input type="email" placeholder="邮箱" value={formData.email} onChange={(e) => setFormData({ ...formData, email: e.target.value })} required /> <input type="password" placeholder="密码" value={formData.password} onChange={(e) => setFormData({ ...formData, password: e.target.value })} required /> <input type="text" placeholder="名字" value={formData.firstName} onChange={(e) => setFormData({ ...formData, firstName: e.target.value })} required /> <input type="text" placeholder="姓氏" value={formData.lastName} onChange={(e) => setFormData({ ...formData, lastName: e.target.value })} required /> <button type="submit">完成注册</button> </form> </div> ); }
方案2:复用Keycloak-js配置
如果已初始化Keycloak实例,可复用其配置拼接API地址,避免硬编码:
'use client'; import { useState, useEffect } from 'react'; import Keycloak from 'keycloak-js'; export default function RegisterPage() { const [keycloak, setKeycloak] = useState(null); const [formData, setFormData] = useState({/* 同方案1的formData结构 */}); useEffect(() => { const kcInstance = new Keycloak({ url: "http://localhost:8081", realm: "AppName", clientId: "appname-web" }); setKeycloak(kcInstance); }, []); const handleSubmit = async (e) => { e.preventDefault(); if (!keycloak) return; try { const apiUrl = `${keycloak.authServerUrl}/realms/${keycloak.realm}/users`; const res = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Origin': 'http://localhost:3000' }, body: JSON.stringify({/* 同方案1的请求体 */}) }); // 后续处理同方案1 } catch (err) { console.error(err); } }; // 表单部分同方案1 }
进阶优化:Next.js API路由代理
生产环境建议通过Next.js的API路由代理请求,避免前端直接暴露Keycloak地址:
// app/api/register/route.js export async function POST(request) { const formData = await request.json(); const keycloakUrl = 'http://localhost:8081/realms/AppName/users'; try { const res = await fetch(keycloakUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: formData.username, email: formData.email, enabled: true, credentials: [{ type: 'password', value: formData.password, temporary: false }], firstName: formData.firstName, lastName: formData.lastName, emailVerified: false }) }); const data = await res.json(); return new Response(JSON.stringify(data), { status: res.status }); } catch (err) { return new Response(JSON.stringify({ error: '代理请求失败' }), { status: 500 }); } }
之后前端只需请求自己的API路由:
// 前端请求替换为: const res = await fetch('/api/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) });
注意事项
- 确保Keycloak的密码策略符合需求(Realm设置 → 安全 → 密码策略)
- 自定义验证邮件内容可在Keycloak控制台的Realm设置 → 邮件 → 模板中修改
- 生产环境务必关闭Web Origins的
*配置,指定具体允许的域名 - 如果需要额外字段,可在Keycloak的用户属性中添加,并在请求体中传入对应字段
内容的提问来源于stack exchange,提问作者Sérgio Serra
相关产品推荐
相关产品推荐

