'use client'
import { useEffect } from 'react'
import {
AppShell,
Group,
Text,
Button,
Avatar,
Paper,
Stack,
Badge,
Code,
Divider,
SimpleGrid,
Menu,
} from '@mantine/core'
import {
IconLogout,
IconUser,
IconShield,
IconServer,
IconKey,
} from '@tabler/icons-react'
interface UserInfo {
id: string
account: string
name: string
phone: string
roles: string[]
}
// 构建跳转 sso-portal 的登录 URL(OAuth2:app_id 即 client_id)
const SSO_APP_ID = process.env.NEXT_PUBLIC_SSO_APP_ID || ''
const SSO_URL = (process.env.NEXT_PUBLIC_SSO_URL || 'http://localhost:5501').replace(/\/+$/, '')
const APP_URL = (process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:4000').replace(/\/+$/, '')
const SSO_STATE_STORAGE_KEY = 'sso_login_state'
function buildSsoLoginUrl(): string {
const bytes = new Uint8Array(32)
window.crypto.getRandomValues(bytes)
const state = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')
const loginUrl = new URL(`${SSO_URL}/login`)
window.sessionStorage.setItem(SSO_STATE_STORAGE_KEY, state)
loginUrl.searchParams.set('app_id', SSO_APP_ID)
loginUrl.searchParams.set('app_url', `${APP_URL}/callback`)
loginUrl.searchParams.set('state', state)
return loginUrl.toString()
}
export default function DashboardPage({
user,
authenticated,
}: {
user: UserInfo | null
// 登录态以 token 为准(拿到 access_token 即算成功);user 可能为 null
authenticated: boolean
}) {
// 未登录(无 token)-> 跳转 SSO
useEffect(() => {
if (!authenticated) {
window.location.href = buildSsoLoginUrl()
}
}, [authenticated])
if (!authenticated) {
return (
正在跳转到 SSO 登录页面...
)
}
const handleLogout = async () => {
await fetch('/api/logout', { method: 'POST' })
// 跳到 SSO 登录页;每次登录都生成新的 state 防止登录 CSRF。
window.location.href = buildSsoLoginUrl()
}
return (
外部应用 Demo
已登录
登录成功!
您已通过 SSO 认证登录到此应用(OAuth2 授权码流程,已换取 access_token)。
用户信息
{user ? (
ID
{user.id}
账号
{user.account}
姓名
{user.name}
手机
{user.phone}
) : (
后端 access_token 接口未返回用户详情,暂无用户信息。
)}
角色权限
{user?.roles?.length ? (
{user.roles.map((role: string) => (
{role}
))}
) : (
暂无角色信息
)}
SSO 流程说明
1. 您打开此应用(http://localhost:4000)
2. 检测到未登录,自动跳转到 SSO(http://localhost:5501/login)
3. 在 SSO 页面完成登录,SSO 返回 sso_code
4. 此应用用 sso_code 调用后端 /api/v1/basis/sso/access_token 换取 access_token
5. access_token 存入 httpOnly Cookie,即算登录成功;用户详情需后端 userinfo 接口提供
}
onClick={handleLogout}>
退出登录(跳转回 SSO)
)
}