首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >无法使用devise-jwt进行身份验证

无法使用devise-jwt进行身份验证
EN

Stack Overflow用户
提问于 2020-04-17 02:16:44
回答 2查看 2K关注 0票数 1

我正在尝试让devisedevise-jwt get工作,这样我就可以在我的API only Rails应用程序中实现授权。

我已经安装了devisedevise-jwt两个gem。

我遵循了这篇博文中的说明:

https://medium.com/@mazik.wyry/rails-5-api-jwt-setup-in-minutes-using-devise-71670fd4ed03

我实现了作者在他的帖子中包含的请求规范,但我无法让它们通过。如果我在会话控制器中放入一个byebug,我会看到它说“用户在继续之前需要登录或注册”。

对我做错了什么有什么想法吗?

以下是相关文件:

routes.rb

代码语言:javascript
复制
Rails.application.routes.draw do

  namespace :api, path: '', defaults: {format: :json} do
      namespace :v1 do
        devise_for :users,
                   path: '',
                   path_names: {
                     sign_in: 'signin',
                     sign_out: 'signout',
                     registration: 'signup'
                   }
        ...
      end
  end

控制器/api/v1/会话_控制器.rb

代码语言:javascript
复制
  class API::V1::SessionsController < Devise::SessionsController
    respond_to :json

    private

    def respond_with(resource, _opts = {})
      render json: resource
    end

    def respond_to_on_destroy
      head :no_content
    end
  end

models/user.rb

代码语言:javascript
复制
class User < ApplicationRecord
      devise  :confirmable, :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :jwt_authenticatable, jwt_revocation_strategy: JwtBlacklist

      ...
  end

models/jwt_blacklist.rb

代码语言:javascript
复制
  class JwtBlacklist < ApplicationRecord
    include Devise::JWT::RevocationStrategies::Blacklist

    self.table_name = 'jwt_blacklist'
  end

config/initializers/devise.rb

代码语言:javascript
复制
  Devise.setup do |config|

    # Setup for devise JWT token authentication
    config.jwt do |jwt|
      jwt.secret = Rails.application.secret_key_base
      jwt.dispatch_requests = [
        ['POST', %r{^*/signin$}]
      ]
      jwt.revocation_requests = [
        ['DELETE', %r{^*/signout$}]
      ]
      jwt.expiration_time = 1.day.to_i
    end

    config.navigational_formats = []

    ...

  end

spec/request/authentication_spec.rb

代码语言:javascript
复制
  require 'rails_helper'

  describe 'POST /v1/signin', type: :request do
    let(:user) { create(:user) }
    let(:url) { '/v1/signin' }
    let(:params) do
      {
        user: {
          email: user.email,
          password: user.password
        }
      }
    end

    context 'when params are correct' do
      before do
        post url, params: params
      end

      it 'returns 200' do
        expect(response).to have_http_status(200)
      end

      it 'returns JTW token in authorization header' do
        expect(response.headers['Authorization']).to be_present
      end

      it 'returns valid JWT token' do
        decoded_token = decoded_jwt_token_from_response(response)
        expect(decoded_token.first['sub']).to be_present
      end
    end

    context 'when login params are incorrect' do
      before { post url }

      it 'returns unathorized status' do
        expect(response.status).to eq 401
      end
    end
  end

  describe 'DELETE /v1/signout', type: :request do
    let(:url) { '/v1/signout' }

    it 'returns 204, no content' do
      delete url
      expect(response).to have_http_status(204)
    end
  end

我希望测试能够通过,但是我得到了以下错误:

测试失败

代码语言:javascript
复制
  Failures:

    1) POST /v1/signin when params are correct returns 200
       Failure/Error: expect(response).to have_http_status(200)
         expected the response to have status code 200 but it was 401
       # ./spec/request/authentication_spec.rb:21:in `block (3 levels) in <top (required)>'

    2) POST /v1/signin when params are correct returns JTW token in authorization header
       Failure/Error: expect(response.headers['Authorization']).to be_present
         expected `nil.present?` to return true, got false
       # ./spec/request/authentication_spec.rb:25:in `block (3 levels) in <top (required)>'

    3) POST /v1/signin when params are correct returns valid JWT token
       Failure/Error: decoded_token = decoded_jwt_token_from_response(response)

       NoMethodError:
         undefined method `decoded_jwt_token_from_response' for #<RSpec::ExampleGroups::POSTV1Signin::WhenParamsAreCorrect:0x00007fec3d3ae158>
       # ./spec/request/authentication_spec.rb:29:in `block (3 levels) in <top (required)>'

  Finished in 0.76386 seconds (files took 3.31 seconds to load)
  5 examples, 3 failures

  Failed examples:

  rspec ./spec/request/authentication_spec.rb:20 # POST /v1/signin when params are correct returns 200
  rspec ./spec/request/authentication_spec.rb:24 # POST /v1/signin when params are correct returns JTW token in authorization header
  rspec ./spec/request/authentication_spec.rb:28 # POST /v1/signin when params are correct returns valid JWT token

EN

回答 2

Stack Overflow用户

发布于 2020-09-25 12:40:30

我不知道你是否找到了解决方案;但我留下了我所做的一个方法,它可能会有所帮助。

要特别注意这个问题,解决方案是改变:

代码语言:javascript
复制
decoded_token = decoded_jwt_token_from_response(response)

至:

代码语言:javascript
复制
decoded_token = JWT.decode(response.headers['authorization'].split(' ').last, Rails.application.credentials.jwt_secret, true)

因为我在文档或其他地方找不到任何东西,所以我选择了JWT提供的方法进行解码。另外,如果你看到我以不同的方式处理请求,但我认为这根本不是问题。

代码语言:javascript
复制
require 'rails_helper'
require "json"

RSpec.describe "POST /login", type: :request do
  
  let(:user) { User.create!(  username: 'usertest',
                              email: 'usertest@email.com',
                              password: 'passwordtest123',
                              password_confirmation: 'passwordtest123') }

  let(:url) { '/users/login' }
  let(:params) do
    {
      user: {
        login: user.email,
        password: user.password
      }       
    }
  end

  context 'when params are correct' do
    before do
      post url, params: params.to_json, headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
    end

    it 'returns 200' do
      expect(response).to have_http_status(200)
    end

    it 'returns JTW token in authorization header' do
      expect(response.headers['authorization']).to be_present
    end

    it 'returns valid JWT token' do
      token_from_request = response.headers['Authorization'].split(' ').last
      decoded_token = JWT.decode(token_from_request, Rails.application.credentials.jwt_secret, true)
      expect(decoded_token.first['sub']).to be_present
    end
  end

  context 'when login params are incorrect' do
    before { post url }
    
    it 'returns unathorized status' do
      expect(response.status).to eq 401
    end
  end
end

RSpec.describe 'DELETE /logout', type: :request do
  let(:url) { '/users/logout' }

  it 'returns 204, no content' do
    delete url
    expect(response).to have_http_status(204)
  end
end

RSpec.describe 'POST /signup', type: :request do
  let(:url) { '/users/signup' }
  let(:params) do
    {
      user: {
        username: 'usertest2',
        email: 'usertest2@email.com',
        password: 'passwordtest123',
        password_confirmation: 'passwordtest123'
      }
    }
  end

  context 'when user is unauthenticated' do
    before  {
              post url,
              params: params.to_json,
              headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
            }

    it 'returns 201' do
      expect(response.status).to eq 201
    end

    it 'returns a new user' do
      expect(response).to have_http_status :created
    end
  end

  context 'when user already exists' do
    before do
      post url,
      params: params.to_json,
      headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }

      post url,
      params: params.to_json,
      headers: { 'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json' }
    end

    it 'returns bad request status' do
      expect(response.status).to eq 400
    end

    it 'returns validation errors' do
      expect(response_body['errors'].first['title']).to eq('Bad Request')
    end
  end
end

PD:我把规范代码留给了register,只有几个不同之处(用户模型中的请求、url、用户名参数(这就是为什么我在登录请求中使用登录参数),我在一个单独的spec.rb文件中完成了所有的工作,...)敬https://medium.com/@mazik.wyry/rails-5-api-jwt-setup-in-minutes-using-devise-71670fd4ed03。请记住这一点。

票数 1
EN

Stack Overflow用户

发布于 2020-04-17 21:41:34

我相信在请求授权之前,您需要使用帮助程序sign_in user。检查https://github.com/heartcombo/devise控制器测试

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61257181

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档