jay*_*tai 7 api reactjs spring-boot next.js next-auth
我正在尝试在 Next.js 上开发一个灵活的身份验证系统,可以使用 Spring (Java) API 后端。使用 Postman 可以完美地实现端点功能。该 API 还提供了自己的 JWT。我想使用 API 端点登录注册用户。这也意味着我需要一种方法来使用服务器中的 JWT 来对尝试登录的用户进行身份验证。
遵循 Next_auth 和 iron-sessions 的文档非常令人困惑。API 工作正常。尤其是 Next_auth 似乎为这种类型的身份验证提供了有限的支持。
我研究了很多帖子、教程,甚至发布了这个问题。这个问题最接近我想要理解的问题,但它涉及登录后状态,并且该过程看起来有点令人困惑。这个问题似乎表明在 Next 上执行自定义身份验证非常复杂,并且最好使用框架。
我是否在这里遗漏了一些东西,或者让 Next js 与外部 API 和 JWT 一起使用是否非常复杂?我不需要 Next 提供的完整堆栈功能。我也不想被迫通过 Google、Twitter、FB 等进行身份验证。
我需要这样的东西,它是使用 React 创建的,并使用 REST 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;
Run Code Online (Sandbox Code Playgroud)
我想在 Next.js 中做类似的事情,而不需要按照 next_auth 等框架/库的规则工作。
我非常感谢任何解释如何使用 API 端点的 post 方法来查找用户名和密码的指导(建议、教程等)。
我还想知道如何使用 API 生成的 JWT 来完成该过程并对用户进行身份验证。我可以把这部分放在另一个问题中。对于这个问题,即使我知道如何使用我描述的 API 端点通过查找用户名和密码来登录,我也会很高兴。在 Next.js 中,我只看到使用 Next_auth 或 iron-sessions 等框架完成身份验证。我还没有看到您在 React 中找到的自定义身份验证方法类型(如上所述)。因此,我想知道:
我们必须使用 Next_auth 或 iron-sessions 进行身份验证吗?是否有任何自定义 Next js 身份验证方法的示例,这些方法不依赖于这些框架,并且可以与后端 API 和 JWT(例如 Spring)很好地配合?
预先感谢您的任何帮助。
在 Dream_Cap 的大力帮助下,他向我引导了一篇相关文章和他自己的 node.js 代码,答案是完全可以编写自定义身份验证方法,而不依赖于任何框架(例如 next_auth)。
解决方案的关键在于 Next js 可以使用 React Context 作为高阶组件 (HOC),来保存身份验证状态并相应地在用户会话中保留更改。这与使用 [...nextuth].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;
Run Code Online (Sandbox Code Playgroud)
登录表单:
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>
);
};
Run Code Online (Sandbox Code Playgroud)
保护路线:
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;
Run Code Online (Sandbox Code Playgroud)
最后,路由保护包裹在主 _app.js 文件中:
function MyApp({ Component, pageProps }) {
// const ProtectedPages = dynamic(()=> import ('../store/ProtectRoute'));
return (
<AuthContextProvider>
<Layout>
<ProtectRoute>
<Component {...pageProps} />
</ProtectRoute>
</Layout>
</AuthContextProvider>
)
};
export default MyApp
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6799 次 |
最近记录: |