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

如何在Next.js中使用Keycloak实现自定义受保护路由及未认证重定向?

在Next.js中用Keycloak保护路由并实现未认证重定向

以下是几种适配不同Next.js路由架构的实现方案,你可以根据自己的项目选择:

一、Pages Router:使用高阶组件(HOC)

适合传统Pages Router项目,通过包裹页面组件实现保护。

1. 完善AuthProvider的上下文逻辑

确保AuthContext能提供认证状态和登录方法:

// contexts/AuthContext.js
import { createContext, useContext, useEffect, useState } from 'react';
import Keycloak from 'keycloak-js';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [keycloak, setKeycloak] = useState(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const initKeycloak = async () => {
      const kc = new Keycloak('/keycloak.json'); // 你的Keycloak配置文件路径
      try {
        const authenticated = await kc.init({
          onLoad: 'check-sso', // 静默检查已有会话
          silentCheckSsoRedirectUri: `${window.location.origin}/silent-check-sso.html`
        });
        setKeycloak(kc);
        setIsAuthenticated(authenticated);
      } catch (err) {
        console.error('Keycloak初始化失败', err);
      } finally {
        setLoading(false);
      }
    };
    initKeycloak();
  }, []);

  const login = () => {
    // 登录后跳回当前受保护路由
    keycloak.login({ redirectUri: `${window.location.origin}${window.location.pathname}` });
  };

  if (loading) return <div>加载中...</div>;

  return (
    <AuthContext.Provider value={{ isAuthenticated, login }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

2. 编写路由保护高阶组件

// hoc/withAuth.js
import { useEffect } from 'react';
import { useAuth } from '../contexts/AuthContext';

const withAuth = (WrappedComponent) => {
  return (props) => {
    const { isAuthenticated, login } = useAuth();

    useEffect(() => {
      if (!isAuthenticated) login();
    }, [isAuthenticated, login]);

    if (!isAuthenticated) return null; // 跳转前可替换为加载提示

    return <WrappedComponent {...props} />;
  };
};

export default withAuth;

3. 保护目标路由

在pages/contact.js中使用HOC包裹页面:

// pages/contact.js
import withAuth from '../hoc/withAuth';

const ContactPage = () => {
  return <div>仅认证用户可见的联系页面</div>;
};

export default withAuth(ContactPage);

二、全局路由保护:使用Next.js Middleware

适合同时保护多个路由,在服务器端拦截请求,适配Pages/App Router。

在项目根目录创建middleware.js:

// middleware.js
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 定义需要保护的路由列表
  const protectedRoutes = ['/contact'];
  const isProtected = protectedRoutes.some(route => request.nextUrl.pathname.startsWith(route));

  if (!isProtected) return NextResponse.next();

  // 检查Keycloak会话Cookie(名称需匹配你的Keycloak配置)
  const hasValidSession = request.cookies.get('KEYCLOAK_SESSION')?.value;

  if (!hasValidSession) {
    // 构造Keycloak登录URL,登录后跳回原路由
    const loginUrl = new URL('/auth/realms/你的领域名/protocol/openid-connect/auth', 'https://你的Keycloak域名');
    loginUrl.searchParams.set('client_id', '你的客户端ID');
    loginUrl.searchParams.set('redirect_uri', `${request.nextUrl.origin}${request.nextUrl.pathname}`);
    loginUrl.searchParams.set('response_type', 'code');
    loginUrl.searchParams.set('scope', 'openid');

    return NextResponse.redirect(loginUrl);
  }

  return NextResponse.next();
}

// 指定Middleware生效的路由
export const config = {
  matcher: ['/contact'],
};

三、App Router:Server Component端保护

适配Next.js 13+的App Router架构,在服务器端直接检查会话。

1. 服务器端会话检查工具函数

// lib/auth.js
import Keycloak from 'keycloak-connect';
import { InMemoryStore } from 'keycloak-connect/stores';

// 初始化Keycloak服务器端实例
const keycloak = new Keycloak({ store: new InMemoryStore() }, {
  clientId: '你的客户端ID',
  realm: '你的领域名',
  authServerUrl: 'https://你的Keycloak域名/auth',
});

export async function getServerSession(req) {
  return new Promise((resolve) => {
    keycloak.authenticated(req, {}, () => {
      resolve(req.kauth?.grant || null);
    });
  });
}

2. 保护目标路由页面

在app/contact/page.js中编写Server Component:

// app/contact/page.js
import { redirect } from 'next/navigation';
import { getServerSession } from '../lib/auth';

export default async function ContactPage() {
  const session = await getServerSession();

  if (!session) {
    // 重定向到自定义登录页或直接跳Keycloak
    redirect('/login');
  }

  return <div>仅认证用户可见的联系页面</div>;
}

3. 登录页触发Keycloak登录

// app/login/page.js
'use client';

import { useEffect } from 'react';
import { useAuth } from '@/contexts/AuthContext';

export default function LoginPage() {
  const { login } = useAuth();

  useEffect(() => {
    login();
  }, [login]);

  return <div>正在跳转到登录页面...</div>;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 02:34:51