k-rails_使用Rails 6和React使用JWT-in-a-cookie保护API的安全

k-rails

In this article I will walk you through setting up a cookie based authentication system with subdomains so your app will be compliant with web standards and its cookies will be saved on iOS and Safari.

在本文中,我将引导您建立带有子域的基于cookie的身份验证系统,以便您的应用符合网络标准,并且其cookie将保存在iOS和Safari上。

为什么要将JWT放在cookie中? (Why put a JWT in a cookie?)

A simple JWT stored in local storage will suffice for securing a small application. But it poses a security risk in a large production-grade application. We mitigate many security issues by encoding the JWT to a HTTP only cookie and passing it to the user’s browser. This way, malicious Javascript has no access to our JWT because the cookie is included automatically for every subsequent request by the browser itself. This greatly reduces the amount of authentication code/logic on the front-end since we don’t need to use Javascript to manage and store the token.

存储在本地存储中的简单JWT就足以保护小型应用程序。 但是,这在大型生产级应用程序中会带来安全风险。 通过将JWT编码为仅HTTP cookie并将其传递给用户的浏览器,我们可以缓解许多安全问题。 这样,恶意Javascript就无法访问我们的JWT,因为浏览器本身的每个后续请求都会自动包含cookie。 因为我们不需要使用Javascript来管理和存储令牌,所以这大大减少了前端的身份验证代码/逻辑量。

苹果问题… (The Apple problem…)

A recent change to Apple Safari both on MacOS and iOS has changed how cookies are saved. If your servers are set up in such a way that your cookies are served across origins, Safari will not save the cookie that is passed by the server. This is because the origin of the cookie and the request origin are different.

MacOS和iOS上对Apple Safari的最近更改都改变了cookie的保存方式。 如果您的服务器设置为可以跨来源发送Cookie,则Safari不会保存服务器传递的Cookie。 这是因为Cookie的来源与请求的来源不同。

This article covers how to implement an authentication system in a way that complies with these rules.

本文介绍如何以符合这些规则的方式来实现身份验证系统。

In a nutshell, the authentication request origin (React) and the response origin (Rails 6) must be on the same domain.

简而言之,身份验证请求来源(React)和响应来源(Rails 6)必须位于同一域上。

This poses a problem during development if our backend and frontend are hosted separately (this is very common if you use something like create-react-app and Rails in API mode). To get around this issue, we will use ngrok.

如果后端和前端分别托管,则会在开发过程中造成问题(如果在API模式下使用诸如create-react-app和Rails之类的东西,这是很常见的)。 要解决此问题,我们将使用ngrok。

设置Ngrok (Setting up Ngrok)

For the uninitiated, Ngrok is a wonderful piece of software that allows you to tunnel traffic from the internet to your local server. Its super useful if you want to serve your development server running on localhost to the rest of the internet.

对于刚起步的人来说,Ngrok是一款很棒的软件,它使您可以将Internet上的流量隧道传输到本地服务器。 如果您想将运行在localhost上的开发服务器服务到Internet的其余部分,则它非常有用。

First head over to https://ngrok.com and sign up for a pro account, this will allow us to reserve a domain that can be used for our frontend and backend. We will use client.your-domain-here.ngrok.io for serving the React application and api.your-domain-here.ngrok.io for the Rails server. Please refer to this excellent guide by Twilio https://www.twilio.com/blog/2016/12/localhost-tunneling-ngrok-mac-os-x.html for a basic intro to setting up ngrok. Once setup, connect it to your paid account (instructions are at https://ngrok.com) and reserve your domain in the ngrok web dashboard.

首先前往https://ngrok.com并注册一个专业帐户,这将使我们能够保留可用于前端和后端的域。 我们将使用client.your-domain-here.ngrok.io服务React应用程序,并使用api.your-domain-here.ngrok.io服务Rails服务器。 请参阅Twilio的这份出色指南https://www.twilio.com/blog/2016/12/localhost-tunneling-ngrok-mac-os-x.html ,以获取设置ngrok的基本介绍。 设置完成后,将其连接到您的付费帐户(说明位于https://ngrok.com ),并在ngrok Web仪表板中保留您的域。

激发ngrok (Firing up ngrok)

Image for post
I like using Tmux for this sort of thing, you can see the 2 ngrok tunnels routing traffic to my local servers
我喜欢将Tmux用于这种情况,您可以看到2条ngrok隧道将流量路由到我的本地服务器

Assuming your rails server is running on port 3000, navigate to where you have ngrok downloaded and punch in

假设您的Rails服务器在端口3000上运行,请导航至已下载ngrok的位置并进行插入

./ngrok http 3000 -subdomain api.your-subdomain-here

Do the same for the frontend, we are going to assume your React server is running on port 5500

对前端执行相同的操作,我们将假设您的React服务器在端口5500上运行

./ngrok http 5500 -subdomain client.your-subdomain-here

These commands essentially tell ngrok to create a subdomain under your reserved domain and route all traffic going to those URL’s to port 5500 and 3000.

这些命令实际上告诉ngrok在保留域下创建一个子域,并将所有流向这些URL的流量路由到端口5500和3000。

With this setup, we fulfill the requirements for Apple/Safari to happily accept and save your server’s cookies in the user’s browser.

通过此设置,我们满足了Apple / Safari能够愉快地接受并在用户浏览器中保存服务器cookie的要求。

让我们从后端开始 (Lets get started on the backend)

We are going to use Rails 6 for this, you can find the complete code on my github repo here: https://github.com/donrestarone/cookie-authentication-backend. I created branches for each of the steps so it will be easier to follow along.

我们将为此使用Rails 6,您可以在我的github存储库上找到完整的代码: https : //github.com/donrestarone/cookie-authentication-backend 。 我为每个步骤创建了分支,因此将更容易进行。

对于仅Rails API的应用程序 (For Rails API only applications)

Image for post

If you are using Rails 6 in API mode like I am, you will need to add an additional middleware to your config/application.rb first. If you are using a traditional Rails stack, you can skip this step.

如果像我一样在API模式下使用Rails 6,则需要先向config / application.rb中添加其他中间件。 如果使用传统的Rails堆栈,则可以跳过此步骤。

On line 36, we added ActionDispatch::Cookies
在第36行,我们添加了ActionDispatch :: Cookies

Next, we need to make sure we have access to the cookies module in our controller. To do that we add this line to the ApplicationController.

接下来,我们需要确保可以访问控制器中的cookie模块。 为此,我们将此行添加到ApplicationController中。

By including it here, all our controllers can handle cookies
通过将其包含在此处,我们所有的控制器都可以处理Cookie

设置CORS(跨源资源共享) (Setup CORS (cross origin resource sharing))

Usually for API’s we will allow wildcard domains (that means any domain can make requests to our server). This approach will not work for http-only cookies because they are tied to a specific domain on the client-side. To get around this, we will whitelist the domain that is serving our React application.

通常对于API,我们会允许使用通配符域(这意味着任何域都可以向我们的服务器发出请求)。 这种方法不适用于仅HTTP的Cookie,因为它们已绑定到客户端上的特定域。 为了解决这个问题,我们将白名单服务于我们的React应用程序的域。

If you are using Rails 6+, there is currently a bug in the latest version of the Rack-cors gem which causes it to reject whitelabeled domains. Make sure you use ‘rack-cors’, “~> 0.4.1” to avoid this issue.

如果您使用的是Rails 6+,则最新版本的Rack-cors gem目前存在一个错误,该错误导致其拒绝带有白标签的域。 确保使用“ rack-cors”,“〜> 0.4.1”来避免此问题。

Debug true will show a log of all requests coming into the API. Without this rejected requests will not be shown. This way you can debug if your front end can’t talk to the backend (check for a CORS error in chrome dev tools)
Debug true将显示所有进入API的请求的日志。 否则,将不会显示被拒绝的请求。 这样,您可以调试前端是否无法与后端对话(检查chrome dev工具中的CORS错误)

创建用于编码和解码JWT的模块 (Create a module for encoding and decoding JWT’s)

This module will accept a payload passed as a hash and an expiry time. If the token is expired, invalid or not signed by our Rails secret this function will return false. Otherwise it will return the decoded token

该模块将接受作为散列和有效时间传递的有效负载。 如果令牌已过期,无效或未被我们的Rails机密签名,则此函数将返回false。 否则它将返回解码的令牌

we will save this file under lib/core_modules/json_web_token
我们将此文件保存在lib / core_modules / json_web_token下

For good measure, we will explicitly tell Rails to autoload the /lib folder.

为了达到良好的效果,我们将明确告诉Rails自动加载/ lib文件夹。

We tell Rails to autoload all files under /lib with the code on line 24
我们告诉Rails使用第24行的代码自动加载/ lib下的所有文件

I’m going to skip past the model and sign up setup for brevity because you can look it up on my Github repo.

为了简化起见,我将跳过模型并注册设置,因为您可以在我的Github存储库中查找它。

在用户登录时提供Cookie,并在用户退出时销毁它 (Serving the cookie when the user signs in and destroying it when they sign out)

Cookie authentication is almost identical to JWT authentication, the user will provide their email and password and if those are valid we will sign a cookie and pass it to their browser.

Cookie身份验证几乎与JWT身份验证相同,用户将提供其电子邮件和密码,如果有效,我们将对Cookie进行签名并将其传递给他们的浏览器。

The magic happens on line 22 where we sign the cookie. The value is set to the JWT (which contains some user information like their ID) and the httponly option ensures that it will only be passed to the browser and not client-side Javascript.

魔术发生在第22行,我们在其中签署了cookie。 该值设置为JWT(其中包含一些用户信息,例如其ID),并且httponly选项可确保仅将其传递给浏览器,而不传递给客户端Javascript。

For signing out, before we run the destroy method we ensure that the cookie is valid (that’s what the before_action block does). And if it is, we simply delete the cookie which removes it from the user’s browser.

为了注销,在运行destroy方法之前,请确保该cookie有效(这是before_action块的作用)。 如果是的话,我们只需删除cookie,即可将其从用户浏览器中删除。

检查用户是否具有有效的cookie并从中提取用户信息 (Checking if the user has a valid cookie and extracting user information from it)

First we will create some helper functions in our application controller. These 2 methods will be used over and over to check if the user has a valid cookie and to get the current user information from that cookie.

首先,我们将在应用程序控制器中创建一些辅助函数。 将反复使用这两种方法来检查用户是否具有有效的cookie并从该cookie获取当前用户信息。

authenticate_cookie will check for a passed cookie and attempt to decode it. If it is valid it will return true thereby continuing the filter chain. Otherwise it will render an error which can be used by the React application to show a useful message to the user (maybe telling them that their session has expired and they need to log back in).

authenticate_cookie将检查传递的cookie并尝试对其进行解码。 如果有效,它将返回true,从而继续进行过滤器链。 否则它将产生一个错误,React应用程序可以使用该错误向用户显示一条有用的消息(也许告诉他们他们的会话已经过期并且他们需要重新登录)。

current_user will help us find the user that is making the request at any given moment.

current_user将帮助我们找到在任何给定时刻发出请求的用户。

Most of the heavy lifting is now done. In case you hung around this long, off to React land we go!

现在大部分繁重的工作已经完成。 如果您徘徊了这么久,去了React Land,我们就去吧!

设置React (Setting up React)

This part is really easy, all you need to do is to add credentials: “include” to each of your fetch calls and the browser will automatically include the cookie for the request. Heres an example of logging in and out

这部分非常容易,您需要做的就是添加凭据:在每个提取调用中添加“ include”,浏览器将自动包括该请求的cookie。 以下是登录和注销的示例

If you take a peek at chrome dev tools when you are logging in, you will see an http-only cookie being dropped into your browser by the Rails server. This cookie disappears when you log out.

如果您在登录时浏览chrome开发工具,则会发现Rails服务器将仅HTTP的Cookie放到浏览器中。 当您注销时,此cookie消失。

Image for post

That’s all there is to it!

这里的所有都是它的!

翻译自: https://levelup.gitconnected.com/api-authentication-with-jwt-and-cookies-featuring-rails-6-and-react-bd33a477c559

k-rails

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值