如何使用 RSpec 为 JWT 验证应用程序做请求规范
How to do request spec for JWT authenticate app using RSpec
我有一个 Rails 5 API 唯一的应用程序并使用 knock 进行 JWT 身份验证。
完成模型和模型规格后,我开始做请求规格。
但是我不知道如何以正确的方式在请求规范中完成身份验证,
我的用户控制器,
module V1
class UsersController < ApplicationController
before_action :authenticate_user, except: [:create]
end
end
应用程序控制器,
class ApplicationController < ActionController::API
include Knock::Authenticable
include ActionController::Serialization
end
我最笨的方案(在rest请求之前调用get token请求获取JWT),
context 'when the request contains an authentication header' do
it 'should return the user info' do
user = create(:user)
post '/user_token', params: {"auth": {"email": user.email, "password": user.password }}
body = response.body
puts body # {"jwt":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0ODgxMDgxMDYsInN1YiI6MX0.GDBHPzbivclJfwSTswXhDkV0TCFCybJFDrjBnLIfN3Q"}
# use the retrieved JWT for future requests
end
end
如有任何建议,我们将不胜感激。
def authenticated_header(user)
token = Knock::AuthToken.new(payload: { sub: user.id }).token
{ 'Authorization': "Bearer #{token}" }
end
describe 'GET /users?me=true' do
URL = '/v1/users?me=true'
AUTH_URL = '/user_token'
context 'when the request with NO authentication header' do
it 'should return unauth for retrieve current user info before login' do
get URL
expect(response).to have_http_status(:unauthorized)
end
end
context 'when the request contains an authentication header' do
it 'should return the user info' do
user = create(:user)
get URL, headers: authenticated_header(user)
puts response.body
end
end
end
在 Lorem 的回答的帮助下,我能够为我的请求规范实现类似的东西。在这里分享它以供其他人查看替代实现。
# spec/requests/locations_spec.rb
require 'rails_helper'
RSpec.describe 'Locations API' do
let!(:user) { create(:user) }
let!(:locations) { create_list(:location, 10, user_id: user.id) }
describe 'GET /locations' do
it 'reponds with invalid request without JWT' do
get '/locations'
expect(response).to have_http_status(401)
expect(response.body).to match(/Invalid Request/)
end
it 'responds with JSON with JWT' do
jwt = confirm_and_login_user(user)
get '/locations', headers: { "Authorization" => "Bearer #{jwt}" }
expect(response).to have_http_status(200)
expect(json.size).to eq(10)
end
end
end
confirm_and_login_user(user)
在 request_spec_helper 中定义,它作为模块包含在 rails_helper.rb
:
中
# spec/support/request_spec_helper.rb
module RequestSpecHelper
def json
JSON.parse(response.body)
end
def confirm_and_login_user(user)
get '/users/confirm', params: {token: user.confirmation_token}
post '/users/login', params: {email: user.email, password: 'password'}
return json['auth_token']
end
end
我正在使用 jwt gem 生成我的令牌,如本 SitePoint 教程中所述 (https://www.sitepoint.com/introduction-to-using-jwt-in-rails/)
Lorem 的回答大部分对我有用。我在 get
上得到 unrecognized keyword
设置 headers:
。我修改了 authenticated_header
方法并将其放入 support/api_helper.rb
以便我可以重用它。修改是将auth token合并到request.headers
.
# spec/support/api_helper.rb
module ApiHelper
def authenticated_header(request, user)
token = Knock::AuthToken.new(payload: { sub: user.id }).token
request.headers.merge!('Authorization': "Bearer #{token}")
end
end
在测试 api 的每个规范文件中,我都包含 api_helper.rb。在测试有效身份验证的情况下,我在 get 语句之前调用 authenticated_header...
# spec/controllers/api/v2/search_controller_spec.rb
RSpec.describe API::V2::SearchController, type: :controller do
include ApiHelper
...
describe '#search_by_id' do
context 'with an unauthenticated user' do
it 'returns unauthorized' do
get :search_by_id, params: { "id" : "123" }
expect(response).to be_unauthorized
end
end
context 'with an authenticated user' do
let(:user) { create(:user) }
it 'renders json listing resource with id' do
expected_result = { id: 123, title: 'Resource 123' }
authenticated_header(request, user)
get :search_by_id, params: { "id" : "123" }
expect(response).to be_successful
expect(JSON.parse(response.body)).to eq expected_result
end
end
第二个测试中的关键行是...
authenticated_header(request, user)
get :search_by_id, params: { "id" : "123" }
我有一个 Rails 5 API 唯一的应用程序并使用 knock 进行 JWT 身份验证。
完成模型和模型规格后,我开始做请求规格。
但是我不知道如何以正确的方式在请求规范中完成身份验证,
我的用户控制器,
module V1
class UsersController < ApplicationController
before_action :authenticate_user, except: [:create]
end
end
应用程序控制器,
class ApplicationController < ActionController::API
include Knock::Authenticable
include ActionController::Serialization
end
我最笨的方案(在rest请求之前调用get token请求获取JWT),
context 'when the request contains an authentication header' do
it 'should return the user info' do
user = create(:user)
post '/user_token', params: {"auth": {"email": user.email, "password": user.password }}
body = response.body
puts body # {"jwt":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0ODgxMDgxMDYsInN1YiI6MX0.GDBHPzbivclJfwSTswXhDkV0TCFCybJFDrjBnLIfN3Q"}
# use the retrieved JWT for future requests
end
end
如有任何建议,我们将不胜感激。
def authenticated_header(user)
token = Knock::AuthToken.new(payload: { sub: user.id }).token
{ 'Authorization': "Bearer #{token}" }
end
describe 'GET /users?me=true' do
URL = '/v1/users?me=true'
AUTH_URL = '/user_token'
context 'when the request with NO authentication header' do
it 'should return unauth for retrieve current user info before login' do
get URL
expect(response).to have_http_status(:unauthorized)
end
end
context 'when the request contains an authentication header' do
it 'should return the user info' do
user = create(:user)
get URL, headers: authenticated_header(user)
puts response.body
end
end
end
在 Lorem 的回答的帮助下,我能够为我的请求规范实现类似的东西。在这里分享它以供其他人查看替代实现。
# spec/requests/locations_spec.rb
require 'rails_helper'
RSpec.describe 'Locations API' do
let!(:user) { create(:user) }
let!(:locations) { create_list(:location, 10, user_id: user.id) }
describe 'GET /locations' do
it 'reponds with invalid request without JWT' do
get '/locations'
expect(response).to have_http_status(401)
expect(response.body).to match(/Invalid Request/)
end
it 'responds with JSON with JWT' do
jwt = confirm_and_login_user(user)
get '/locations', headers: { "Authorization" => "Bearer #{jwt}" }
expect(response).to have_http_status(200)
expect(json.size).to eq(10)
end
end
end
confirm_and_login_user(user)
在 request_spec_helper 中定义,它作为模块包含在 rails_helper.rb
:
# spec/support/request_spec_helper.rb
module RequestSpecHelper
def json
JSON.parse(response.body)
end
def confirm_and_login_user(user)
get '/users/confirm', params: {token: user.confirmation_token}
post '/users/login', params: {email: user.email, password: 'password'}
return json['auth_token']
end
end
我正在使用 jwt gem 生成我的令牌,如本 SitePoint 教程中所述 (https://www.sitepoint.com/introduction-to-using-jwt-in-rails/)
Lorem 的回答大部分对我有用。我在 get
上得到 unrecognized keyword
设置 headers:
。我修改了 authenticated_header
方法并将其放入 support/api_helper.rb
以便我可以重用它。修改是将auth token合并到request.headers
.
# spec/support/api_helper.rb
module ApiHelper
def authenticated_header(request, user)
token = Knock::AuthToken.new(payload: { sub: user.id }).token
request.headers.merge!('Authorization': "Bearer #{token}")
end
end
在测试 api 的每个规范文件中,我都包含 api_helper.rb。在测试有效身份验证的情况下,我在 get 语句之前调用 authenticated_header...
# spec/controllers/api/v2/search_controller_spec.rb
RSpec.describe API::V2::SearchController, type: :controller do
include ApiHelper
...
describe '#search_by_id' do
context 'with an unauthenticated user' do
it 'returns unauthorized' do
get :search_by_id, params: { "id" : "123" }
expect(response).to be_unauthorized
end
end
context 'with an authenticated user' do
let(:user) { create(:user) }
it 'renders json listing resource with id' do
expected_result = { id: 123, title: 'Resource 123' }
authenticated_header(request, user)
get :search_by_id, params: { "id" : "123" }
expect(response).to be_successful
expect(JSON.parse(response.body)).to eq expected_result
end
end
第二个测试中的关键行是...
authenticated_header(request, user)
get :search_by_id, params: { "id" : "123" }