Sinatra Songs API 教程:使用 Auth0 添加授权保护 CRUD API

作者:API传播员 · 2025-10-22 · 阅读时间:5分钟

在这篇文章中,我们将学习如何使用 Auth0 为 Sinatra API 添加授权功能,并保护其端点。我们将构建一个名为 Sinatra Songs API 的歌曲 CRUD API,展示如何通过 Sinatra 框架实现基本的 API 功能,并结合 Auth0 来实现安全性。


项目要求

在本项目中,我们将使用以下技术和工具:

  • Ruby(推荐版本 3.0 或更高)
  • Sinatra(轻量级 Ruby Web 框架)
  • Auth0(身份验证和授权服务)
  • Postmancurl(用于测试 API)

构建歌曲 API

创建项目

首先,在终端中创建一个名为 sinatra-auth0-songs-api 的新文件夹,并将其设为当前目录。

mkdir sinatra-auth0-songs-api
cd sinatra-auth0-songs-api

安装 Sinatra

创建一个 Gemfile 文件,用于管理项目依赖:

# Gemfile
source 'https://rubygems.org'

ruby File.read('.ruby-version').stripgem 'sinatra', '~> 3.0', '>= 3.0.2'
gem 'puma'

然后,创建 .ruby-version 文件,指定 Ruby 版本:

3.1.2

运行以下命令安装依赖:

bundle install

至此,Sinatra 已成功安装!🎉


创建歌曲模型

在项目中创建一个 models 文件夹,并添加 song.rb 文件:

# models/song.rb
# frozen_string_literal: true

class Song
  attr_accessor :id, :name, :url  def initialize(id, name, url)
    @id = id
    @name = name
    @url = url
  end  def to_json(*_args)
    { id: id, name: name, url: url }.to_json
  end
end

实现 CRUD API

创建一个 api.rb 文件,作为 API 的主入口:

delete ‘/songs/:id’ do
{ todo: :implementation }.to_json
end

require 'sinatra'
require 'json'

before do
content_type 'application/json'
end

get '/songs' do
{ todo: :implementation }.to_json
end

get '/songs/:id' do
{ todo: :implementation }.to_json
end

post '/songs' do
{ todo: :implementation }.to_json
end

put '/songs/:id' do
{ todo: :implementation }.to_json
end

delete '/songs/:id' do
{ todo: :implementation }.to_json
end

运行以下命令启动服务器:

ruby api.rb

服务器启动后,访问 http://localhost:4567 即可测试 API。


使用 songs.json 填充数据

为了给 API 提供数据,我们将使用一个名为 songs.json 的文件,该文件包含 Frank Sinatra 的热门歌曲。创建一个 helpers 文件夹,并添加 songs_helper.rb 文件:

# helpers/songs_helper.rb
# frozen_string_literal: true

require_relative '../models/song'
require 'json'class SongsHelper
  def self.songs
    filepath = File.join(File.dirname(__FILE__), '../songs.json')
    file = File.read(filepath)
    data = JSON.parse(file)['songs']    data.map { |song| Song.new(song['id'], song['name'], song['url']) }  end
end

api.rb 中加载 songs.json 数据:

# api.rb
require_relative 'helpers/songs_helper'

songs ||= SongsHelper.songsget '/songs' do
  songs.to_json
end

添加授权功能

配置 Auth0

  1. 登录 Auth0 管理控制台
  2. 创建一个新的 API:
    • 名称:Sinatra Songs API
    • 标识符https://sinatra-auth0-songs-api
    • 签名算法:RS256

记录下 标识符域名,稍后会用到。


安装依赖

Gemfile 中添加以下依赖:

gem 'dotenv'
gem 'jwt'

运行以下命令安装依赖:

bundle install

创建 .env 文件,存储 Auth0 的配置信息:

AUTH0_DOMAIN=your-auth0-domain
AUTH0_AUDIENCE=https://sinatra-auth0-songs-api

验证访问令牌

创建一个 auth0_client_helper.rb 文件,用于验证访问令牌:

jwks_hash = JSON.parse(jwks_response.body).transform_keys(&:to_sym)
decoded_token = decode_token(token, jwks_hash)
Response.new(decoded_token, nil)

rescue JWT::VerificationError, JWT::DecodeError
Response.new(nil, Error.new(‘Bad credentials’, 401))
end
end

require 'jwt'
require 'net/http'

class Auth0ClientHelper
Error = Struct.new(:message, :status)
Response = Struct.new(:decoded_token, :error)

def self.domain_url
"https://#{ENV['AUTH0_DOMAIN']}/"
end

def self.decode_token(token, jwks_hash)
JWT.decode(token, nil, true, {
algorithm: 'RS256',
iss: domain_url,
verify_iss: true,
aud: ENV['AUTH0_AUDIENCE'],
verify_aud: true,
jwks: { keys: jwks_hash[:keys] }
})
end

def self.get_jwks
jwks_uri = URI("#{domain_url}.well-known/jwks.json")
Net::HTTP.get_response(jwks_uri)
end

def self.validate_token(token)
jwks_response = get_jwks
return Response.new(nil, Error.new('Unable to verify credentials', :internal_server_error)) unless jwks_response.is_a?(Net::HTTPSuccess)

jwks_hash = JSON.parse(jwks_response.body).transform_keys(&:to_sym)
decoded_token = decode_token(token, jwks_hash)
Response.new(decoded_token, nil)
rescue JWT::VerificationError, JWT::DecodeError
Response.new(nil, Error.new('Bad credentials', 401))
end
end

保护 API 端点

api.rb 中添加授权逻辑:

# api.rb
require_relative 'helpers/auth0_client_helper'

helpers do
  def authorize!
    token = token_from_request
    validation_response = Auth0ClientHelper.validate_token(token)
    halt validation_response.error.status, { message: validation_response.error.message }.to_json if validation_response.error
  end  def token_from_request
    authorization_header = request.env['HTTP_AUTHORIZATION']
    halt 401, { message: 'Authorization header missing' }.to_json unless authorization_header    schema, token = authorization_header.split
    halt 401, { message: 'Invalid authorization header format' }.to_json unless schema.downcase == 'bearer'    token
  end
endbefore method: %i[post put delete] do
  authorize!
end

总结

通过本文,我们学习了如何使用 Sinatra 构建一个基本的 CRUD API,并结合 Auth0 实现基于令牌的授权功能。希望这篇文章对您有所帮助!如果您对其他 Ruby 框架感兴趣,欢迎在评论中分享您的想法!

原文链接: https://auth0.com/blog/add-authorization-to-sinatra-api-using-auth0/