当前位置:   article > 正文

react cookie_安全React快速应用程序jsonwebtoken cookie会话auth0和通行证教程

react cookies

react cookie

I’ve been a long-time learner of express, node, and react projects. I’m always hopping from one example to another trying to figure out the best configuration for my express servers. There is one particular topic/question that has seemed to vacuum several days (if not entire weeks) out of my life during my developments.

我是Express,Node和React项目的长期学习者。 我总是从一个例子跳到另一个例子,试图找出适合我的快速服务器的最佳配置。 在开发过程中,有一个特定的主题/问题似乎已经消失了几天(如果不是整整几周)。

您将如何确保会话安全? (How will you secure your session?)

Whether you are authenticating your users via Google, Facebook, or another third party authenticator, or even if you are creating your own local authentication (mostly a bad idea unless you know what your doing), the security of your authentication process also rests on how you maintain that session with your users.

无论您是通过Google,Facebook还是其他第三方身份验证器对用户进行身份验证,或者即使您正在创建自己的本地身份验证(除非您知道自己的工作方式,否则大多数情况下都是一个坏主意),身份验证过程的安全性还取决于如何您可以与用户保持该会话。

The point of this tutorial is to create a secure express react app that we can use over and over for other cool projects. I will be going over my preferred configuration, and my understanding behind it, for those who are looking for a (sorta) simple and secure setup for their react-express projects. There are many more configurations out there with higher levels of security or ease, so if you come across any or wish to thrash my pitch, I would love to hear them.

本教程的重点是创建一个安全的Express React应用程序,我们可以一遍又一遍地将其用于其他出色的项目。 对于那些正在为他们的react-express项目寻找(某种)简单而安全的设置的人,我将介绍我偏爱的配置及其背后的理解。 那里有更多具有更高安全性或便捷性的配置,因此,如果您遇到任何问题或希望克服我的疑问,我很想听听他们的意见。

您首先需要了解的是: (What you need to know first:)

There are a few key concepts you that I will be using in this tutorial, so here is a brief overview:

我将在本教程中使用一些关键概念,因此这里是一个简要概述:

Cookies

饼干

HTTP Cookies are small pieces of data stored on the web browser, sent from the server side. There are some key properties that go into cookies. A secure cookie can only be transmitted over an encrypted connection (https). A http-only cookie cannot be access by client-side APIs, such as javascript. These cookies get sent back to the originating server, so they can hold information about the connecting user.

HTTP Cookie是存储在Web浏览器中的小块数据,是从服务器端发送的。 Cookie中包含一些关键属性。 安全cookie只能通过加密连接(https)传输。 客户端API(例如javascript)无法访问仅HTTP的Cookie。 这些cookie被发送回原始服务器,因此它们可以保存有关连接用户的信息。

JSON Web Tokens (JWT)

JSON Web令牌(JWT)

JWT tokens are cryptographically signed, base64 JSON objects. You can create these tokens and send them from your backend server to your frontend to be stored. When these tokens are sent back, you can verify the signature on the token to verify that the JWT is valid. This can be used as a very handy way to verify a session.

JWT令牌是经过密码签名的base64 JSON对象。 您可以创建这些令牌,并将其从后端服务器发送到前端进行存储。 当这些令牌发送回时,您可以验证令牌上的签名以验证JWT有效。 这可以用作验证会话的便捷方法。

Why Cookies To Hold JWTs and Not Local-Storage?

为什么Cookies保留JWT,而不保留本地存储?

Local storage on the browser is susceptible to XSS attacks, in which malicious code can scrape the users browser storage and snag valuable session data. This is easily avoided through secure/http-only cookies

浏览器上的本地存储易受XSS攻击,其中,恶意代码会抓取用户浏览器存储并捕获有价值的会话数据。 通过安全/仅HTTP cookie可以轻松避免这种情况

我第一次学习时遇到的是:(What I came across as I was first learning:)

There are two very popular packages available for maintaining sessions in express:

有两种非常流行的软件包可用于维护快速会话:

express-session and cookie-session

express-session and cookie-session

Express-session

快速会议

Express-session is a session middleware used for storing session data server-side. This means that when the user logs in, their session data is being stored in a database somewhere on the backend, and the session id is stored in a cookie, which is then passed to the frontend. When the cookie comes back on a request, this session Id is used to lookup the session in the database.

Express-session是用于在服务器端存储会话数据的会话中间件 这意味着,当用户登录时,其会话数据将存储在后端某处的数据库中,而会话ID被存储在cookie中,然后将其传递给前端。 当Cookie根据请求返回时,该会话ID用于在数据库中查找会话。

Cookie-session

Cookie会话

Cookie-session is another session middleware. This middleware creates a cookie which can be used to store information within a cookie that gets sent to the frontend. This does not store any data on the server, so there is no storage involved.

Cookie会话是另一种会话中间件。 该中间件创建一个cookie,该cookie可用于将cookie中的信息存储到发送到前端的cookie中。 这不会在服务器上存储任何数据,因此不涉及任何存储。

为什么一个接一个? (Why one over the other?)

Express-session seems to be used more often in tutorials I find online, especially with popular authentication libraries such as passport. This could certainly save some time and headache for beginners who just want to dive in via a quick tutorial.

我在网上找到的教程似乎更常使用Express-session,特别是在通行证等流行的身份验证库中。 对于只想通过快速教程学习的初学者来说,这无疑可以节省一些时间和头痛。

Unfortunately, moving past development is where things with express-session get more involved. Since the default session storage for express-session is the MemoryStore, it is purposely not designed for a production environment, and will leak memory since it does not scale past a single process. It will require a separate server-side storage system such as redis or mongodb in order to operate nicely in a production environment.

不幸的是,超越发展才是涉及快速会话的事情。 由于用于Express-Session的默认会话存储是MemoryStore,因此它不是专门为生产环境设计的,并且由于无法扩展到单个进程,因此会泄漏内存。 它将需要一个单独的服务器端存储系统,例如redis或mongodb,以便在生产环境中正常运行。

Adding a server-side session-storage comes also comes with a performance hindrance, but allows for more data to be stored regarding the session, since its an actual storage system and not a small cookie. Cookies (by the RFC625 specification) recommends that a browser should allow at least 4096 bytes per cookie. Unfortunately this means that you should not exceed 4093 bytes per domain.

添加服务器端会话存储也带来了性能上的障碍,但是由于它是实际的存储系统,而不是很小的cookie,因此允许存储有关该会话的更多数据。 Cookies(根据RFC625规范)建议浏览器每个cookie至少允许4096个字节。 不幸的是,这意味着每个域最多不能超过4093字节。

Why Cookie-session is a lovely choice:

为什么Cookie会话是一个不错的选择:

Cookie-session does not require server-side storage for production, and you have free manipulation of what goes into your cookie session. We can easily assign values to our session cookie, such as a JWT token. This frees your server for less latency and problems in load-balancing situations.

Cookie会话不需要用于生产的服务器端存储,并且您可以自由操作Cookie会话中的内容。 我们可以轻松地为会话cookie分配值,例如JWT令牌。 这样可以释放您的服务器,以减少等待时间和负载平衡情况下的问题。

And if you wanted to add a server side storage for tracking sessions, you can add this logic to a cookie-session setup, in case you had a use-case for this functionality. cookie-session middleware is flexible, and just needs a bit of a love.

而且,如果您想添加服务器端存储来跟踪会话,则可以在cookie会话设置中添加此逻辑,以防万一您有此功能的用例。 cookie会话中间件是灵活的,只需要一点爱。

看起来像什么: (What it looks like:)

We are going to use passport, jsonwebtoken, and cookie-session. I will also be using a ReactJS frontend.

我们将使用护照,jsonwebtoken和cookie会话。 我还将使用ReactJS前端。

Create a new project directory and initialize a new node project.

创建一个新的项目目录并初始化一个新的节点项目。

$ mkdir secure-starter$ cd secure-starter$ npm init

Nothing fancy, I just like to set my “main” as server.js. And now we have a package.json file.

没什么,我只想将我的“ main”设置为server.js。 现在我们有了一个package.json文件。

Let’s download the starting dependencies

让我们下载开始的依赖

$ npm install --save express cookie-session

And now we create server.js at the root directory for a simple express app:

现在,我们在根目录下创建一个简单的快速应用程序server.js:

// server.jsconst express = require('express');const session = require('cookie-session');/* Create Express App */const app = express();app.listen(8080, () => {  console.log("I'm listening!");});module.exports = app;

In this example above, we are doing the following

在上面的示例中,我们正在执行以下操作

  1. Importing our dependencies

    导入我们的依赖
  2. Creating an express app with express()

    使用express()创建一个Express应用

  3. Our app is now listening on port 8080

    我们的应用程序现在正在监听8080端口

If we run our server, we can see that its alive:

如果运行服务器,我们可以看到它仍然有效:

$ npm start> node server.jsI'm listening!

Let’s add the cookie stuff:

让我们添加cookie的东西:

/* Create Express App */const app = express();.../* Set Cookie Settings */app.use(  session({    name: 'session',    secret: 'secretKeyWooo',    expires: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours  }));...app.listen(8080, () => {    console.log("I'm listening!");});

Ok so what does this mean

好的,那是什么意思

  1. We tell our app to use cookie-session middleware

    我们告诉我们的应用使用cookie会话中间件
  2. Name: is the name that the cookie is set to

    名称:将cookie设置为的名称
  3. secret: A string which is used to sign & verify cookie values

    秘密:用于签署和验证Cookie值的字符串
  4. expires: when our cookie will expire.

    过期:我们的Cookie过期的时间。

It’s important to note that there are other options we can pass into our cookie settings, and a reason why I didn’t include them:

请务必注意,我们还可以将其他选项传递到Cookie设置中,以及我之所以不包含这些选项的原因:

  • secure: a boolean indicating whether the cookie is to be sent only over https. This is false by default for http and true by default for https. So we can leave this blank for now, but for production, this should be true. Please always host your site through https for this reason.

    安全:一个布尔值,指示是否仅通过https发送cookie。 对于http默认为false,对于https默认为true。 因此,我们暂时可以保留此空白,但是对于生产而言,这应该是正确的。 因此,请始终通过https托管您的网站

  • httpOnly: a boolean indicating whether the cookie is only to be sent over http(s). This is true by default, which is good, as we don’t want this to be available by client Javascript.

    httpOnly:一个布尔值,指示cookie是否仅通过http发送。 默认情况下是这样,这很好,因为我们不希望客户端Javascript可以使用它。
  • signed: a boolean indicating whether the cookie is to be signed. This is true by default.

    签名:一个布尔值,指示是否要对cookie进行签名。 默认情况下是这样。

There are a few other settings which we don’t need, but could be useful for your application, so take a look on the documentation for details.

我们不需要其他一些设置,但对您的应用程序可能有用,因此请查看文档以了解详细信息。

添加一些其他安全中间件 (Adding some additional security middlewares)

Here are some additional security middlewares and their explanations:

以下是一些其他安全中间件及其说明:

$ npm install --save helmet hpp csurf express-rate-limit

helmet — This sets various HTTP headers that can help defend against common web app security vulnerabilities, such as xss attacks.

安全帽-设置各种HTTP标头,可以帮助防御常见的Web应用程序安全漏洞,例如xss攻击。

hpp — This protects against HTTP Parameter Pollution attacks

hpp —防止受到HTTP参数污染攻击

csurf — This protects against Cross-site request forgery. This needs to be used after our cookie-session connect.

csurf —这可以防止跨站点请求伪造。 我们的cookie会话连接后需要使用它。

Our server.js looks like this after the implementations:

实现后,我们的server.js如下所示:

// server.jsconst express = require('express');const session = require('cookie-session');const helmet = require('helmet');const hpp = require('hpp');const csurf = require('csurf');/* Create Express App */const app = express();/* Set Security Configs */app.use(helmet());app.use(hpp());/* Set Cookie Settings */app.use(    session({        name: 'session',        secret: 'secretKeyWooo',        expires: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours    }));app.use(csurf());app.use(limiter);app.listen(8080, () => {    console.log("I'm listening!");});module.exports = app;

创建React前端: (Creating the react frontend:)

$ npx create-react-app client

This will create a boilerplate react app in a new directory called client.

这将在名为client的新目录中创建样板react应用程序。

After the install is completed we can run our react frontend like this:

安装完成后,我们可以像这样运行我们的react前端:

$ cd client$ yarn start

This should spin up our development server and open up our template on localhost:3000

这应该启动我们的开发服务器并在localhost:3000上打开我们的模板

Image for post

Very nice.

非常好。

Time to gut some of the App.js and create some new pages. And also set up our proxy:

现在该抽空一些App.js并创建一些新页面。 并设置我们的代理:

$ cd client$ yarn add http-proxy-middleware

setupProxy.js: this is in client/src

setupProxy.js:这在client / src中

const proxy = require('http-proxy-middleware').createProxyMiddleware;module.exports = function (app) {    app.use(proxy(`/auth/**`, { target: 'http://localhost:8080' }));};

The reason why we do this is to be able to pass requests that go to /auth/* to our backend, instead of being caught by localhost:3000, this proxy setting should not affect anything in production.

我们这样做的原因是能够将转到/auth/*请求传递到我们的后端,而不是被localhost:3000捕获,此代理设置不应影响生产中的任何内容。

App.js

App.js

import React, {useState} from 'react';import './App.css';import Home from "./Home";import Profile from "./Profile";import Loading from "./Loading";function App() {  const [auth, setAuth] = useState(null); // IF WE CHANGE THIS INITIAL VALUE WE GET DIFFERENT PAGES  if (auth === null) {    return <Loading/>  }  if (auth) {    return <Profile/>  }  return <Home/>}export default App;

Explanation: Our App.js file now is going to “route” our user depending on his authentication state. When the page first loads, we don’t know whether or not our user is authenticated, so we create an auth value using useState and set its initial value to be null;

说明:现在,我们的App.js文件将根据其身份验证状态“路由”用户。 首次加载页面时,我们不知道我们的用户是否已通过身份验证,因此我们使用useState创建一个auth值,并将其初始值设置为null;

While the auth value is null, we return our loading screen. If our auth is true then we return our profile screen, and otherwise we will show the home screen (or login screen). We will implement logic later on that actually changes the auth value based on if our user is signed in.

当auth值为null时,我们返回加载屏幕。 如果我们的身份验证为true,则返回个人资料屏幕,否则将显示主屏幕(或登录屏幕)。 我们稍后将实现逻辑,该逻辑实际上将根据用户是否登录来更改auth值。

Home.js: in client/src

Home.js:在client / src中

import React from 'react';import './App.css';function Home() {    return (        <div className="App">            <header className="App-header">                <p>                    You are not logged in                </p>                <a                    className="App-link"                    href={"/auth/login"}                >                    Login Here                </a>            </header>        </div>    );}export default Home;

Explanation: This is a simple login screen, and our link to login points to /auth/login, which should route to our backend.

说明:这是一个简单的登录屏幕,登录的链接指向/ auth / login,该路由应该路由到我们的后端。

Loading.js

Loading.js

import React from 'react';import './App.css';function Loading() {    return (        <div className="App">            <header className="App-header">                <p>                    Loading...                </p>            </header>        </div>    );}export default Loading;

Profile.js

Profile.js

import React from 'react';import './App.css';function Profile() {    return (        <div className="App">            <header className="App-header">                <p>                    You are logged in :)                </p>                <a                    className="App-link"                    href={"/auth/logout"}                >                    Logout                </a>            </header>        </div>    );}export default Profile;

So our screen looks like this if auth is null:

因此,如果auth为null,则我们的屏幕如下所示:

Image for post

And this if our auth is false:

如果我们的auth为假,则为:

Image for post

As of right now, these links and buttons will not do anything, but they will mean something after we configure our auth0 and backend server auth setup.

截至目前,这些链接和按钮将无法执行任何操作,但是在我们配置auth0和后端服务器auth设置后,它们将具有某些意义。

创建一个auth0帐户并设置回调/重定向URL (Create an auth0 account and set up callback/redirect urls)

Auth0 is a great third-party authentication party that allows for multifaceted integrations for all types of applications. You don’t need to use Auth0, and there are many options for passport based authentication strategies, but Auth0 is one that I haven’t worked with yet, and they allow for their own login screen, and logging for all auth attempts.

Auth0是出色的第三方身份验证方,它允许对所有类型的应用程序进行多方面的集成。 您无需使用Auth0,基于护照的身份验证策略有很多选项,但是Auth0是我尚未使用的一种,它们允许使用自己的登录屏幕,并记录所有身份验证尝试。

  1. Go to https://manage.auth0.com/welcome/#

    转到https://manage.auth0.com/welcome/#

  2. Create an account by signing up

    通过注册创建帐户
  3. Choose a tenant domain associated with yourself or your app

    选择与您自己或您的应用相关联的租户域
  4. Choose personal and go to the next steps

    选择个人,然后继续下一步
  5. Create Application and select regular web applications

    创建应用程序并选择常规Web应用程序
Image for post

Select node.js as the tech behind your project.

选择node.js作为项目背后的技术。

Once you create a new application, you’ll be able to view the basic information and properties in your dashboard. You will also have access to a quick-start tutorial for Auth0, which is very handy, but only useful fully if you have decided to use express-session. Finding a tutorial for Node.js, auth0, and cookie-session is actually more difficult than I thought it would be.

创建新应用程序后,您将能够在仪表板中查看基本信息和属性。 您还将可以访问Auth0的快速入门教程,该教程非常方便,但是只有在您决定使用Express-Session时才完全有用。 查找Node.js,auth0和cookie会话的教程实际上比我想象的要难。

Image for post

An important thing you will need: In your allowed callback URLs section, you should put http://localhost:3000/auth/callback. This is the url that Auth0 will redirect to after it finishes authenticating your user.

您将需要做的一件重要事情:在允许的回调URL部分中,您应该输入http://localhost:3000/auth/callback. 这是Auth0完成用户身份验证后将重定向到的URL。

You’re going to want to copy your Auth0 application domain, clientID, and client secret into your application. The best practice is to store this in an .env file and use thedotenv package to apply these configs to your node process, Don’t forget to exclude them from your git commits, so you’re not publicly displaying your client secret. You can also use this .env file to hold your cookie secret.

您将想要将Auth0应用程序域,clientID和客户端密钥复制到您的应用程序中。 最佳实践是将其存储在.env文件中,并使用dotenv包将这些配置应用于您的节点进程,不要忘记将它们从git commit中排除,因此您不会公开显示您的客户端机密。 您也可以使用此.env文件来保存您的cookie秘密。

Even while making this tutorial, I accidentally committed my auth0 secret to github. Luckily, Auth0 lets you rotate your secret key. Whoops :)

即使在编写本教程时,我也无意中将auth0秘密提交给了github。 幸运的是,Auth0使您可以旋转密钥。 哎呀:)

$ npm install --save dotenv

And then we add our dotenv config setup

然后我们添加我们的dotenv配置设置

const dotenv = require('dotenv');const path = require('path');/* Import config */ // THIS IS NEW :)dotenv.config({path: path.resolve(__dirname, '.env')});/* Create Express App */const app = express();/* Set Security Configs */app.use(helmet());app.use(hpp());/* Set Cookie Settings */app.use(    session({        name: 'session',        secret: process.env.COOKIE_SECRET, // WE USE THIS NOW :)        expires: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours    }));app.use(csurf());

And now our .env file, which should be at the root directory:

现在,我们的.env文件应该位于根目录中:

AUTH0_SECRET=<your secret>AUTH0_CLIENT_ID=<your client id>AUTH0_DOMAIN=<your domain>AUTH0_CALLBACK_URL=http://localhost:3000/auth/callbackCOOKIE_SECRET=<move your cookie secret here>JWT_SECRET_KEY=secretttttttt // THIS IS ALSO NEW

实施护照: (Implementing passport:)

Passport is an authentication middleware for Node.js, and it’s super nice. It has many strategies for authentication, such as through google, facebook, or even local authentication strategies. We are going to implement two strategies: auth0 and jwt.

Passport是Node.js的身份验证中间件,非常好。 它具有许多身份验证策略,例如通过google,facebook甚至本地身份验证策略。 我们将实现两种策略:auth0和jwt。

$ npm install --save passport passport-auth0 passport-jwt jsonwebtoken

And now we create our passport.js file:

现在我们创建我们的passport.js文件:

// middlewares/passport.jsconst passport = require('passport');const Auth0Strategy = require('passport-auth0');const JwtStrategy = require('passport-jwt').Strategy;const auth0Strategy = new Auth0Strategy(    {        domain: process.env.AUTH0_DOMAIN,        clientID: process.env.AUTH0_CLIENT_ID,        clientSecret: process.env.AUTH0_SECRET,        callbackURL: process.env.AUTH0_CALLBACK_URL,    },    (accessToken, refreshToken, extraParams, profile, done) => {        return done(null, profile);    });const jwtStrategy = new JwtStrategy(    {        jwtFromRequest: (req) => req.session.jwt,        secretOrKey: process.env.JWT_SECRET_KEY,    },    (payload, done) => {        // TODO: add additional jwt token verificationreturn done(null, payload);    });passport.use(auth0Strategy);passport.use(jwtStrategy);module.exports = passport;

Let’s talk about these strategies:

让我们谈谈这些策略:

  1. Auth0Strategy: This takes in your domain, clientId, clientSecret, and callbackURL from your .env file, and uses it to redirect the user to the Auth0 sign in page. Once the user is done with signing in, Auth0 redirects them back to a callback url, in which we will now have the profile for this user.

    Auth0Strategy :这将从.env文件中获取您的域,clientId,clientSecret和callbackURL,并使用它将用户重定向到Auth0登录页面。 用户完成登录后,Auth0会将其重定向回回调URL,现在我们将在其中获得该用户的配置文件。

  2. The JwtStrategy is a simple way for passport to verify a jwt token using a given secret to decode the the jwt’s signature and return the payload that was encoded into the token.

    JwtStrategy是护照使用给定秘密验证jwt令牌以解码jwt的签名并返回编码到令牌中的有效载荷的简单方法。

Import your passport file into your app.js file, make sure this is after you import your .env file, because passport.js uses process.env configs.

将您的通行证文件导入到您的app.js文件中,请确保这是在您导入.env文件之后,因为passport.js使用process.env配置。

const passport = require('./middlewares/passport');

And after you initialize your app, you should tell it to use your passport middleware:

在初始化您的应用后,您应该告诉它使用护照中间件:

app.use(passport.initialize());

Now our app is using passport and our configured strategies are available.

现在我们的应用程序正在使用通行证,并且可以使用我们配置的策略。

实施我们的身份验证路由 (Implementing our authentication routes)

We should create our auth.js routes. These will be the open endpoints exposed in for our frontend to redirect against for login/logout procedures.

我们应该创建我们的auth.js路由。 这些将是开放的端点,供我们的前端重定向以用于登录/注销过程。

const express = require('express');const passport = require('passport');const jwt = require('jsonwebtoken');const jwtRequired = passport.authenticate('jwt', { session: false });const router = express.Router();router.get(    '/login',    passport.authenticate('auth0', {        scope: 'openid email profile',    }),    (req, res) => {        res.redirect('/');    });router.get('/callback', (req, res, next) => {    passport.authenticate('auth0', (err, user) => {        if (err) {            return next(err);        }        if (!user) {            return res.redirect('/login');        }        console.log("CALLBACK SUCCESSFUL!")        const userReturnObject = {            nickname: user.nickname,        };        req.session.jwt = jwt.sign(userReturnObject, process.env.JWT_SECRET_KEY);        return res.redirect('/');    })(req, res, next);});module.exports = router;

Explanation:

说明:

  1. Our /login route utilizes our new auth0 passport authentication, requesting a few user scopes for the profile. When this endpoint is hit, it will actually redirect the user to the Auth0 login screen.

    我们的/login路由利用了我们新的auth0通行证身份验证,为配置文件请求了一些用户范围。 当点击此端点时,它将实际上将用户重定向到Auth0登录屏幕。

  2. /callback is required to receive the user data back from the Auth0 login screen. This will pass our credentials back into the passport authenticate procedure, which will verify that the info that came back from Auth0 is valid. If there was an err, or a null user, we redirect the user back to the /login path. Otherwise, we now have the creds of the user

    需要/callback才能从Auth0登录屏幕接收回用户数据。 这会将我们的凭据传递回护照认证过程,该过程将验证从Auth0返回的信息是否有效。 如果存在错误或用户为空,我们会将用户重定向回/ login路径。 否则,我们现在拥有用户的信誉

req.session.jwt = jwt.sign(userReturnObject, process.env.JWT_SECRET_KEY)

3. The snippet above is the bread and butter of creating a stateless session for our user. We take some information that came from the user object, pack it in a JWT token, and sign it with our secret key. We then assign that JWT token to our req.session, which is our cookie created with the cookie-session library.

3.上面的代码片段是为我们的用户创建无状态会话的基础。 我们获取来自用户对象的一些信息,将其包装在JWT令牌中,并使用我们的密钥对其进行签名。 然后,我们将该JWT令牌分配给我们的req.session,这是使用cookie-session库创建的cookie-session

This cookie will be sent back on every request, and should contain our users jwt token with it.

该Cookie将在每次请求时发送回,并应包含我们的用户jwt令牌。

And in our server.js file we need to include the new auth routes:

在我们的server.js文件中,我们需要包括新的身份验证路由:

app.use(passport.initialize()); //... this is new stuffconst authRoutes = require('./routes/auth');app.use('/auth', authRoutes);//...app.listen(8080, () => {    console.log("I'm listening!");});

Now when we click on the login button we should properly redirect to our auth0 login screen:

现在,当我们单击登录按钮时,我们应该正确地重定向到我们的auth0登录屏幕:

Image for post

When we login, we should be successfully redirected back to our app. Unfortunately, we haven’t fully finished our frontend, so it may be hard to determine if we are actually signed in. But you can place some logging in the callback function to print some success statements! The one i added should show:

登录时,应成功将其重定向回我们的应用程序。 不幸的是,我们还没有完全完成我们的前端,因此可能很难确定我们是否真正登录。但是您可以在回调函数中放置一些日志以打印一些成功语句! 我添加的那个应该显示:

CALLBACK SUCCESSFUL!

CALLBACK SUCCESSFUL!

设置私人路线 (Setting up private routes)

We have officially given our new user a jwt token. But we want to be able to use that token to verify that his session is valid.

我们已正式为新用户提供了jwt令牌。 但是我们希望能够使用该令牌来验证他的会话是否有效。

In our auth route file, we can add our passport-jwt strategy as a middleware to protect routes:

在我们的身份验证路由文件中,我们可以添加我们的passport-jwt策略作为中间件来保护路由:

const jwtRequired = passport.authenticate('jwt', { session: false });router.get('/private-route', jwtRequired, (req, res) => {    return res.send('This is a private route');});

Now, when we receive a request at the /private-route endpoint, our passport-jwt strategy attempts to decode the token that is on the cookie-session attached to the request. If there’s an issue, or no token, a 401 will be sent. If there’s no issue, then the route is accessible!

现在,当我们在/private-route端点接收到请求时,我们的password-jwt策略将尝试解码附加到该请求的cookie会话上的令牌。 如果有问题或没有令牌,将发送401。 如果没有问题,则可以访问该路线!

如果我们要保护前端路由怎么办? (What if we want to protect frontend routes?)

Our browser has the cookie, but our actual client-side react frontend is not aware of authentication state. When we load the page in our browser, we want our app to be able to check with the backend to get the current user session. We can add another route to our auth routes to allow this:

我们的浏览器具有cookie,但是我们实际的客户端React前端不知道身份验证状态。 当我们在浏览器中加载页面时,我们希望我们的应用程序能够与后端进行检查以获取当前的用户会话。 我们可以在身份验证路由中添加其他路由,以实现以下目的:

router.get('/current-session', (req, res) => {    passport.authenticate('jwt', { session: false }, (err, user) => {        if (err || !user) {            res.send(false);        } else {            res.send(user);        }    })(req, res);});

In this endpoint, we are verifying that a user’s cookie-session contains a valid jwt token, but once the token is decoded, we take the information that we stored in that token and we send it to the frontend.

在此端点中,我们正在验证用户的cookie会话是否包含有效的jwt令牌,但是一旦令牌被解码,我们就会获取存储在该令牌中的信息,然后将其发送到前端。

前端工作 (The frontend work)

We need to set up our frontend so that when our website loads, we send a request to the backend to get our session, and if our session exists then we can route our user properly. We will install axios as a way to easily send requests:

我们需要设置前端,以便在我们的网站加载时,我们向后端发送请求以获取会话,如果会话存在,则可以正确地路由用户。 我们将安装axios,以轻松发送请求:

$ cd client $ yarn add axios

And in our App.js we add the logic to send the request to our new current-user endpoint:

在我们的App.js中,我们添加了将请求发送到新的current-user端点的逻辑:

import React, {useState, useEffect} from 'react';import axios from 'axios';import './App.css';import Home from "./Home";import Profile from "./Profile";import Loading from "./Loading";function App() {  const [auth, setAuth] = useState(null);  useEffect(() => {    axios.get('/auth/current-session').then(({data}) => {      setAuth(data);    })  }, [])  if (auth === null) {    return <Loading/>  }  if (auth) {    return <Profile auth={auth}/>  }  return <Home/>}export default App;

You can see that whatever data we get back from that request, we set our frontend auth state. If you look at the endpoint logic, if our session does not exist (our user isn’t signed in) then a false value is sent back, which would tell our frontend to show the Home screen. If our session exists, our auth state is our user object. We pass that auth state object to our profile.

您可以看到,无论从该请求中获取的任何数据,我们都设置了前端身份验证状态。 如果您查看端点逻辑,则如果我们的会话不存在(我们的用户未登录),则将返回一个假值,这将告诉我们的前端显示主屏幕。 如果我们的会话存在,则我们的身份验证状态是我们的用户对象。 我们将该身份验证状态对象传递给我们的个人资料。

And now we can use our auth object in the Profile.js Screen:

现在我们可以在Profile.js屏幕中使用auth对象:

import React from 'react';import './App.css';function Profile({auth}) {    return (        <div className="App">            <header className="App-header">                <p>                    You are logged in as {auth && auth.nickname ? auth.nickname : null}                </p>                <a                    className="App-link"                    href={"/auth/logout"}                >                    Logout                </a>            </header>        </div>    );}export default Profile;

Which should look a little like this when logged in:

登录后应该看起来像这样:

Image for post

在注销时破坏会话 (Destroy your session on logout)

You may notice that on our Profile.js screen, we have a button that redirects to /auth/logout. In order for this to work, we need to add this endpoint in our backend auth routes:

您可能会注意到,在Profile.js屏幕上,我们有一个重定向到/auth/logout的按钮。 为了使其正常工作,我们需要在后端身份验证路由中添加此端点:

router.get('/logout', (req, res) => {    req.session = null;    const homeURL = encodeURIComponent('http://localhost:3000/');    res.redirect(        `https://${process.env.AUTH0_DOMAIN}/v2/logout?returnTo=${homeURL}&client_id=${process.env.AUTH0_CLIENT_ID}`    );});

In this we are simply getting rid of our jwt token that we assigned to req.session, and letting Auth0 know that our user has requested a logout. This should then redirect to our home page! And we are officially done!

在这种情况下,我们只是摆脱了分配给req.session的jwt令牌,并让Auth0知道我们的用户已请求注销。 然后应将其重定向到我们的主页! 至此我们正式完成!

结论 (Conclusion)

There are oh so many ways to implement solid authentication layers, and once you get used to how each individual piece works, you can put them together to make a process that fits the functionality of your app.

有很多方法可以实现可靠的身份验证层,一旦您习惯了各个部分的工作原理,就可以将它们组合在一起以形成适合您应用程序功能的流程。

Thank you very much for reading, and please send me any questions if anything was not clear!

非常感谢您阅读,如果有任何不清楚的地方,请发给我任何问题!

Here is the source code: https://github.com/jdstregz/secure-starter

这是源代码: https : //github.com/jdstregz/secure-starter

Happy hacking!

骇客入侵!

普通英语JavaScript (JavaScript In Plain English)

Enjoyed this article? If so, get more similar content by subscribing to Decoded, our YouTube channel!

喜欢这篇文章吗? 如果是这样,请订阅我们的YouTube频道解码,以获得更多类似的内容

翻译自: https://medium.com/javascript-in-plain-english/secure-react-express-apps-jsonwebtoken-cookie-session-auth0-and-passport-tutorial-e58d6dce6c91

react cookie

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/513640
推荐阅读
相关标签
  

闽ICP备14008679号