next.js中用于身份验证的next_auth和其他框架的替代方案



我正在尝试在Next.js上开发一个灵活的身份验证系统,该系统可以使用Spring(Java(API后端。端点使用Postman功能完美。API还提供自己的JWT。我希望使用API端点登录注册用户。这也意味着我需要一种方法来使用服务器上的JWT来验证试图登录的用户

遵循Next_auth和iron会话的文档非常令人困惑。API运行良好。Next_auth似乎特别为这种类型的身份验证提供了有限的支持。

我研究了很多帖子、教程,甚至发布了这个问题。这个问题最接近我想要理解的,但它涉及到登录后的状态,这个过程看起来有点混乱。这个问题似乎表明在Next上执行自定义身份验证非常复杂,最好使用框架。

我是遗漏了什么,还是让Next js与外部API和JWT一起工作非常复杂我不需要Next提供的全栈功能。我也不想被迫通过谷歌、推特、FB等进行身份验证。

我需要这样的东西,它是使用React创建的,并使用RESTneneneba API端点来登录注册用户和管理各自的用户会话。

var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");

var urlencoded = new URLSearchParams();
urlencoded.append("username", enteredEmail);
urlencoded.append("password", enteredPassword);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch(API_LOGIN_URL, requestOptions)
.then((res) => {
setIsLoading(false);
if (res.ok) {
return res.json();
} else {
return res.json().then((data) => {
let errorMessage = 'Authentication failed!';
throw new Error(errorMessage);
});
}
})
.then((data)=> {
authCtx.login(data.access_token);
const processedData = JSON.stringify(data);
console.log("Admin status "+ processedData);
for(let i = 0; i < processedData.length; i++) {
if(processedData.includes("ROLE_SUPER_ADMIN")) {
console.log("Found Admin"); 
authCtx.adminAccess(true);
} 
if(processedData.includes("ROLE_USER")) {
console.log("Found User");
break;
}
else {
console.log("Not Found");
}
}})
.catch((err) => {
alert(err.message);
});
}
};
return (
<section className={classes.auth}>
<h1>{isLogin ? 'Login' : 'Sign Up'}</h1>
<form onSubmit={submitHandler}>
<div className={classes.control}>
<label htmlFor='email'>Your Email</label>
<input type='email' id='email' required ref={emailInputRef} />
</div>
<div className={classes.control}>
<label htmlFor='password'>Your Password</label>
<input type='password' id='password' required ref={passwordInputRef} />
</div>
<div className={classes.actions}>
{!isLoading && <button>{isLogin ? 'Login' : 'Create Account'}</button>}
{isLoading && <p>Sending request</p>}
<button
type='button'
className={classes.toggle}
onClick={switchAuthModeHandler}
>
{isLogin ? 'Create new account' : 'Login with existing account'}
</button>
</div>
</form>
</section>
);
};
export default AuthForm;

我想在Next.js中做一些类似的事情,而不需要按照像Next_auth这样的框架/库的规则来工作。

我真的很感谢任何指导(建议、教程等(,解释如何使用post方法到API端点来查找用户名和密码。

我还想知道如何使用API生成的JWT来完成流程并对用户进行身份验证。我可以把这部分放在另一个问题中。对于这个问题,即使我知道如何使用我描述的API端点通过查找用户名和密码登录,我也会很高兴。在Next.js中,我只看到使用Next_auth或iron会话等框架进行身份验证。我还没有看到您在React中找到的那种类型的自定义身份验证方法(如上所述(。因此,我想知道:

我们必须使用Next_auth或iron会话进行身份验证吗?有没有任何自定义Next-js身份验证方法的例子不依赖于这些框架,并且可以很好地与后端API和JWT(如Spring(配合使用

提前感谢您的帮助。

在Dream_Cap的大力帮助下,他指导我阅读了一篇相关文章和他自己的node.js代码,答案是完全可以在不依赖任何框架(如next_auth(的情况下编写自定义身份验证方法。

解决方案的关键是,Next js可以使用React Context作为一个高阶组件(HOC(来保持身份验证状态,并在用户会话中保持相应的更改。这与使用[…nexttuth].js方法有些不同,后者旨在捕获所有请求。

这种替代方法基本上意味着你可以使用与普通React应用程序几乎相同的方法,但对Next.js上下文进行了轻微修改:

let logoutTimer;
let initialToken;
let initialAdminToken;
const AuthContext = React.createContext({
token: '',
admintoken: '',
isLoggedIn: false,
isAdmin: false,
login: (token) => { },
adminAccess: (admintoken) => { },
logout: () => { },
});
const calcTimeRemaining = (expirationTime) => {
const currentTime = new Date().getTime();
const adjExpireTime = new Date(expirationTime).getTime();
const remaingDuration = adjExpireTime - currentTime;
return remaingDuration;
}
export const AuthContextProvider = (props) => {
const authCtx = useContext(AuthContext);
const isAdmin = authCtx.isAdmin;
const [token, setToken] = useState(initialToken);
const [admintoken, setAdminToken] = useState(initialAdminToken);
const userIsLoggedIn = !!token;
const userHasAdmin = !!admintoken;

useEffect(() => {
initialToken = localStorage.getItem('token');
initialAdminToken = localStorage.getItem('admintoken');
if(initialAdminToken !== initialToken) {
setToken(initialToken);

} else {
setToken(initialToken);
setAdminToken(initialAdminToken);
}
}, [initialToken, initialAdminToken]);

const logoutHandler = () => {
setToken(null);
setAdminToken(null);
localStorage.removeItem('token');
localStorage.removeItem('admintoken');
};
const loginHandler = (token) => {
if(admintoken == null) {
setToken(token);

localStorage.setItem('token', token);
} else {
setToken(token);
localStorage.setItem('token', token);
setAdminToken(token);
localStorage.setItem('admintoken', token);
}
// const remainingTime = calcTimeRemaining(expirationTime);
setTimeout(logoutHandler, 300000);

};
const adminTokenHandler = (admintoken) => {
setAdminToken(admintoken);
localStorage.setItem('admintoken', admintoken);
}
const contextValue = {
token: token,
admintoken: admintoken,
isAdmin: userHasAdmin,
isLoggedIn: userIsLoggedIn,
adminAccess: adminTokenHandler,
login: loginHandler,
logout: logoutHandler,
};
return (
<AuthContext.Provider value={contextValue}>
{props.children}
</AuthContext.Provider>
);
};

export default AuthContext;

登录表单:

const AuthForm = () => {
const emailInputRef = useRef();
const passwordInputRef = useRef();
const [isLoading, setIsLoading] = useState(false);
const [isAdmin, setIsAdmin] = useState(false);
const router = useRouter();
const authCtx = useContext(AuthContext);

const submitHandler = (event) => {
event.preventDefault();
const enteredEmail = emailInputRef.current.value;
const enteredPassword = passwordInputRef.current.value;

var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("username", enteredEmail);
urlencoded.append("password", enteredPassword);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch(API_LOGIN_URL, requestOptions)
.then(async (res) => {
setIsLoading(false);
if (res.ok) {
return res.json();
} else {
const data = await res.json();
let errorMessage = 'Authentication failed!';
throw new Error(errorMessage);
}
})
.then((data) => {
authCtx.login(data.access_token);
router.replace('/');
const processedData = JSON.stringify(data);
for (let i = 0; i < processedData.length; i++) {
if (processedData.includes("ROLE_SUPER_ADMIN")) {
console.log("Found Admin");
authCtx.adminAccess(data.access_token);

} else {
console.log("Found User");
authCtx.adminAccess(null);
}


}
})
.catch((err) => {
alert(err.message);
});
};
return (
<section className={classes.auth}>
<h1>Login</h1>
<form onSubmit={submitHandler}>
<div className={classes.control}>
<label htmlFor='email'>Your Email</label>
<input type='email' id='email' required ref={emailInputRef} />
</div>
<div className={classes.control}>
<label htmlFor='password'>Your Password</label>
<input type='password' id='password' required ref={passwordInputRef} />
</div>
<div className={classes.actions}>
{!isLoading && <button>Login</button>}
{isLoading && <p>Sending request</p>}
</div>
</form>
</section>
);
};

保护路线:

const ProtectRoute = ({ children }) => {
const authCtx = useContext(AuthContext);
const isLoggedIn = authCtx.isLoggedIn;
if (!isLoggedIn && typeof window !== 'undefined' && window.location.pathname == '/') {
return <HomePage />;
} else {
if (!isLoggedIn && typeof window !== 'undefined' && window.location.pathname !== '/auth') {
return <RestrictedSection />;
} 
else {
return children;
} 
}
}
export default ProtectRoute;

最后,路由保护被包裹在主_app.js文件中:

function MyApp({ Component, pageProps }) {

// const ProtectedPages = dynamic(()=> import ('../store/ProtectRoute'));
return (
<AuthContextProvider>

<Layout>
<ProtectRoute> 
<Component {...pageProps} />
</ProtectRoute>
</Layout>

</AuthContextProvider>
)
};
export default MyApp

最新更新