【问题标题】:React Native + Redux basic authenticationReact Native + Redux 基本认证
【发布时间】:2016-06-11 03:57:04
【问题描述】:

我正在寻找一种方法来为我的 react-native 应用程序创建基本身份验证。 我找不到任何反应原生应用的好例子。

  • 要登录,应用程序将电子邮件/密码 + clientSecret 发送到我的服务器
  • 如果OK,服务器返回accessToken + refreshToken
  • 用户已登录,所有其他请求都包含带有 accessToken 的承载。
  • 如果 accessToken 过期,应用会自动使用 refreshToken 请求一个新的。
  • 用户一直处于登录状态,状态应保存在手机中。

最好的方法是什么?

谢谢。

【问题讨论】:

  • 无法为您编写完整的示例,但请查看 JSON web tokens 作为您在客户端和服务器之间传递的内容。这将允许您执行无 DB 角色和您想要的过期行为。

标签: javascript authentication react-native redux


【解决方案1】:

当应用与强制某种形式的身份验证的 HTTP API 通信时,应用通常会遵循以下步骤:

  1. 应用未通过身份验证,因此我们提示用户登录。
  2. 用户输入他们的凭据(用户名和密码),然后点击提交。
  3. 我们将这些凭据发送到 API,并检查响应:
    • 成功时 (200 - OK):我们缓存身份验证令牌/哈希,因为我们将在每个后续请求中使用此令牌/哈希
      • 如果令牌/哈希在任何后续 API 请求期间不起作用(401 - 未授权),我们需要使哈希/令牌无效并提示用户重新登录。
    • 或者,失败时(401 - 未经授权):我们向用户显示错误消息,提示他们重新输入凭据。

登录

根据上面定义的工作流程,我们的应用程序首先显示一个登录表单,第 2 步在用户点击登录按钮时开始,该按钮调度下面的 login 操作创建者:

/// 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'))
    )
  }
}

上面的函数中有很多代码,但请放心 大部分代码都在清理响应并且可以被抽象掉。

我们要做的第一件事是发送一个动作LOGIN_REQUEST,它会更新我们的商店并让我们知道用户isLoggingIn

dispatch(loginRequest())

我们使用它来显示活动指示器(旋转轮、“正在加载...”等),并在我们的登录视图中禁用登录按钮。

接下来,我们对用户的用户名和密码进行 base64 编码以进行 http 基本身份验证,并将其传递给请求的标头。

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

如果一切顺利,我们将发送一个LOGIN_SUCCESS 操作,这会导致我们在我们的商店中拥有一个身份验证hash,我们将在后续请求中使用它。

dispatch(loginSuccess(hash, data.user))

另一方面,如果出现问题,我们也想让用户知道:

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

loginSuccessloginFailureloginRequest 动作创建器相当通用,并不真正保证代码示例。见:https://github.com/peterp/redux-http-basic-auth-example/blob/master/actions/user.js)

减速机

我们的reducer也很典型:

/// 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
  }
}

后续 API 请求

现在我们的存储中有一个身份验证哈希,我们可以将它传递到后续请求的标头中。

在下面的示例中,我们为经过身份验证的用户获取朋友列表:

/// 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))
        }
      }
    )
  }
}

您可能会发现大多数 API 请求通常会分派与上述相同的 3 个操作:API_REQUESTAPI_SUCCESSAPI_FAILURE,因此大部分请求/响应代码都可以推送到 Redux 中间件中。

我们从存储中获取哈希认证令牌并设置请求。

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

如果 API 响应带有 401 状态代码,那么我们必须从存储中删除我们的哈希,并再次向用户显示登录视图。

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

我已经笼统地回答了这个问题,并且只处理了 http-basic-auth。

我认为概念可能保持不变,您将accessTokenrefreshToken在商店中推送,并在后续请求中提取。

如果请求失败,那么您将不得不调度另一个更新 accessToken 的操作,然后撤回原始请求。

【讨论】:

  • 我将用一个完整的例子来完成我正在处理的 repo。
  • (投票赞成)@peterp 这是基本身份验证的一个很好的例子,但你对坚持“记住我”功能有何看法?我见过的大多数应用只需要在首次启动时获取您的凭据,而不是每次启动都需要获取您的凭据,除非它们与财务相关(或类似)
  • 我还有一个问题。您是否建议创建一个中间件来注入承载?
  • @ChrisGeirman 我认为您建议将身份验证令牌存储在诸如钥匙串 (iOS) 和密钥库 (Android) 之类的安全设备中可能是最好的。
  • @alexmngn 是的,我自己也很忙,redux 文档中的“如何减少样板”部分很好地解决了这个问题。在redux.js.org/docs/recipes/ReducingBoilerplate.html中搜索“终于可以自己写中间件了”
【解决方案2】:

我没有看到太多这方面的例子,我认为这绝对是需要更多报道的东西。我自己还没有实现身份验证,否则我会向您指出一些代码示例。但是我可以向您指出我收集的几个链接,它们可能会帮助您朝着正确的方向前进...

无论您如何执行身份验证,您都需要安全地存储您的访问、刷新和秘密令牌。在 iOS 上,我相信你会使用 keychain 来做到这一点,而对于 Android,它看起来像 KeyStore 就是这样。您可能会发现 oblador/react-native-keychain 很有帮助,尽管它还不支持 android it looks like it may support android soon

【讨论】:

  • Android 现在支持 react-native-keychain!
  • 掌声 @oblador
  • 嘿 @oblador 和 Chris,我想使用钥匙串在 2 个不同的反应原生应用程序之间共享通行证或任何字符串。我遇到了麻烦,你有什么建议吗?我也发布了这个问题 github.com/oblador/react-native-keychain/issues/45 。提前致谢。
【解决方案3】:

我实际上正在制作一个视频教程系列,它至少可以回答您提出的一些问题。可以在此处找到该视频以及脚本和示例代码:http://codecookbook.co/post/how-to-build-a-react-native-login-form-with-redux-pt1/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多