我尝试做一个简单的授权控制,其中“管理员”用户可以访问所有页面,而“经理”用户只能访问主页和建议页面。
然而,我没想到使用next和firebase创建这样的东西会如此困难,我已经尝试了一个多星期,我读了很多关于customClaims的文章,但我无法根据我的需要实现它。
My UserDialogCreate.tsx:
//... my imports
interface UserCreate {
name: string;
email: string;
password: string;
role: string;
}
const createUserSchema = Yup.object({
email: Yup.string().required("Email não pode ficar em branco"),
password: Yup.string().required("Senha não pode ficar em branco"),
});
export function UserDialogCreate() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [passwordsMatch, setPasswordsMatch] = useState(true);
const [role, setRole] = useState("manager");
const [isNewUserModalOpen, setIsNewUserModalOpen] = useState(false);
function handleOpenNewUserModal() {
setIsNewUserModalOpen(true);
}
async function handleCreateNewUser(e: FormEvent) {
e.preventDefault();
if (password === confirmPassword) {
setPasswordsMatch(true);
await createUserSchema
.validate({
email,
password,
confirmPassword,
})
.then(() => {
createUser({
name,
email,
password,
role: role.toLowerCase(),
});
setName("");
setEmail("");
setPassword("")
setConfirmPassword("")
setRole("manager");
})
.catch((error) => {
toast.error(error.message);
});
} else {
setPasswordsMatch(false);
}
}
async function createUser(userInput: UserCreate) {
try {
const userCredential = await createUserWithEmailAndPassword(auth, userInput.email, userInput.password);
const customClaims = {
role: userInput.role
};
if (userInput.role === "admin") {
customClaims.role = "admin";
} else if (userInput.role === "manager") {
customClaims.role = "manager";
}
// await setCustomUserClaims(userCredential.user, customClaims);
const userRef = ref(realTimeDatabase, "users");
const newUser = {
name: userInput.name,
email: userInput.email,
role: userInput.role,
createdAt: new Date().toString(),
};
await push(userRef, newUser);
setIsNewUserModalOpen(false);
toast.success("Usuário cadastrado com sucesso");
} catch (error) {
console.log(error)
toast.error("Ocorreu um erro ao cadastrar o usuário: " + error);
}
}
return (
<Dialog open={isNewUserModalOpen} onOpenChange={setIsNewUserModalOpen}>
<DialogTrigger asChild>
<Button onClick={handleOpenNewUserModal} className="bg-blue-800 text-gray-100 cursor-pointer">
Criar Usuário
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Criar Usuário</DialogTitle>
</DialogHeader>
<form onSubmit={handleCreateNewUser}>
//... form
<Button className='w-full'>Criar</Button>
</form>
</DialogContent>
</Dialog>
)
}
字符串
我的钩子/useAuth.tsx:
"use client"
import Cookie from "js-cookie"
import { User, onAuthStateChanged, signInWithEmailAndPassword, signOut } from "firebase/auth"
import { ReactNode, createContext, useContext, useEffect, useState } from "react"
import { auth } from "../lib/firebaseConfig"
import { useRouter } from "next/navigation"
import { toast } from "react-toastify"
interface CustomUser extends User {
customClaims: {
role: string;
};
}
interface HandleLoginProps {
email: string
password: string
}
interface AuthContextData {
handleLogin: (data: HandleLoginProps) => Promise<void>
handleLogout: () => void
isLoggedIn: boolean
currentUser: any | null
isLoading: boolean
userRole: string | null;
}
interface AuthProviderProps {
children: ReactNode;
}
const AuthContext = createContext<AuthContextData>({} as AuthContextData)
export function AuthProvider({ children }: AuthProviderProps) {
const [currentUser, setCurrentUser] = useState<any | null>(null)
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(false)
const [userRole, setUserRole] = useState<string | null>(null);
const router = useRouter()
async function handleLogin({ email, password }: HandleLoginProps) {
try {
Cookie.set('auth_token', 'ckjnehcnhbecevbiutveiuvtvtuvhvxgvwiutve')
const userCredential = await signInWithEmailAndPassword(auth, email, password)
setIsLoggedIn(true)
setCurrentUser(userCredential.user)
router.push("/")
toast.success("Seja bem vindo, " + email)
} catch (error) {
setError(true)
toast.error("E-mail e/ou senha incorretos.")
if (error instanceof Error) {
console.log(error.message)
} else {
console.log("Unexpected error: " + error)
}
}
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user: User | null) => {
if (user) {
const customUser = user as CustomUser;
setCurrentUser(customUser)
setIsLoading(false)
setUserRole(customUser.customClaims?.role || null);
} else {
setIsLoggedIn(false)
}
})
return () => {
unsubscribe();
};
}, [])
return (
<AuthContext.Provider value={{ currentUser, isLoggedIn, userRole, isLoading, handleLogin, handleLogout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext)
if (!context) {
throw new Error('useAuth must be used within an AuthProvider component')
}
return context
}
型
我的侧边栏.tsx:
"use client"
import React, { forwardRef } from 'react';
import { User2, Home, ScrollText, Folders, FileCode2 } from "lucide-react";
import Link from "next/link";
import { useAuth } from "@/hooks/useAuth";
interface SidebarProps extends React.HTMLProps<HTMLDivElement> {
showNav: boolean;
}
const Sidebar = forwardRef<HTMLDivElement, SidebarProps>((props, ref) => {
const { currentUser, userRole } = useAuth()
console.log("USER SIDEBAR: " + currentUser)
console.log("USER SIDEBAR 2: " + userRole)
return (
<div ref={ref} className="fixed w-60 h-full bg-gray-200 shadow-sm">
<div className="flex justify-center mt-6 mb-14">
<picture>
<img
className="w-32 h-auto"
src="/small-blue.png"
alt="company logo"
/>
</picture>
</div>
<div className="flex flex-col">
<Link href="/">
<div
className={`pl-6 py-3 mx-5 rounded text-center cursor-pointer mb-3 flex items-center transition-colors text-blue-900 hover:bg-gray-100 hover:text-purple-500`}
>
<div className="mr-2">
<Home className="h-5 w-5" />
</div>
<div>
<p>Home</p>
</div>
</div>
</Link>
<Link href="/proposals">
<div
className={`pl-6 py-3 mx-5 rounded text-center cursor-pointer mb-3 flex items-center transition-colors text-blue-900 hover:bg-gray-100 hover:text-purple-500`}
>
<div className="mr-2">
<FileCode2 className="h-5 w-5" />
</div>
<div>
<p>Gerar Propostas</p>
</div>
</div>
</Link>
{userRole === "admin" ? (
<>
<Link href="/services">
<div
className={`pl-6 py-3 mx-5 rounded text-center cursor-pointer mb-3 flex items-center transition-colors text-blue-900 hover:bg-gray-100 hover:text-purple-500`}
>
<div className="mr-2">
<ScrollText className="h-5 w-5" />
</div>
<div>
<p>Serviços</p>
</div>
</div>
</Link>
<Link href="/services-types">
<div
className={`pl-6 py-3 mx-5 rounded text-center cursor-pointer mb-3 flex items-center transition-colors text-blue-900 hover:bg-gray-100 hover:text-purple-500`}
>
<div className="mr-2">
<Folders className="h-5 w-5" />
</div>
<div>
<p>Tipos de Serviços</p>
</div>
</div>
</Link>
<Link href="/users">
<div
className={`pl-6 py-3 mx-5 rounded text-center cursor-pointer mb-3 flex items-center transition-colors text-blue-900 hover:bg-gray-100 hover:text-purple-500`}
>
<div className="mr-2">
<User2 className="h-5 w-5" />
</div>
<div>
<p>Usuários</p>
</div>
</div>
</Link>
</>
) : ""}
</div>
</div>
)
});
Sidebar.displayName = "SideBar";
export default Sidebar;
型
基本上,我有一个通过Firebase Realtime创建的用户,当创建一个新用户时,这也在Firebase Auth中注册。一切都很好,除了路由部分,我不能让管理员用户只访问主页和建议。我认为因为useRole为null:/
如果有人想帮助我,他们可以去:
https://proposals-mayarabecker-dev.vercel.app/
12345678
或
12345678
我的源代码在这里:
https://bitbucket.org/eltin182/proposals-mayarabecker-dev/src/main/frontend/的
我希望我解释了我的问题和困难,如果你想要更多的信息,你可以问
1条答案
按热度按时间svgewumm1#
在
Sidebar.tsx
文件中,您是否尝试将user
和customUser
变量记录到控制台,以查看它是否包含预期的用户详细信息。