简体   繁体   English

React - 处理登录和身份验证的最佳方法是什么?

[英]React - What is the best way to handle login and authentication?

New to react and working on an application with authentication/logging in. It currently works but feels hacked together.刚开始使用身份验证/登录来响应和处理应用程序。它目前可以工作,但感觉被黑客攻击了。 Right now I have my isAuthenticated state located in my routes.js like so:现在我的isAuthenticated状态位于我的routes.js如下所示:

class Routes extends Component {

    constructor(props) {
        super(props);

        this.state = {
            isAuthenticated: false,
         }
     }

On my login page, I need to know when a user is authenticated to redirect them to the home page.在我的登录页面上,我需要知道用户何时通过身份验证以将他们重定向到home What is the best design pattern to allow access and manipulation of this isAuthenticated state?允许访问和操作此isAuthenticated状态的最佳设计模式是什么? How I currently have it set up is I have a function that sets the state inside the routes.js and sends the state as a prop like so:我目前的设置方式是我有一个函数可以在routes.js中设置状态并将状态作为道具发送,如下所示:

 setAuthenticated = (isAuthenticated) => {
        this.setState({isAuthenticated});
    }

and down below in the router...并在路由器下方...

<Route path="/" exact component={() =>
                            <div>
                                <Login
                                    isAuthenticated={this.state.isAuthenticated}
                                    setAuthenticated={this.setAuthenticated}
                            </div>
                        } />

Yes, I understand this is bad design because this is changing props values which are supposed to be immutable.是的,我知道这是一个糟糕的设计,因为这改变了本来应该是不可变的 props 值。 This is also bad because when I change this value in my login.js it causes multiple unnecessary re-renders.这也很糟糕,因为当我在login.js更改此值时,它会导致多次不必要的重新渲染。 Should I be declaring isAuthenticated as some type of global variable?我应该将isAuthenticated声明为某种类型的全局变量吗? I am not using any state management by the way.顺便说一下,我没有使用任何状态管理。

Edit: I am setting isAuthenticated based on a response from my server which confirms correct login/password combination.编辑:我正在根据来自我的服务器的响应设置isAuthenticated ,该响应确认了正确的登录名/密码组合。

Handling isAuthenticated only in the state means the user will be unauthenticated every time he refreshes the page.仅在state处理isAuthenticated意味着用户每次刷新页面时都将未经身份验证。 That's not really user-friendly!这不是真正的用户友好! :) :)

So instead, the Login page should store an access_token (coming from your backend) in the cookies or localStorage of the browser .因此,登录页面应该在浏览器的cookieslocalStorage存储一个access_token (来自您的后端)。 An access_token proves the user is authenticated and also verifies his identity. access_token证明用户已通过身份验证并验证其身份。 You will usually pass this access_token to every next requests to your server, to check if this user is allowed to access the data he's requesting, or allowed to create, edit and delete the things he's trying to create, edit and delete.您通常会将这个access_token传递给您服务器的每个下一个请求,以检查是否允许此用户访问他请求的数据,或者是否允许创建、编辑和删除他尝试创建、编辑和删除的内容。

Then you can check this access_token on every other pages as well and redirect the user to the Login page if he's not authenticated anymore.然后,您也可以在所有其他页面上检查此access_token ,如果用户不再通过身份验证,则将用户重定向到登录页面。


A brief aside on the difference between access_token and refresh_tokenthis will help you understand the code bellow, but feel free to skip ahead if you are already familiar with it .简要介绍access_tokenrefresh_token之间的区别——这将帮助您理解下面的代码,但如果您已经熟悉它,请随时跳过

Your backend probably uses OAuth2 , which is the most common authentication protocol nowadays.您的后端可能使用OAuth2 ,这是当今最常见的身份验证协议。 With OAuth2 , your app makes a first request to the server containing the username and password of the user to authenticate.使用OAuth2 ,您的应用程序向服务器发出第一个请求,其中包含用户的用户名和密码以进行身份​​验证。 Once the user is authenticated, he receives 1) an access_token , which usually expires after an hour, and 2) a refresh_token , which expires after a very long time (hours, days).用户通过身份验证后,他会收到 1) access_token ,通常在一个小时后过期,2) refresh_token ,它会在很长时间(几小时、几天)后过期。 When the access_token expires, instead of asking the user for his username and password again, your app sends the refresh_token to the server to obtain a new access_token for this user.access_token过期时,您的应用不会再次询问用户的用户名和密码,而是将refresh_token发送到服务器以获取该用户的新access_token


A brief aside on the differences between cookies and localStoragefeel free to skip it too!简要介绍cookieslocalStorage之间的区别——也可以跳过它!

localStorage is the most recent technology between both. localStorage是两者之间的最新技术。 It's a simple key/value persistence system, which seems perfect to store the access_token and its value.这是一个简单的键/值持久化系统,它似乎非常适合存储access_token及其值。 But we also need to persist its date of expiration.但是我们也需要坚持它的过期日期。 We could store a second key/value pair named expires but it would be more logic to handle on our side.我们可以存储名为expires的第二个键/值对,但在我们这边处理会更具逻辑性。

On the other hand, cookies have a native expires property, which is exactly what we need!另一方面, cookies有一个原生的expires属性,这正是我们所需要的! cookies are an old technology and are not very developer-friendly, so I personally use js-cookie , which is a small library to manipulate cookies . cookies是一项老技术,对开发人员不是很友好,所以我个人使用js-cookie ,这是一个操作cookies的小型库。 It makes it look like a simple key/value persistence system too: Cookies.set('access_token', value) then Cookies.get('access_token') .它也使它看起来像一个简单的键/值持久性系统: Cookies.set('access_token', value)然后Cookies.get('access_token')

Other pro for the cookies : they are cross subdomains ! cookies其他cookies :它们是跨子域的 If your Login app is login.mycompany.com and your Main app is app.mycompany.com , then you can create a cookie on the Login app and access it from the Main app.如果您的登录应用程序是login.mycompany.com并且您的主应用程序是app.mycompany.com ,那么您可以在登录应用程序上创建一个cookie并从主应用程序访问它。 This is not possible with LocalStorage .这对于LocalStorage是不可能的。


Here are some of the methods and special React components I use for authentication:以下是我用于身份验证的一些方法和特殊的 React 组件:

isAuthenticated() isAuthenticated()

import Cookies from 'js-cookie'

export const getAccessToken = () => Cookies.get('access_token')
export const getRefreshToken = () => Cookies.get('refresh_token')
export const isAuthenticated = () => !!getAccessToken()

authenticate()认证()

export const authenticate = async () => {
  if (getRefreshToken()) {
    try {
      const tokens = await refreshTokens() // call an API, returns tokens

      const expires = (tokens.expires_in || 60 * 60) * 1000
      const inOneHour = new Date(new Date().getTime() + expires)

      // you will have the exact same setters in your Login page/app too
      Cookies.set('access_token', tokens.access_token, { expires: inOneHour })
      Cookies.set('refresh_token', tokens.refresh_token)

      return true
    } catch (error) {
      redirectToLogin()
      return false
    }
  }

  redirectToLogin()
  return false
}

redirectToLogin()重定向登录()

const redirectToLogin = () => {
  window.location.replace(
    `${getConfig().LOGIN_URL}?next=${window.location.href}`
  )
  // or history.push('/login') if your Login page is inside the same app
}

AuthenticatedRoute认证路由

export const AuthenticatedRoute = ({
  component: Component,
  exact,
  path,
}) => (
  <Route
    exact={exact}
    path={path}
    render={props =>
      isAuthenticated() ? (
        <Component {...props} />
      ) : (
        <AuthenticateBeforeRender render={() => <Component {...props} />} />
      )
    }
  />
)

AuthenticateBeforeRender在渲染前进行身份验证

class AuthenticateBeforeRender extends Component {
  state = {
    isAuthenticated: false,
  }

  componentDidMount() {
    authenticate().then(isAuthenticated => {
      this.setState({ isAuthenticated })
    })
  }

  render() {
    return this.state.isAuthenticated ? this.props.render() : null
  }
}

If you are using an application where the authentication lasts only for one session, storing it in state is enough.如果您使用的应用程序的身份验证仅持续一个会话,则将其存储在状态中就足够了。 But do note that this means, the user will lose the authenticated status on page refresh.但请注意,这意味着用户将在页面刷新时失去已验证状态。

Here is an example using React Context, where we create context using createContext and use Consumer to access it across the application.这是一个使用 React Context 的示例,我们使用createContext创建上下文并使用Consumer在整个应用程序中访问它。

 const AuthenticationContext = React.createContext(); const { Provider, Consumer } = AuthenticationContext; function Login(props) { return ( <Consumer> { value=> <button onClick={value.login}>Login</button> } </Consumer> ); } function Logout() { return ( <Consumer> { value=> <button onClick={value.logout}>Logout</button> } </Consumer> ); } function AnotherComponent() { return ( <Consumer> { value=>{ return value.isAuthenticated? <p>Logged in</p>: <p>Not Logged in</p> } } </Consumer> ); } class App extends React.Component { constructor(props) { super(props); this.login = ()=> { this.setState({ isAuthenticated: true }); } this.logout = ()=> { this.setState({ isAuthenticated: false }); } this.state = { isAuthenticated: false, login: this.login, logout: this.logout } } render() { return ( <Provider value={this.state}> <Login /> <Logout /> <AnotherComponent /> </Provider> ); } } ReactDOM.render(<App />, document.getElementById("root"));
 <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"></div>

https://reactjs.org/docs/context.html#reactcreatecontext https://reactjs.org/docs/context.html#reactcreatecontext

If your page protection relies on an 'isAuthenticated' state variable, you should probably disable the react devtools in production. 如果页面保护依赖于“ isAuthenticated”状态变量,则可能应该在生产环境中禁用react devtools Otherwise, it would be possible to inspect the page and manually flip the flag to true, exposing the protected page to an unauthenticated user. 否则,可以检查页面并手动将标志翻转为true,从而将受保护的页面暴露给未经身份验证的用户。

you can set the access token in the local storage on login and clear it after the user logs out.您可以在登录时在本地存储中设置访问令牌,并在用户注销后清除它。 the is authenticated method will then be used to check if there is a token and whether the token is valid while making an API call然后将使用经过身份验证的方法来检查是否有令牌以及在进行 API 调用时令牌是否有效

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用 React 处理身份验证的最佳方法是什么? - What's the best way to handle authentication with React? MySql、NodeJS、ExpressJS 和 bcrypt:处理用户登录的最佳方式是什么? - MySql, NodeJS, ExpressJS and bcrypt: what is the best way to handle users' login? React + Redux - 在表单组件中处理CRUD的最佳方法是什么? - React + Redux - What's the best way to handle CRUD in a form component? 在 React 中处理基本文档文件下载的最佳方法是什么? - What is the best way to handle basic document file downloads in React? 使用 React 和 Redux 处理数据密集型请求的最佳方法是什么? - What is the best way to handle data-intensive requests with React and Redux? 使用React处理登录的基本方法 - Basic way to handle login with React 使用 React 处理页面中的所有状态是最好的方法吗 - Is it best way to handle all state in page with React 在 React 应用程序中使用访问令牌实现身份验证的最佳方法是什么? - What could be the best way to implement authentication using access token in React app? 通过 React Router v5 处理多种语言的最佳方式是什么? - What's the best way to handle multiple languages via React Router v5? 以本机形式处理键盘的最佳方法是什么? 矿井一直躲藏/显示并分散注意力 - What is the best way to handle the keyboard in react native forms? Mine keeps hiding / showing and is distracting
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM