jjzjj

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

coder 2024-05-12 原文

新的 react 和使用身份验证/登录的应用程序的工作。它目前工作但感觉一起被黑了。现在我的 isAuthenticated 状态位于我的 routes.js 中,如下所示:

class Routes extends Component {

    constructor(props) {
        super(props);

        this.state = {
            isAuthenticated: false,
         }
     }

在我的登录页面上,我需要知道用户何时通过身份验证才能将他们重定向到 home 页面。允许访问和操作此 isAuthenticated 状态的最佳设计模式是什么?我目前的设置方式是我有一个函数可以在 routes.js 中设置状态并将状态作为 prop 发送,如下所示:

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

在路由器下面...

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

是的,我知道这是一个糟糕的设计,因为这会改变本应不可变的 Prop 值。这也很糟糕,因为当我在 login.js 中更改此值时,它会导致多次不必要的重新呈现。我应该将 isAuthenticated 声明为某种类型的全局变量吗?顺便说一句,我没有使用任何状态管理。

编辑:我正在设置 isAuthenticated 基于我的服务器的响应,确认正确的登录名/密码组合。

最佳答案

处理 isAuthenticated 仅在state 表示用户每次刷新页面都将未通过身份验证。这不是真正的用户友好! :)

因此,登录页面应该存储一个access_token (来自您的后端) cookies 中或 localStorage 浏览器的。一个access_token证明用户已通过身份验证并验证其身份。你通常会通过这个 access_token对您的服务器的每个下一个请求,以检查该用户是否被允许访问他请求的数据,或者是否被允许创建、编辑和删除他试图创建、编辑和删除的东西。

然后你可以查看这个access_token在所有其他页面上,如果他不再通过身份验证,则将用户重定向到登录页面。


关于 access_token 之间区别的简要说明 refresh_token 这将帮助您理解下面的代码,但如果您已经熟悉它,请随时跳过

您的后端可能使用 OAuth2 ,这是当今最常见的身份验证协议(protocol)。与 OAuth2 ,您的应用向服务器发出第一个请求,其中包含用户的用户名和密码以进行身份​​验证。一旦用户通过身份验证,他将收到 1) access_token。 ,通常在一小时后过期,以及 2) refresh_token ,它会在很长一段时间(几小时、几天)后过期。当 access_token过期,而不是再次询问用户他的用户名和密码,你的应用程序发送 refresh_token到服务器获取新的access_token对于这个用户。


关于 cookies 之间差异的简要说明 localStorage 也可以随意跳过它!

localStorage是两者之间最新的技术。这是一个简单的键/值持久化系统,似乎非常适合存储 access_token及其值(value)。但我们还需要保留其到期日期。我们可以存储名为 expires 的第二个键/值对但在我们这边处理会更合乎逻辑。

另一方面,cookies有一个本地人expires属性(property),这正是我们所需要的! cookies是一项古老的技术,对开发人员不是很友好,所以我个人使用 js-cookie ,这是一个小型图书馆来操纵cookies .它也使它看起来像一个简单的键/值持久性系统:Cookies.set('access_token', value)然后Cookies.get('access_token') .

其他亲为cookies : 他们是交叉的subdomains !如果您的登录应用程序是 login.mycompany.com并且您的主要应用程序是 app.mycompany.com , 然后你可以创建一个 cookie在登录应用程序上并从主应用程序访问它。这对于 LocalStorage 是不可能的.


以下是我用于身份验证的一些方法和特殊的 React 组件:

isAuthenticated()

import Cookies from 'js-cookie'

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

验证()

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
}

认证路由

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

关于javascript - React - 处理登录和身份验证的最佳方式是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49819183/

有关javascript - React - 处理登录和身份验证的最佳方式是什么?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  3. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  4. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  5. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  6. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  7. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  10. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

随机推荐