使用 React 时,rails api-only 应用程序中出现 401 未经授权的错误

Posted

技术标签:

【中文标题】使用 React 时,rails api-only 应用程序中出现 401 未经授权的错误【英文标题】:401 Unauthorized error in rails api-only app when using React 【发布时间】:2018-02-19 19:12:41 【问题描述】:

我正在尝试使用 rails api-ony 应用程序作为后端,使用 React js 作为前端。我正在使用 axios 发送/接收请求。最初我什至无法登录,然后经过多次谷歌搜索,我发现 this gem 它与 CORS 有关,它解决了我的 POST 请求错误。 当我在 rails 应用程序中执行 POST 请求时,我能够成功登录,并且我得到了令牌作为回报(这适用于 POSTMAN 应用程序)。现在,当我使用 React 应用程序执行 GET 请求并将该令牌作为 Header 传递时,它不断给我401 Unauthorized error,然后在经过多次谷歌搜索后我又看到了this SO post。它没有解决问题。我错过了什么?请说明此问题的原因。 这是application_controller.rb:

class ApplicationController < ActionController::API
    include ActionController::MimeResponds

    before_action :authenticate_request, :current_user, :cors_preflight_check
    attr_reader :current_user

    #before_filter :current_user, :cors_preflight_check
  after_action :cors_set_access_control_headers

# For all responses in this controller, return the CORS access control headers.

def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
    headers['Access-Control-Request-Method'] = '*'
    headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
  headers['Access-Control-Max-Age'] = "1728000"
end

# If this is a preflight OPTIONS request, then short-circuit the
# request, return only the necessary headers and return an empty
# text/plain.

def cors_preflight_check
  if request.method == :options
        headers['Access-Control-Allow-Origin'] = '*'
        headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
        headers['Access-Control-Request-Method'] = '*'
        headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
    headers['Access-Control-Max-Age'] = '1728000'
    render :text => '', :content_type => 'text/plain'
  end
end

    require 'date'
            require 'json'
            require 'groupdate'

    @@PRESENT_DATE=Date.parse('2016-12-31T00:00:00.000Z')
    @@MONTHLY_SELECTOR="SELECT CONCAT(MONTHNAME(invoiceDate),' - ',YEAR(invoiceDate)), SUM(invoiceAmt) FROM invoices GROUP BY YEAR(invoiceDate), MONTH(invoiceDate)"
    @@QUARTERLY_SELECTOR="SELECT SUM(invoiceAmt) AS invoiceTotal, CONCAT('Q',QUARTER(invoiceDate), '(', YEAR(invoiceDate), ')') FROM invoices GROUP BY YEAR(invoiceDate), QUARTER(invoiceDate) ORDER BY YEAR(invoiceDate), QUARTER(invoiceDate)"
    @@YEARLY_SELECTOR="SELECT SUM(invoiceAmt) AS invoiceTotal, YEAR(invoiceDate) AS Year FROM invoices GROUP BY YEAR(invoiceDate) ORDER BY YEAR(invoiceDate)"

    private

    def authenticate_request
        @current_user = AuthorizeApiRequest.call(request.headers).result
        render json:  error: 'Not Authorized' , status: 401 unless @current_user
    end
end

在我的反应应用程序中,我有这个:

import React,  Component  from 'react';
import '../App.css';
var axios = require('axios');

class Login extends Component 
  constructor(props)
    super(props);
    //this.state = isToggleOn: true;
    this.loadDashboard = this.loadDashboard.bind(this);
    this.handleOnSubmit = this.handleOnSubmit.bind(this);
  

  loadDashboard(token)
    axios(
      method:'get',
      url:'http://localhost:3000/api/dashboard',
      Authorization: 
        Authorization: token,
      ,
    )
     .then(function (response) 
       console.log(response);
     )
     .catch(function (error) 
       console.log("Error in loading Dashboard "+error.response.status);
     );
  

  handleOnSubmit = () => 
     console.log("submittwed");
     axios(
       method:'post',
       url:'http://localhost:3000/authenticate',
       data: 
         email: 'test@mail.com',
         password: 'apple'
       ,
     )
      .then((response) => 
        var token = response.data.auth_token;
        console.log(token);
        this.loadDashboard(token);
      )
      .catch(function (error) 
        console.log("Error in login "+error);
      );
   

  render() 
    return (
      <div>
         Username: <input type="email" name="fname" /><br />
         Password: <input type="password" name="lname" /><br />
         <button onClick=this.handleOnSubmit>LOG IN</button>
      </div>
    );
  


export default Login;      

完整的标题响应:

Request URL:http://localhost:3000/api/dashboard
Request Method:GET
Status Code:401 Unauthorized
Remote Address:127.0.0.1:3000
Referrer Policy:no-referrer-when-downgrade
Response Headers
view source
Access-Control-Allow-Methods:GET, POST, OPTIONS
Access-Control-Allow-Origin:*
Access-Control-Expose-Headers:
Access-Control-Max-Age:1728000
Cache-Control:no-cache
Content-Type:application/json; charset=utf-8
Transfer-Encoding:chunked
Vary:Origin
X-Request-Id:f78a4c29-ef9a-446c-8771-9e2b70d41757
X-Runtime:0.045998
Request Headers
view source
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Host:localhost:3000
Origin:http://localhost:3001
Referer:http://localhost:3001/
User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/60.0.3112.113 Safari/537.36

Rails 版本:Rails 5.1.3

有没有其他人遇到过类似的情况?? 我该如何解决这个问题? 有没有更简单/更好的方法?

请帮我解决这个问题。

【问题讨论】:

您是否必须在headers 键中添加Authorization 标头而不是data @HemersonCarlin 我刚刚做了。但它没有用。还更新了我的问题 请显示完整的错误响应。 401 可能是各种各样的东西,但内容正文和标题通常会很明显。请务必标记正确的 Rails 版本。你真的在使用 Rails 3.x 吗? 【参考方案1】:

您缺少要发送到您的 api 调用的 headers 键。

axios(
  method:'get',
  url:'http://localhost:3000/api/dashboard',
  headers: 
    Authorization: `Bearer $token`,
  ,
)
  .then(function (response) 
    console.log(response)
  )
  .catch(function (error) 
    console.log("Error in loading Dashboard "+error.response.status)
  )

【讨论】:

以上是关于使用 React 时,rails api-only 应用程序中出现 401 未经授权的错误的主要内容,如果未能解决你的问题,请参考以下文章

Rails 6 API + React + Google login:登录后如何授权用户访问某些服务器路由?

使用 Rails 和 React 时如何在标头中获取 JWT 令牌

如何在 Webpacker 中使用 Rails Url 助手/Rails 5.1 中的 React-rails

React + Rails CORS 问题

Rails 仅在生产时才在页面重新加载时丢失会话变量 current_user_id(React 16.13、Rails 6、Heroku)

如何使用 React on rails 解决代理错误