首页 > 解决方案 > 如何使用带有设计和 JWT 的 Rails 6 API 成功注销

问题描述

我已经被困了好几天了,但我找不到使用 JWT 从设计会话中注销的正确解决方案。我有一个用反应做的前面,在登录和搜索时一切正常,但是当我注销页面时,如果我不进行刷新,我就无法登录。我将设计会话控制器的代码放在应用程序控制器、路由和中间件构建旁边,以便在我的前端使用 redux(我正在使用 React)。提前谢谢你,我需要别的东西,让我知道。

设计::会话控制器

# frozen_string_literal: true

class Api::SessionsController < Devise::SessionsController
  respond_to :json, :html
  
  # GET /resource/sign_in
  # def new
  #   super
  # end

  # POST /resource/sign_in
  # def create
  #   super
  # end

  # DELETE /resource/sign_out
  # def destroy
  #   super
  # end

  # protected

  private
    def revoke_token(token)
      # Decode JWT to get jti and exp values.
      begin
        secret = Rails.application.credentials.jwt_secret
        jti = JWT.decode(token, secret, true, algorithm: 'HS256', verify_jti: true)[0]['jti']
        exp = JWT.decode(token, secret, true, algorithm: 'HS256')[0]['exp']
        user = User.find(JWT.decode(token, secret, true, algorithm: 'HS256')[0]['sub'])
        sign_out user
        # Add record to blacklist.
        time_now = Time.zone.now.to_s.split(" UTC")[0]
        sql_blacklist_jwt = "INSERT INTO jwt_blacklist (jti, exp, created_at, updated_at) VALUES ('#{ jti }', '#{ Time.at(exp) }', '#{time_now}', '#{time_now}');"
        ActiveRecord::Base.connection.execute(sql_blacklist_jwt)
      rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError
        head :unauthorized
      end
    end

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

    def respond_to_on_destroy
      token = request.headers['Authorization'].split("Bearer ")[1]
      revoke_token(token)
      request.delete_header('Authorization')
      render json: :ok
    end
end

应用控制器

class ApplicationController < ActionController::API
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :authenticate_user

  protected

  def configure_permitted_parameters
    added_attrs = %i[username email password password_confirmation remember_me]
    devise_parameter_sanitizer.permit(:sign_up, keys: added_attrs)
    devise_parameter_sanitizer.permit(:account_update, keys: added_attrs)
  end

  private

  def authenticate_user
    if request.headers['Authorization'].present?
      token = request.headers['Authorization'].split("Bearer ")[1]
      begin
        jwt_payload = JWT.decode(token, Rails.application.credentials.jwt_secret).first
        @current_user_id = jwt_payload['sub']
      rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError
        head :unauthorized
      end
    end
  end

  def authenticate_user!(options = {})
    head :unauthorized unless signed_in?
  end

  def current_user
    @current_user ||= super || User.find(@current_user_id)
  end

  def signed_in?
    @current_user_id.present?
  end
end

路线.rb

Rails.application.routes.draw do
  devise_for :users, skip: %i[registrations sessions passwords]

  namespace :api do
    devise_scope :user do
      post 'signup', to: 'registrations#create'
      post 'login', to: 'sessions#create'
      delete 'logout', to: 'sessions#destroy'
      get 'login', to: 'sessions#create'
    end

    resources :notes
    resources :searches
    
    get 'get_places', to: 'searches#get_places'
  end
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

中间件.js

import * as constants from './constants';

import axios from 'axios';
import { logoutUser } from './actions/authActionCreators'

export const apiMiddleware = ({ dispatch, getState }) => next => action => {
    if (action.type !== constants.API) return next(action);

    dispatch({ type: constants.TOGGLE_LOADER });
    const BASE_URL = 'http://localhost:3001';
    const AUTH_TOKEN = getState().user.token;
    if (AUTH_TOKEN)
        axios.defaults.headers.common['Authorization'] = `Bearer ${AUTH_TOKEN}`;

    const { url, method, success, data, postProcessSuccess, postProcessError } = action.payload;

    console.log('AUTH_TOKEN '+AUTH_TOKEN);
    console.log('url '+url);

    axios({
        method,
        url: BASE_URL + url,
        data: data ? data : null,
        headers: {
            'Content-Type': 'application/json', 'Accept': '*/*'
        }
    }).then((response) => {
        dispatch({ type: constants.TOGGLE_LOADER });
        if (success) dispatch(success(response));
        if (postProcessSuccess) postProcessSuccess(response);
    }).catch(error => {
        dispatch({ type: constants.TOGGLE_LOADER });
        if (typeof(error.response) === "undefined") {
            console.warn(error);
            postProcessError('An error has ocurred');
        } else {
            if (error.response && error.response.status === 403)
                dispatch(logoutUser());
            if (error.response.data.message) {
                if (postProcessError) postProcessError(error.reponse.data.message);
            }
        }
    })
};

标签: ruby-on-railsreactjsreduxdevisejwt

解决方案


推荐阅读