我是 Next.js 的新手,并将我的前端从 React 移动到 Next (v4)。我已经使用 Spring 开发了一个后端,它连接到 Azure 上托管的 MySQL 数据库。该 API 在 Postman 和 React 前端上经过全面测试和功能。API 包括允许身份验证的端点。API 还会生成一个 JWT 令牌。
将前端从 React 迁移到 Next js
在尝试将前端从 React 中移出时,身份验证是面临的第一个问题。我选择尝试 next_auth,但使用用户名和密码凭据实现 next_auth 似乎存在一些问题。
使用 Spring / Java REST API 进行身份验证的下一个身份验证
上面让我问,Next auth 是否首先适合使用 Spring API 进行身份验证?或者如果自定义方法(即:标准 React)会更好?Next auth 文档似乎更喜欢使用内置方法,如 Google、Twitter 等,对自定义凭据方法的支持较少。
SOF上的类似问题
在这个特定的用例上,我没有找到很多类似的问题。这是我发现的唯一一个关于 Next auth 和 Spring API 的问题。这个问题没有答案。
最接近我的问题的是这个问题。它说问题在于 JWT 令牌和回调的配置。我按照指示进行操作,但没有解决问题。这个问题是关于将 Next auth 连接到 Spring Boot API。这个问题通常是关于如何配置和使用凭据提供程序。
基于所有这些,我尝试了以下方法:
React 代码(包括 Postman 签名)
React 中的以下代码适用于 API。我还在 fetch 方法中提取了 Postman 签名。
const AuthForm = () => {
const emailInputRef = useRef();
const passwordInputRef = useRef();
const [isLogin, setIsLogin] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [isAdmin, setIsAdmin] = useState(false);
const authCtx = useContext(AuthContext);
const switchAuthModeHandler = () => {
setIsLogin((prevState) => !prevState);
};
const submitHandler = (event) => {
event.preventDefault();
const enteredEmail = emailInputRef.current.value;
const enteredPassword = passwordInputRef.current.value;
// optional: Add validation
setIsLoading(true);
if (isLogin) {
// Postman signature here
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 应用程序代码
[...nextauth].js
export default NextAuth({
session: {
strategy: "jwt",
secret: process.env.SECRET,
maxAge: 30 * 24 * 60 * 60, // 30 days
},
providers: [
CredentialsProvider({
name: 'credentials',
credentials: {
Username: {label: "Username", type: "text", placeholder: 'app@apptest.com'},
Password: {label: "Password", type: "password"}
},
async authorize(credentials, req) {
// extracted from Postman
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("Username", credentials.Username);
urlencoded.append("Password", credentials.Password);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,d
redirect: 'follow'
};
const user = fetch(API_LOGIN_URL, {
requestOptions
})
console.log("Credentials are: " + credentials.Username)
if (user) {
// Any object returned will be saved in `user` property of the JWT
return user
} else {
// If you return null or false then the credentials will be rejected
return null
// You can also Reject this callback with an Error or with a URL:
// throw new Error('error message') // Redirect to error page
// throw '/path/to/redirect' // Redirect to a URL
}
},
})],
callbacks: {
async session({ session, token, user }) {
session.user.id = token.id;
session.accessToken = token.accessToken;
return session;
},
async jwt({ token, user, account, profile, isNewUser }) {
if (user) {
token.id = user.id;
}
if (account) {
token.accessToken = account.access_token;
}
return token;
},
}
}
)
Authform.js(登录表单)
import { useState, useRef, useContext } from 'react';
import classes from './AuthForm.module.css';
import { signIn } from 'next-auth/react';
import { API_LOGIN_URL } from '../Constants';
import { redirect } from 'next/dist/server/api-utils';
function AuthForm () {
const emailInputRef = useRef();
const passwordInputRef = useRef();
async function submitHandler (event) {
event.preventDefault();
const enteredEmail = emailInputRef.current.value;
const enteredPassword = passwordInputRef.current.value;
const result = await signIn('credentials', {
redirect: false,
username: enteredEmail,
password: enteredPassword,
callbackUrl: `${window.location.origin}`
});
console.log("The email " + enteredEmail);
console.log("Result is: " + result.error);
}
return (
<section className={classes.auth}>
<h1>Login</h1>
<form onSubmit={submitHandler}>
<div className={classes.control}>
<label htmlFor='username'>Your Email</label>
<input type='email' id='username' 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}>
<button>Login</button>
<button
type='button'
className={classes.toggle}
>
</button>
</div>
</form>
</section>
);
}
export default AuthForm;
错误
在前端
实际登录时,我没有收到任何错误。如果我在重新启动应用程序后刷新浏览器,我会得到:
[next-auth][warn][NO_SECRET] https://next-auth.js.org/warnings#no_secret [next-auth][error][JWT_SESSION_ERROR] https://next-auth.js.org/errors #jwt_session_error 解密操作失败 { message: '解密操作失败', stack: 'JWEDecryptionFailed: 解密操作失败\n' +
后端
登录实际上确实到达了后端(在监视 API 终端时),但它为用户名和密码值提供了 null。
主要问题
- Next auth 是对 Spring API 进行身份验证的正确框架,还是编写自定义身份验证方法更好(甚至坚持使用 React)?
- 上面的代码有什么问题?
谢谢!