简体   繁体   English

React Native + Redux基本身份验证

[英]React Native + Redux basic authentication

I'm looking for a way to create a basic authentication for my react-native app. 我正在寻找一种为我的react-native应用程序创建基本身份验证的方法。 I couldn't find any good example for react-native app. 我找不到react-native app的任何好例子。

  • To login, the app sends the email/password + clientSecret to my server 要登录,应用程序会将电子邮件/密码+ clientSecret发送到我的服务器
  • If OK, the server returns accessToken + refreshToken 如果正常,则服务器返回accessToken + refreshToken
  • The user is logged in, all other requests include the bearer with the accessToken. 用户已登录,所有其他请求包括具有accessToken的承载。
  • If the accessToken expires, the app requests a new one with the refreshToken automatically. 如果accessToken过期,应用程序会自动请求一个带有refreshToken的新服务器。
  • The user stays logged in all the time, the states should be saved in the phone. 用户始终保持登录状态,状态应保存在手机中。

What would be the best approach for this? 对此最好的方法是什么?

Thanks. 谢谢。

When an app communicates with a HTTP API which enforces some form of authentication, the app typically follows these steps: 当应用程序与强制执行某种形式身份验证的HTTP API进行通信时,应用程序通常会遵循以下步骤:

  1. The app is not authenticated, so we prompt the user to log in. 该应用未经过身份验证,因此我们提示用户登录。
  2. The user enters their credentials (username and password), and taps submit. 用户输入其凭据(用户名和密码),然后点击提交。
  3. We send these credentials to the API, and inspect the response: 我们将这些凭证发送到API,并检查响应:
    • On success (200 - OK): We cache the authentication token/ hash, because we're going to use this token/ hash in every subsequent request. 成功时(200 - OK):我们缓存身份验证令牌/哈希,因为我们将在每个后续请求中使用此令牌/哈希。
      • If the token/ hash does not work during any of the subsequent API requests (401 - Unauthorized), we'll need to invalidate the hash/ token and prompt the user to log in again. 如果令牌/哈希在任何后续API请求(401 - 未授权)期间不起作用,我们将需要使哈希/令牌无效并提示用户再次登录。
    • Or, on failure (401 - Unauthorized): We display an error message to the user, prompting them re-enter their credentials. 或者,失败时(401 - 未经授权):我们向用户显示错误消息,提示他们重新输入凭据。

Logging In 在登录

Based on the work flow defined above our app starts by displaying a login form, step 2 kicks in when the user taps the login button which dispatches the login action creator below: 根据上面定义的工作流程,我们的应用程序首先显示一个登录表单,当用户点击下面的login操作创建者的登录按​​钮时, 第2步启动:

/// actions/user.js

export function login(username, password) {
  return (dispatch) => {

    // We use this to update the store state of `isLoggingIn`          
    // which can be used to display an activity indicator on the login
    // view.
    dispatch(loginRequest())

    // Note: This base64 encode method only works in NodeJS, so use an
    // implementation that works for your platform:
    // `base64-js` for React Native,
    // `btoa()` for browsers, etc...
    const hash = new Buffer(`${username}:${password}`).toString('base64')
    return fetch('https://httpbin.org/basic-auth/admin/secret', {
      headers: {
        'Authorization': `Basic ${hash}`
      }
    })
    .then(response => response.json().then(json => ({ json, response })))
    .then(({json, response}) => {
      if (response.ok === false) {
        return Promise.reject(json)
      }
      return json
    })
    .then(
      data => {
        // data = { authenticated: true, user: 'admin' }
        // We pass the `authentication hash` down to the reducer so that it
        // can be used in subsequent API requests.

        dispatch(loginSuccess(hash, data.user))
      },
      (data) => dispatch(loginFailure(data.error || 'Log in failed'))
    )
  }
}

There's a lot of code in the function above, but take comfort in the fact that the majority of the code is sanitising the response and can be abstracted away. 上面的函数中有很多代码,但是大多数代码都在清理响应并且可以将其抽象出来。

The first thing we do is dispatch an action LOGIN_REQUEST which updates our store and lets us know that the user isLoggingIn . 我们要做的第一件事就是调度一个动作LOGIN_REQUEST来更新我们的商店并让我们知道用户正在isLoggingIn

dispatch(loginRequest())

We use this to display an activity indicator ( spinning wheel, "Loading...", etc. ), and to disable the log in button in our log in view. 我们使用它来显示活动指示器( 旋转轮,“正在加载...”等 ),并在我们的登录视图中禁用登录按钮。

Next we base64 encode the user's username and password for http basic auth, and pass it to the request's headers. 接下来,我们base64编码用户的http basic auth的用户名和密码,并将其传递给请求的标头。

const hash = new Buffer(`${username}:${password}`).toString('base64')
return fetch('https://httpbin.org/basic-auth/admin/secret', {
  headers: {
    'Authorization': `Basic ${hash}`
  }
/* ... */

If everything went well, we'll dispatch a LOGIN_SUCCESS action, which results in us having an authentication hash in our store, which we'll use in subsequent requests. 如果一切顺利,我们将调度一个LOGIN_SUCCESS操作,这会导致我们在商店中有一个身份验证hash ,我们将在后续请求中使用。

dispatch(loginSuccess(hash, data.user))

On the flip side, if something went wrong then we also want to let the user know: 另一方面,如果出现问题,我们也想让用户知道:

dispatch(loginFailure(data.error || 'Log in failed')

The loginSuccess , loginFailure , and loginRequest action creators are fairly generic and don't really warrant code samples. loginSuccessloginFailureloginRequest操作创建者是相当通用的,并不真正保证代码示例。 See: https://github.com/peterp/redux-http-basic-auth-example/blob/master/actions/user.js) 请参阅: https//github.com/peterp/redux-http-basic-auth-example/blob/master/actions/user.js)

Reducer 减速器

Our reducer is also typical: 我们的减速机也很典型:

/// reducers/user.js
function user(state = {
  isLoggingIn: false,
  isAuthenticated: false
}, action) {
  switch(action.type) {
    case LOGIN_REQUEST:
      return {
        isLoggingIn: true, // Show a loading indicator.
        isAuthenticated: false
      }
    case LOGIN_FAILURE:
      return {
        isLoggingIn: false,
        isAuthenticated: false,
        error: action.error
      }
    case LOGIN_SUCCESS:
      return {
        isLoggingIn: false,
        isAuthenticated: true, // Dismiss the login view.
        hash: action.hash, // Used in subsequent API requests.
        user: action.user
      }
    default:
      return state
  }
}

Subsequent API requests 后续API请求

Now that we have an authentication hash in our store we can pass it into subsequent request's headers. 现在我们在商店中有一个身份验证哈希,我们可以将它传递给后续请求的头文件。

In our example below we're fetching a list of friends for our authenticated user: 在下面的示例中,我们为经过身份验证的用户提取了一个朋友列表:

/// actions/friends.js
export function fetchFriends() {
  return (dispatch, getState) => {

    dispatch(friendsRequest())

    // Notice how we grab the hash from the store:
    const hash = getState().user.hash
    return fetch(`https://httpbin.org/get/friends/`, {
      headers: {
        'Authorization': `Basic ${hash}`
      }
    })
    .then(response => response.json().then(json => ({ json, response })))
    .then(({json, response}) => {
      if (response.ok === false) {
        return Promise.reject({response, json})
      }
      return json
    })
    .then(
      data => {
        // data = { friends: [ {}, {}, ... ] }
        dispatch(friendsSuccess(data.friends))
      },
      ({response, data}) => {
        dispatch(friendsFailure(data.error))

        // did our request fail because our auth credentials aren't working?
        if (response.status == 401) {
          dispatch(loginFailure(data.error))
        }
      }
    )
  }
}

You may find that most API requests typically dispatch the same 3 actions as above: API_REQUEST , API_SUCCESS , and API_FAILURE , and as such the majority of the request/ response code can be pushed into Redux middleware. 您可能会发现大多数API请求通常会调度与上述相同的3个操作: API_REQUESTAPI_SUCCESSAPI_FAILURE ,因此大多数请求/响应代码都可以推送到Redux中间件。

We fetch the hash authentication token from the store and setup the request. 我们从商店获取哈希认证令牌并设置请求。

const hash = getState().user.hash
return fetch(`https://httpbin.org/get/friends/`, {
  headers: {
    'Authorization': `Basic ${hash}`
  }
})
/* ... */

If the API response with a 401 status code then we've got to remove our hash from the store, and present the user with a log in view again. 如果API响应带有401状态代码,那么我们必须从商店中删除我们的哈希,并再次向用户显示日志。

if (response.status == 401) {
  dispatch(loginFailure(data.error))
}

I've answered the question generically and only dealing with http-basic-auth. 我一般都回答了这个问题,只处理了http-basic-auth。

I think that the concept may remain the same, you'll push the accessToken and refreshToken in the store, and extract it in subsequent requests. 我认为概念可能保持不变,您将在商店中推送accessTokenrefreshToken ,并在后续请求中提取它。

If the request fails then you'll have to dispatch another action which updates the accessToken, and then recalls the original request. 如果请求失败,那么您将不得不分派另一个更新accessToken的操作,然后调用原始请求。

I haven't seen too much by way of examples in this area, and think it's definitely something that needs more coverage. 我在这个领域没有看到太多的例子,并且认为它肯定需要更多的报道。 I've not yet implemented auth myself, else I'd point you to some code examples. 我自己还没有实现auth,否则我会指出一些代码示例。 But I can point you to a couple links I've collected that may help you in the right direction... 但我可以向您指出我收集的一些链接,可以帮助您朝着正确的方向......

Regardless how you perform your auth, you'll need to securely store your access, refresh, and secret tokens. 无论您如何执行身份验证,都需要安全地存储访问权限,刷新权限和秘密令牌。 On iOS I believe you'd do that using keychain and for Android it looks like KeyStore is the way. 在iOS上我相信你会使用钥匙串来做到这一点,对于Android来说,它看起来像是KeyStore You may find oblador/react-native-keychain helpful, though it doesn't yet support android it looks like it may support android soon . 你可能会发现oblador / react-native-keychain很有用,虽然它还不支持android它看起来很快就会支持android

I'm actually working on a video tutorial series that answers at least some of the questions your asking. 我实际上正在制作一个视频教程系列,至少可以回答你提出的一些问题。 The video along with a transcript and sample code can be found here: http://codecookbook.co/post/how-to-build-a-react-native-login-form-with-redux-pt1/ 视频以及成绩单和示例代码可在此处找到: http//codecookbook.co/post/how-to-build-a-react-native-login-form-with-redux-pt1/

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM