Next.js 示例 Auth - 基于 auth 的重新路由,环绕其他功能

Posted

技术标签:

【中文标题】Next.js 示例 Auth - 基于 auth 的重新路由,环绕其他功能【英文标题】:Next.js Example Auth - re-routing based on auth, wrapping around other functions 【发布时间】:2020-08-16 05:11:29 【问题描述】:

我正在尝试将 next.js 与身份验证一起用于一个小项目。身份验证当前有效,但不允许我在导航栏中显示数据。

我最初将它与 firebase 一起使用,但现在不再使用了!!现在在下面单独设置身份验证。

这是示例存储库,其中包含用于 auth 和 next.js 的我的 API,我正在尝试将它们集成在一起以使用用于 api 调用的标头集进行登录和注销。

https://github.com/Hewlbern/example

只需获得基本的登录和注销功能,我就可以控制用户对我网站的访问。我知道这真的很简单——只是很困惑如何使用 next.js 和应用程序的文档页面如何工作:S

我正在尝试显示此 API 的输出表,并允许下载输出的 json(到 CSV 或其他格式)。因此,在使用查询参数进行搜索后,并且仅在用户登录后的页面上可用,才是重点:)

这是我正在使用的登录功能的示例。

import  useRef, useState  from 'react';
import React from 'react'
import PropTypes from 'prop-types'

import Layout from "../components/Layout";




export default function Login() 
  const emailRef = useRef<htmlInputElement>(null);
  const passRef = useRef<HTMLInputElement>(null);
  const [message, setMessage] = useState<any>(null);
  async function handleLogin() 
    const resp = await fetch('http://localhost:3001/auth/login', 
      method: 'POST',
      headers: 
        'Content-Type': "application/x-www-form-urlencoded"
      ,
      body: JSON.stringify(
        email: emailRef.current?.value,
        password: passRef.current?.value
      )
    );
    const json = await resp.json();
    setMessage(json);
  

  return (
    <Layout>
      JSON.stringify(message)
      <input type="text" placeholder="email" ref=emailRef />
      <input type="password" placeholder="password" ref=passRef />
      <button onClick=handleLogin>Login</button>
    </Layout>
  );

这是发布到这个 api 请求

router.post('/login', (req, res) => 

// console.log(req.body)

    let email = req.body.email;
    let password = req.body.password;

    console.log(email,password)

    DatabaseService.GetUser(email).then(user => 
            if(user===null)
                res.sendStatus(404);
            
            else
                if(bcrypt.compareSync(password, user[0].password)) 
                    jwt.sign(user, 'secretkey',  expiresIn: '30d' , (err, token) => 
                        DatabaseService.SetSession(token,JSON.stringify(user[0].user_id)).then(inserted=>
                            res.json(
                                token
                            );
                        );
                    );
                 else 
                    res.sendStatus(500);
                
            
        );
);

所以就这个小例子来说,我目前发送请求的方式有问题吗? (认为​​这是登录接受请求的格式?)

如果有人做过类似的事情或知道如何解决这些问题,我将不胜感激 :)

干杯!

【问题讨论】:

这里的具体问题是什么,身份验证无法正常工作,您是否需要逻辑来处理登录和注册按钮 当然。我希望身份验证正常工作,因此我可以在导航栏级别使用登录和注销按钮,并且我想使用 API 进行登录和注销以及用户功能,如 github API 链接中所示。 @ShubhamKhatri 更新了问题,希望它更容易理解! @MichaelHolborn 在 github 上创建一个示例项目并分享链接,并详细说明当前源代码存在的问题。 @Nirus Okay 会做 :) 【参考方案1】:

我在这里推荐的是创建一个自定义钩子,它利用 React 的 Context API 来“监控”身份验证状态的变化。然后将您的应用程序包装在该提供程序中,您将可以使用新的自定义挂钩灵活地使用该身份验证状态做任何您想做的事情。

以下是使用 Firebase 进行身份验证时自定义挂钩的外观示例:

import React,  createContext, useContext, useState  from 'react'

import  auth  from './services' // this is just firebase.auth()

const UserContext = createContext()

export const UserProvider = ( children ) => 
  const [user, setUser] = useState(undefined)

  auth.onAuthStateChanged(setUser)

  return <UserContext.Provider value=user>children</UserContext.Provider>


export const useUser = () => useContext(UserContext)

现在您只需将您的应用包装在UserProvider 中。

像这样:

import React,  StrictMode  from 'react'
import ReactDOM from 'react-dom'
import  BrowserRouter  from 'react-router-dom'

import App from './app'
import  UserProvider  from './hooks'

const rootElement = document.getElementById('root')
ReactDOM.render(
  <StrictMode>
    <BrowserRouter>
      <UserProvider>
        <App />
      </UserProvider>
    </BrowserRouter>
  </StrictMode>,
  rootElement
)

然后举个例子,假设您希望在登录页面被记录后自动直接离开登录页面。如果用户已登录,您可以使用useEffect 钩子和useHistory 钩子导航到/

这样就可以了:

import React,  useEffect  from 'react'
import  useHistory  from 'react-router-dom'

import  useUser  from './hooks'

const LoginPage = () => 
  const history = useHistory()
  const user = useUser() // user is undefined if not logged in

  useEffect(() => 
    if (user)  // will run the condition if user exists
      history.push('/')
    

  , [user])

  ...

您可以继续在导航栏中实际使用user 数据,方法如下:

import React from 'react'
import  Link  from 'react-router-dom'

import  useUser  from './hooks'

const NavBar = () => 
  const user = useUser()

  return (
    <div>
      user ?
        <Link to="/profile">Welcome, user.displayName</Link> :
        <Link to="/login">Login</Link>
      
    </div>
  )

显然,您可以根据自己的需要为我们更改此设置,但所有这一切都应该让您了解如何以干净稳健的方式处理身份验证状态。

【讨论】:

与我链接的 API 的工作方式相同吗? 您应该能够使用任何 API 调整此解决方案,只需相应地更新 useUser 钩子即可。 我会尝试实现这一点,然后回复你它是如何工作的! 我认为这应该不是问题,除非路由工作方式不同,然后只需将我的 react-router-dom 解决方案替换为您的替代路由解决方案。 @BarryMichaelDoyle 你必须注意 Nextjs 执行服务器端渲染,很多关于 Context 和 Wrappers 的概念需要特别注意

以上是关于Next.js 示例 Auth - 基于 auth 的重新路由,环绕其他功能的主要内容,如果未能解决你的问题,请参考以下文章

如何使用firebase auth限制对next.js中页面的访问?

next-auth 中 useSession() 的“加载”返回值是啥?

如何在 Next.js 中使用 next-auth 实现凭据授权?

Next.js/Next-Auth/Apollo/GraphQL 设置

Next js:next auth provider + redux provider

Auth0 Next.js 错误 withPageAuthRequired 您不应该在 getServerSideProps 解决后访问“res”