Rails-pangu
是一个基于 Rails 6(API Only) 框架搭建的一站式服务开发的技术解决方案,它整合了 Devise, JWT(JSON Web Tokens), Postgres, Redis, Docker, Rspec, RuboCop, CircleCI 等多项业界端技术,它是后端项目开发的起点,可作为开发者强有力的生产工具。
盘古在中国古神话中是万物创始之神1. 正如盘古,
Rails-pangu
意在成为您新rails项目的starter kit,它帮您扫除那些有难度但重复的活儿。
~ $ git clone https://github.com/ruilisi/rails-pangu
~ $ cd rails-pangu
~ $ bundle install
~ $ rails db:create db:migrate db:seed
~ $ rspec
尝试开发与测试来获得rails-pang
的一手体验。
与rails 5
不同,rails 6
是未来发展的必然趋势。
🚀 Devise
来自Devise官方文档:
Devise是基于Warden的Rails身份验证解决方案。它具有以下特点:
- 基于Rack;
- 是一个基于Rails引擎的完整MVC解决方案;
- 允许您同时运行多个模型;
- 功能模块化,只需要调用所需要的模块。
由此可见,devise
提供了一套完整的符合行业标准且方便使用的身份验证方式。
JSON Web Tokens,是目前最流行的跨域认证解决方案。
实现了将devise
和jwt
技术融合,虽然也有其他开发者通过其它方式实现了两者的融合运用,但都效果欠佳。
我们在 app/models/jwt_denylist.rb 里面通过使用redis实现了 devise-jwt的 denylist strategy
。
使用postgres作为默认数据库。当一个Web服务器的流量变得很大时,sqlite3显然无法满足我们的需求。
Ruby行为驱动测试框架,让TDD高效有趣。
🚀 RuboCop
Ruby代码静态分析和格式工具,基于社区Ruby样式准侧
🚀 CircleCI
CircleCI是一个行业流行的持续集成和持续部署的开发工具,方便与团队成员之间代码交流,密切协作。
在本项目中,我们通过CircleCI用rspec
和RuboCop
来测试Rails-pangu
的代码库。
将Ruby对象设置为测试用例。
Dockerfile及与之配套的定制化特性被加入到本项目中:
本项目提供的Docker构建方案包含了两大优化:
- 原本的Gem源
https://rubygems.org
被镜像到https://gems.ruby-china.com
,这既帮助中国开发者加速Gem构建速度,也作为一个样例演示如何使用Gem镜像。
本项目添加了docker-compose.yml
,它包括了容器web
, postgres
, redis
。
web
容器中无法运行rspec
和其他一些命令,因为docker镜像通过运行bundle config set without 'development test'
只安装生产模式的gems。
~ $ docker-compose up -d
~ $ docker-compose exec web rails db:create db:migrate db:seed
接下来你就可以运行开发与测试中的步骤了,因为3000
端口被映射且暴露了。
🚀 Puma
Puma是一个简单、快速、线程化、高度并发的HTTP1.1服务器,用于Ruby/Rack应用的开发。
几乎所有的Web项目都使用redis
作为存储系统,因为它快速、高效、简洁。
config/cors.rb
被修改为允许任何访问源,且任何请求头中的Authorization
也被允许。
你可以添加cron job 在 bin/gen_cronjobs.rb
, 样例如下.
puts [
"59 * * * * ruby script",
"*/10 * * * * ruby script"
].map { |job|
*schedule, cmd = job.split(' ')
"#{schedule.join(' ')} cd /usr/src/app; rails runner \"Util.run_once('#{cmd}')\""
}.join("\n")
如果你想运行 bash 脚本,你可以把 cd /usr/src/app; rails runner \"Util.run_once('#{cmd}')\"
替换为你自定义的命令.
除了我们提供的默认角色之外,我们还允许developer创建他们的自定义角色。
由于redis的访问内存的性能极高,redis是用来实现denylist
的一个好的选择。在jwt_denylist
中,我们用redis实现了黑名单。通过将redis
的过期时间设置为与jwt token
的过期时间相同,可以在令牌过期时自动从redis中删除此令牌。
def self.jwt_revoked?(payload, user)
# Check if in the denylist
$redis.get("user_denylist:#{user.id}:#{payload['jti']}").present?
end
def self.revoke_jwt(payload, user)
# REVOKE JWT
expiration = payload['exp'] - payload['iat']
$redis.setex("user_denylist:#{user.id}:#{payload['jti']}", expiration, payload['jti'])
end
你也可以通过自己的策略实现黑名单。你只需要重写两个方法:jwt-revoked?
以及jwt-denylist.rb
中的revoke-jwt
,这两个方法都接受jwt负载和user
记录作为参数。
def self.jwt_revoked?(payload, user)
# Does something to check whether the JWT token is revoked for given user
end
def self.revoke_jwt(payload, user)
# Does something to revoke the JWT token for given user
end
您可以在device.rb
中配置dispatch
请求。当配置它时,您需要说明哪些请求将为以前经过身份验证的用户分派令牌(通常通过其他一些Warden策略,例如需要用户名和电子邮件参数的策略)。配置它时,需要将请求路径添加到dispath_requests
。
jwt.dispatch_requests = [['POST', %r{^users/sign_in$}]]
您可以在device.rb
中配置dispatch
请求。配置时,先将请求路径添加到撤销请求中,并说明哪些请求需要取消传入JWT令牌。
jwt.revocation_requests = [['DELETE', %r{^users/sign_out$}]]
user
记录还可以实现jwt_payload
方法,这使它有机会向JWT负载添加一些内容。
def jwt_payloads
# { 'foo' => 'bar' }
end
在user
记录上添加一个hook方法on_jwt_dispatch
。它在用户调用令牌时执行,并将令牌和有效负载作为参数。这个方法也会在你调用dispatch_requests
访问路由时被调用。
def on_jwt_dispatch(token, payload)
# do_something(token, payload)
end
要求
- rails服务器运行:
rails s
- 安装
httpie
~ $ http -b localhost:3000/ping
pong
~ $ http -b post localhost:3000/users user:='{"email":"user@test.com","password":"Test1aBc"}'
{
"created_at": "2020-10-10T05:43:20.349Z",
"email": "user@test.com",
"id": 1,
"updated_at": "2020-10-10T05:43:20.349Z"
}
~ $ http post localhost:3000/users/sign_in user:='{"email":"user@test.com","password":"Test1aBc"}'
HTTP/1.1 200 OK
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwic2NwIjoidXNlciIsImF1ZCI6bnVsbCwiaWF0IjoxNjAyMzE3ODYxLCJleHAiOjE2MDI0MDQyNjEsImp0aSI6IjNkOGY4ZThkLTY2YjUtNGE5Ny05YzkzLTUxZmFmMGQyMTM1YSJ9.Q-HWFNtLtfNO2iZsTRBfmlJlBBxHWTwrSlTjBaS6GNI
Cache-Control: max-age=0, private, must-revalidate
Content-Type: application/json; charset=utf-8
ETag: W/"df30d418ad05c15dbfdc6e34ef53f723"
Referrer-Policy: strict-origin-when-cross-origin
Transfer-Encoding: chunked
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: SAMEORIGIN
X-Permitted-Cross-Domain-Policies: none
X-Request-Id: 957a1c92-c5a8-4607-81df-6ca70ba9b846
X-Runtime: 0.193702
X-XSS-Protection: 1; mode=block
{
"created_at": "2020-10-10T05:43:20.349Z",
"email": "user@test.com",
"id": 1,
"updated_at": "2020-10-10T05:43:20.349Z"
}
用POST users/sign_in
得到的bearer(eyJhbGciOiJIUzI1NiJ9...
)来GET auth_ping
:
~ $ http -b localhost:3000/auth_ping "Authorization:Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwic2NwIjoidXNlciIsImF1ZCI6bnVsbCwiaWF0IjoxNjAyMzE3ODYxLCJleHAiOjE2MDI0MDQyNjEsImp0aSI6IjNkOGY4ZThkLTY2YjUtNGE5Ny05YzkzLTUxZmFmMGQyMTM1YSJ9.Q-HWFNtLtfNO2iZsTRBfmlJlBBxHWTwrSlTjBaS6GNI"
pong
不用bearer来GET
auth_ping
会得到401 Unauthorized
:
~ $ http localhost:3000/auth_ping [ruby-2.7.2]
HTTP/1.1 401 Unauthorized
Cache-Control: no-cache
Content-Type: */*; charset=utf-8
Transfer-Encoding: chunked
X-Request-Id: 8c21f5f2-f385-4b0b-b1f6-478ef06de256
X-Runtime: 0.003266
You need to sign in or sign up before continuing.
- LINGTI (https://lingti666.com/): 灵缇加速器是一款游戏加速器,包含PC、Mac、iOS、Android、路由器插件、路由盒子客户端。灵缇iOS和Android客户端支持Switch游戏热点加速,让你享受极致的联机和下载体验。
- eSheep (https://esheeps.com/): 电子绵羊是一款帮助您极速连接亚洲的视频音乐网站网络加速器。
代码和文档版权归2020年MIT许可下发布的Rails-pangu Authors 和 Ruilisi Technology所拥有。
致谢 (emoji key):
hophacker 💻 📖 🚇 |
Jiawei Li 💻 📖 |
tato 💻 📖 |
caibiwsq 💻 📖 |
Eric Guo 💻 📖 |
该项目遵循贡献者规范。欢迎任何形式的捐助!
- [1] 盘古维基