jenkins 构建api_构建SailsJS应用:从API到身份验证

jenkins 构建api

TL;DR There are lots of JavaScript frameworks out there and it can be overwhelming to keep up with all of them. SailsJs is a Node.js MVC framework. Currently, SailsJs has over 15,000 stars on GitHub. In this tutorial, I'll show you how easy it is to build a web application with SailsJs and add authentication to it. Check out the repo to get the code.

TL; DR那里有很多JavaScript框架,要跟上所有框架的工作可能会让人不知所措。 SailsJs是一个Node.js MVC框架。 目前,SailsJs在GitHub上拥有超过15,000个星星。 在本教程中,我将向您展示使用SailsJs构建Web应用程序并向其添加身份验证是多么容易。 查看仓库以获取代码。

SailsJs is a Node.js framework that is designed to emulate the familiar MVC pattern of frameworks like Ruby on Rails and Laravel, but with support for modern apps architecture such as building data-driven APIs and realtime apps. SailsJs saves you time and effort because it is designed for building practical, production-ready Node.js apps in a matter of weeks and ships with a lot of features out of the box. These features include the following:

SailsJs是一个Node.js框架,旨在模仿Ruby on Rails和Laravel等框架的熟悉的MVC模式,但支持现代应用程序体系结构,例如构建数据驱动的API和实时应用程序。 SailsJs节省了您的时间和精力,因为它旨在在数周内构建实用的,可投入生产的Node.js应用程序,并且具有许多现成的功能。 这些功能包括:

  • Waterline ORM

    水线ORM
  • Rest APIs Generator

    REST API生成器
  • WebSocket Support

    WebSocket支持
  • Policies ( Effective role-based access control)

    策略(基于角色的有效访问控制)
  • Flexible Asset Pipeline

    灵活资产管道

SailsJs makes good use of already written and well-tested modules from the JavaScript community. The documentation is very detailed, and there is a vibrant community of users and collaborators who engage on Gitter, Google Forum, and IRC.

SailsJs充分利用了JavaScript社区中已编写且经过测试的模块。 该文档非常详细,并且有一个活跃的用户和协作者社区,他们参与了GitterGoogle ForumIRC

我们将构建什么:报价清单应用程序 ( What We'll Build: A Quote Listing App )

We'll be building a simple application that retrieves Chuck Norris quotes from a NodeJS backend with SailsJs. Scratch that, we’ll build our own Chuck Norris API. Once we add authentication to the app, all logged-in users will have the privilege of getting access to a set of private quotes.

我们将构建一个简单的应用程序,该应用程序使用SailsJsNodeJS后端检索Chuck Norris报价。 首先,我们将构建自己的Chuck Norris API。 将身份验证添加到应用程序后,所有登录的用户都将有权访问一组私人报价。

探索目录结构 ( Exploring the Directory Structure )

A typical SailsJS application has the following structure:

典型的SailsJS应用程序具有以下结构:

├── api │ ├── controllers │ ├── models │ ├── policies │ ├── responses │ └── services ├── assets │ ├── images │ ├── js │ │ └── dependencies │ ├── styles │ └── templates ├── config │ ├── env │ └── locales ├── node_modules │ ├── ejs │ ├── grunt │ ├── grunt-contrib-clean │ ├── rc │ ├── sails │ └── sails-disk ├── tasks │ ├── config │ └── register └── views

├──API│├──控制器│├──模型│├──策略│├──响应│└──服务├──资产│├──图像│├──js││└── ├──样式│└──模板├──配置│├──env│└──语言环境├──节点模块│├──ejs│├──grunt│├──grunt-contrib-clean│├── rc│├──风帆│└──风帆盘├──任务│├──配置│└──注册└──视图

The api directory constitutes the “M” and “C” in a typical MVC framework. In a nutshell,

api目录在典型的MVC框架中构成“ M”和“ C”。 简而言之,

  • Models query your database and return the necessary data.

    模型查询您的数据库并返回必要的数据。
  • Views are pages that render data. In SailsJs, they are .ejs template files.

    视图是呈现数据的页面。 在SailsJs中,它们是.ejs模板文件。
  • Controllers handle user requests, retrieve data from the models and pass them on to the views.

    控制器处理用户请求,从模型中检索数据,并将其传递给视图。
  • Policies are used to restrict access to certain parts of your app. It can be used for anything: HTTP Basic Auth, 3rd party Single Sign on, OAuth 2.0 or your own custom authorization scheme. ** Services** are more like helpers that you can write once and use anywhere in your app. They are globalised by default, so you don’t need to require them.

    策略用于限制对应用程序某些部分的访问。 它可以用于任何事物:HTTP基本身份验证,第三方单点登录,OAuth 2.0或您自己的自定义授权方案。 **服务**更像是助手,您可以编写一次并在应用程序中的任何位置使用。 默认情况下它们是全球化的,因此您不需要它们。

让我们开始吧。 ( Let's Get Started. )

SailsJs uses Npm to manage its dependencies. So, before using SailsJs, make sure you have node and npm installed on your machine. We can install the latest release of SailsJs from the command line like so:

SailsJs使用NPM来管理其依赖关系。 因此,在使用SailsJs之前,请确保已在计算机上安装了nodenpm 。 我们可以从命令行安装最新版本的SailsJs ,如下所示:

npm -g install sails

Once it is installed, we can now create a new app from the command line like so:

安装之后,我们现在可以从命令行创建一个新应用,如下所示:

sails new appcd app
sails lift

sails lift starts the web server and points you to the url where the app is currently been served.

sails lift启动Web服务器,然后将您指向当前为该应用程序提供服务的URL。

Read more about the anatomy of a sails app here

在此处阅读更多有关Sails应用程序的剖析

设置路线 ( Setting Up the Routes )

Sailsjs has a beautiful way of quickly generating API routes and actions based on your application design called Blueprints. The SailsJs Blueprint API powers the restful API that is obtained anytime a model and controller is created. For example, if you create a Task.js model and TaskController.js controller file in your project, blueprints will give you the ability to visit this url /task/create?id=1&name=code to create a new task. The local sails-disk db is used by default, so you don’t even have to worry about a data store just yet. Wow, just awesome!!!!

Sailsjs具有一种漂亮的方法,可以根据您的应用程序设计(称为蓝图)快速生成API路由和操作。 SailsJs蓝图API支持在创建模型和控制器时就获得的Restful API。 例如,如果您在项目中创建Task.js模型和TaskController.js控制器文件,则蓝图将使您能够访问此url / task / create?id = 1&name = code来创建新任务。 默认情况下使用本地的sails-disk db,因此您甚至不必担心数据存储。 哇,真棒!

Blueprints are great for prototyping. They can be easily enabled and disabled anytime.

蓝图非常适合制作原型。 它们可以随时轻松启用和禁用。

SailsJs ships with a scaffolding tool that you can use to generate your controller and model files easily with for an api resource.

SailsJs附带了一个脚手架工具,您可以使用该工具轻松地为api资源生成控制器和模型文件。

Running the command above creates a Task.js model and a TaskController.js controller file. For this project, we’ll manually create our files and routes. Open up config/routes.js and add this to the routes like so:

运行上面的命令将创建一个Task.js模型和一个TaskController.js控制器文件。 对于此项目,我们将手动创建文件和路由。 打开config / routes.js并将其添加到路由中,如下所示:

'get /api/random-quote': 'QuoteController.getQuote',
'get /api/protected/random-quote': 'QuoteController.getProtectedQuote'

Once a user hits those routes, the QuoteController is invoked, then the respective actions are responsible for processing the incoming requests and returning a response back to the client. Next, let’s set it up!

一旦用户点击了这些路由,QuoteController就会被调用,然后各自的动作负责处理传入的请求并将响应返回给客户端。 接下来,让我们进行设置!

设置控制器 ( Setting Up the Controllers )

Open up api/controllers directory and create a QuoteController.js file like so:

打开api / controllers目录,并创建一个QuoteController.js文件,如下所示:

/**
 * QuoteController
 *
 * @description :: Server-side logic for managing quotes
 * @help        :: See http://sailsjs.org/#!/documentation/concepts/Controllers
 */

module.exports = {
    getQuote: function(req, res) {
        return res.json({ quote: quoter.getRandomOne() });
    },

    getProtectedQuote: function(req, res) {
        return res.json({ quote: quoter.getRandomOne() });
    }
};

By convention, Sails controllers are Pascal-based. For example, you must define your controllers like the following: TaskController.js, ItemController.js. If you have experience with using ExpressJs, you will discover that the syntax is very familiar. Well, SailsJs is built on top of ExpressJs!

按照惯例,Sails控制器基于Pascal。 例如,您必须像以下那样定义控制器:TaskController.js,ItemController.js。 如果您有使用ExpressJ的经验,您会发现该语法非常熟悉。 好吧,SailsJs是基于ExpressJs构建的!

Note: You can also scaffold your controllers with action names like so:

注意:您还可以使用以下操作名称来构造控制器:

Now, where is quoter.getRandomOne coming from? quoter is a custom Sails service, getRandomOne is a method from the service. Let’s go ahead and create the service.

现在,quoter.getRandomOne来自哪里? quoter是自定义的Sails服务,getRandomOne是该服务中的方法。 让我们继续创建服务。

设置服务 ( Setting up the Service )

Services are simply functions( helpers ) that you can use anywhere in your app. They are globalized by default like I mentioned earlier. Open up api/services directory and create a quoter.js file like so:

服务只是您可以在应用程序中的任何位置使用的简单功能(助手)。 正如我前面提到的,它们默认情况下是全球化的。 打开api / services目录并创建一个quoter.js文件,如下所示:

var quotes = require('./quotes.json');

module.exports.getRandomOne = function() {
  var totalAmount = quotes.length;
  var rand = Math.floor(Math.random() * totalAmount);
  return quotes[rand];
}

Create a quotes.json file within that directory and dump this array of quotes in it. It’s that simple!!!

在该目录中创建一个quotes.json文件,并将此引号数组转储到其中。 就这么简单!!!

Let’s test our API routes with Postman. In my opinion, Postman is one of the best tools on the planet for testing your APIs during development. Run sails lift from your console and put the API url in your postman like so:

让我们用Postman测试我们的API路由。 我认为,Postman是地球上用于在开发过程中测试API的最佳工具之一。 从控制台运行sails lift ,将API网址放入邮递员中,如下所示:

Do the same for /api/protected/random-quote. We can now access our quotes from these two routes. The next phase is to protect the second route /api/protected/random-quote. We need some form of Authorization that will run as a middleware to verify that a particular user can access that route. SailsJS has an inbuilt Access-control-logic system called Policies.

对/ api / protected / random-quote进行相同的操作。 现在,我们可以从这两条路线访问报价。 下一阶段是保护第二条路由/ api / protected / random-quote。 我们需要某种形式的授权,可以作为中间件运行,以验证特定用户可以访问该路由。 SailsJS有一个内置的访问控制逻辑系统,称为Policy。

设定政策 ( Setting Up Policies )

Policies are simply Sails way of authorizing and granting access control. They let you allow or deny access to your controllers. Open up api/policies directory and create the file isAuthenticated.js like so:

策略只是Sails授权和授予访问控制的方式。 它们允许您允许或拒绝对控制器的访问。 打开api / policies目录并创建文件isAuthenticated.js,如下所示:

/**
 * isAuthenticated
 *
 */
var jwt = require('express-jwt');

var authCheck = jwt({
  secret: new Buffer('AUTH0_CLIENT_SECRET', 'base64'),
  audience: 'AUTH0_CLIENT_ID'
});

module.exports = authCheck;

Make sure you quickly npm install express-jwt to ensure that module exists. Auth0 is an authentication and authorization platform that you can use to easily secure your APIs. When signing up for an account, you will need to give your app a domain name which cannot be changed later. Since you can have multiple apps under the same account, how you name your domain will depend on your situation. In most cases, it’s best to name it with something that is relevant to your organization, such as your company’s name. If it makes sense, you can also use your application’s name–it’s up to you.

确保您快速npm install express-jwt以确保该模块存在。 Auth0是可用于轻松保护API的身份验证和授权平台。 注册帐户时,您需要为您的应用提供一个域名,以后无法更改。 由于您可以在同一个帐户下拥有多个应用程序,因此如何命名域取决于您的情况。 在大多数情况下,最好使用与您的组织相关的名称来命名它,例如公司名称。 如果可行,您也可以使用应用程序的名称-由您自己决定。

Your Auth0 domain takes the pattern your-domain.auth0.com and is used when configuring the Auth0 tools that we’ll see below. Once you’ve signed up, you’ll be asked what kind of authentication you’d like for your application. It’s fine to leave the defaults in place, as you’ll be able to change them later. After you have signed up, head over to your dashboard to check things out. If you click the Apps / APIs link in the left sidebar, you’ll see that your account gets created with a Default App. Click the Default App to see your credentials and other details.

您的Auth0域采用的格式为your-domain.auth0.com,并在配置Auth0工具时使用,我们将在下面看到。 注册后,系统会询问您要对应用程序进行哪种身份验证。 可以保留默认值,因为以后可以更改它们。 注册后,转到仪表板签出内容。 如果单击左侧栏中的“应用程序/ API”链接,则会看到您的帐户是使用默认应用程序创建的。 单击默认应用程序以查看您的凭据和其他详细信息。

So, replace AUTH0_CLIENT_ID and AUTH0_CLIENT_SECRET with the real values. Next, we need to map the isAuthenticated policy to the QuoteController.

因此,将AUTH0_CLIENT_ID和AUTH0_CLIENT_SECRET替换为实际值。 接下来,我们需要将isAuthenticated策略映射到QuoteController。

Note: Allowed Callback URLS and Allowed Origins should also be filled with the proper URLS. If you are running on port 3000, it will be http://localhost:3000/

注意:“允许的回调URL”和“允许的来源”也应填写正确的URL。 如果您在端口3000上运行,它将为http:// localhost:3000 /

Open up config/policies.js and add this like so:

打开config / policies.js并像这样添加:

module.exports.policies = {

  /***************************************************************************
  *                                                                          *
  * Default policy for all controllers and actions (`true` allows public     *
  * access)                                                                  *
  *                                                                          *
  ***************************************************************************/

  '*': true,

  QuoteController: {
    getProtectedQuote: 'isAuthenticated'
  }
};

Policies.js

Policies.js

So, the isAuthenticated policy will run just before the getProtectedQuote action of the QuoteController is invoked.

因此,isAuthenticated策略将在调用QuoteController的getProtectedQuote操作之前运行。

Now, try to access /api/protected/random-quote without a valid token!

现在,尝试在没有有效令牌的情况下访问/ api / protected / random-quote!

Note: Open up config/cors.js and make sure you it is configured like this to avoid CORS errors & preflight request errors.

注:打开配置/ cors.js并确保它被配置像这样以避免CORS错误和预检请求错误。

Aha!, We were denied access. Auth0 has secured our API. Next, let’s build the frontend to communicate with the API.

啊!,我们被拒绝了。 Auth0已保护我们的API。 接下来,让我们构建与API通信的前端。

建立前端 ( Build The Frontend )

We’ll use Angular 1.x to quickly build out the frontend to communicate with our quotes API.

我们将使用Angular 1.x快速构建前端以与我们的Quotes API通信。

Within the project directory, create a folder called angular. Move into the directory & run npm init to create a package.json file. Now, go ahead and install the following dependencies

在项目目录中,创建一个名为angular的文件夹。 移至目录并运行npm init来创建package.json文件。 现在,继续安装以下依赖项

# Angular related modules
npm install angular angular-material angular-ui-router angular-aria angular-animate

# Auth0 related modules
npm install angular-jwt angular-storage auth0-angular

# To serve the app (if not already installed)
npm install -g http-server

Next, let’s set up our app.js and index.html files to bootstrap the application. At this time we can let Angular know which modules we need access to from the dependencies we installed.

接下来,让我们设置app.js和index.html文件以引导应用程序。 此时,我们可以让Angular从安装的依赖项中知道我们需要访问哪些模块。

(function() {

  'use strict';

  angular
    .module('authApp', ['auth0', 'angular-storage', 'angular-jwt', 'ngMaterial', 'ui.router'])
    .config(function($provide, authProvider, $urlRouterProvider, $stateProvider, $httpProvider, jwtInterceptorProvider) {

      authProvider.init({
        domain: 'YOUR_AUTH0_DOMAIN',
        clientID: 'YOUR_AUTH0_CLIENT_ID'
      });
    });
})();

Here we’ve configured authProvider from auth0-angular with our credentials from the dashboard. Of course, you’ll want to replace the values in the sample with your own credentials.

在这里,我们使用仪表板中的凭据从auth0-angular配置了authProvider。 当然,您需要使用自己的凭据替换示例中的值。

<!DOCTYPE html>
<html lang="en" ng-app="authApp">
<head>
  <meta charset="UTF-8">
  <title>Get Chuck Norris Quotes</title>
  <link rel="stylesheet" href="node_modules/angular-material/angular-material.css">
  <!-- Auth0 Lock script and AngularJS module -->
  <script src="//cdn.auth0.com/js/lock-9.2.min.js"></script>

  <!-- Setting the right viewport -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
</head>
<body>

  <!-- TODO: create the toolbar and ui-view -->

  <script src="node_modules/angular/angular.js"></script>
  <script src="node_modules/angular-animate/angular-animate.js"></script>
  <script src="node_modules/angular-aria/angular-aria.js"></script>
  <script src="node_modules/angular-material/angular-material.js"></script>
  <script src="node_modules/angular-jwt/dist/angular-jwt.js"></script>
  <script src="node_modules/angular-storage/dist/angular-storage.js"></script>
  <script src="node_modules/auth0-angular/build/auth0-angular.js"></script>
  <script src="node_modules/angular-ui-router/release/angular-ui-router.js"></script>
</body>
</html>

Notice here that we’re bringing in the Auth0Lock widget script from Auth0’s CDN. This is the script that we’ll need to show the login box that Auth0 provides. By setting the viewport like we have, we make sure that the widget shows up properly on mobile devices.

请注意,这里是从Auth0的CDN引入Auth0Lock小部件脚本。 这是我们需要显示Auth0提供的登录框的脚本。 通过像我们一样设置视口,我们确保小部件在移动设备上正确显示。

设置工具栏 ( Set Up the Toolbar )

We will create a top navbar with Angular Material’s toolbar component. We’ll wrap this up into its own directive to keep things clean.

我们将使用Angular Material的工具栏组件创建一个顶部导航栏。 我们将其包装成自己的指令以保持环境整洁。

// components/toolbar/toolbar.dir.js

//组件/toolbar/toolbar.dir.js

(function() {

  'use strict';

  angular
    .module('authApp')
    .directive('toolbar', toolbar);

  function toolbar() {
    return {
      templateUrl: 'components/toolbar/toolbar.tpl.html',
      controller: toolbarController,
      controllerAs: 'toolbar'
    }
  }

  function toolbarController(auth, store, $location) {
    var vm = this;
    vm.login = login;
    vm.logout = logout;
    vm.auth = auth;

    function login() {
      // The auth service has a signin method that
      // makes use of Auth0Lock. If authentication
      // is successful, the user's profile and token
      // are saved in local storage with the store service
      auth.signin({}, function(profile, token) {
        store.set('profile', profile);
        store.set('token', token);
        $location.path('/');
      }, function(error) {
        console.log(error);
      })
    }

    function logout() {
      // The signout method on the auth service
      // sets isAuthenticated to false but we
      // also need to remove the profile and
      // token from local storage
      auth.signout();
      store.remove('profile');
      store.remove('token');
      $location.path('/');
    }
  }

})();

In the login method, auth.signin is responsible for opening Auth0’s Lock widget. This is awesome–with a single method, we have a fully-functioning login box! The callback gives us access to the user’s profile and JWT on a successful login or signup, and we need to store these in local storage for use later. The logout method simply removes the profile and token from local storage and uses auth.signout to set the user’s authentication state to false.

在登录方法中,auth.signin负责打开Auth0的Lock小部件。 太棒了–使用单一方法,我们将提供功能齐全的登录框! 回调使我们能够在成功登录或注册后访问用户的个人资料和JWT,我们需要将它们存储在本地存储中以备后用。 注销方法仅从本地存储中删除配置文件和令牌,并使用auth.signout将用户的身份验证状态设置为false。

We now need a template for this directive.

现在,我们需要此指令的模板。

<md-toolbar>
  <div class="md-toolbar-tools">
    <h2>
      <span>Get Chuck Norris Quotes</span>
    </h2>
    <span flex></span>

    <md-button
      aria-label="Login"
      ng-if="!toolbar.auth.isAuthenticated"
      ng-click="toolbar.login()">
      Login
    </md-button>

    <md-button
      ui-sref="profile"
      ng-if="toolbar.auth.isAuthenticated"
      aria-label="Profile">
      Profile
    </md-button>

    <md-button
      aria-label="Logout"
      ng-if="toolbar.auth.isAuthenticated"
      ng-click="toolbar.logout()">
      Logout
    </md-button>

  </div>
</md-toolbar>

Notice here that we are conditionally showing and hiding the three buttons based on the user’s isAuthenticated state. Let’s now drop this directive into our index.html file to see it at work. At the same time, we’ll drop in some other scripts that we’ll need later.

注意这里我们基于用户的isAuthenticated状态有条件地显示和隐藏三个按钮。 现在,将该指令放入我们的index.html文件中,以查看其工作情况。 同时,我们将添加其他一些稍后需要的脚本。

<!--- index.html -->
...

<body>

  <toolbar></toolbar>

  <div layout-padding>
    <ui-view></ui-view>
  </div>

  ...

  <script src="app.js"></script>
  <script src="components/toolbar/toolbar.dir.js"></script>
  <script src="components/profile/profile.ctr.js"></script>

</body>
</html>
`

Start the server and navigate to http://localhost:8080/ . We can now test this out by clicking the Login button and either signing up for an account, or signing in with an account we create in our Auth0 dashboard.

启动服务器并导航到http:// localhost:8080 / 。 现在,我们可以通过单击“登录”按钮并注册一个帐户,或使用在Auth0信息中心中创建的帐户登录来进行测试。

Once authentication is complete, we can see that the profile and JWT for the user have been saved in localStorage. Now we can use these for the user’s profile area and to make authenticated requests.

身份验证完成后,我们可以看到该用户的配置文件和JWT已保存在localStorage中。 现在,我们可以将其用于用户的个人资料区域并发出经过身份验证的请求。

设置路由 ( Set Up Routing )

We’ll use UI Router. Let’s make a /profile route that will take the user to their profile page and simply display some of the data that is saved in localStorage. We want this route to be protected so that if the user isn’t authenticated, they aren’t able to navigate to it. We’ll also set up a /home route so that the user is redirected to somewhere meaningful if they are logged out. First, let’s set up our home and profile components.

我们将使用UI路由器。 让我们进行一个/ profile路由,该路由会将用户带到其个人资料页面,并仅显示保存在localStorage中的一些数据。 我们希望此路由受到保护,以便如果用户未通过身份验证,他们将无法导航到该路由。 我们还将设置/ home路由,以便在用户注销后将其重定向到有意义的地方。 首先,让我们设置家庭和个人资料组件。

<!-- components/home/home.tpl.html -->

<md-content>
  <h1>Welcome to the Angular Auth app!</h1>
  <h3>Login from the toolbar above to access your profile.</h3>
</md-content>

We’ll also set up the profile view and controller with methods to make HTTP calls to the SailsJS API.

我们还将通过使用方法对Profiles视图和控制器进行设置,以对SailsJS API进行HTTP调用。

<!-- components/profile/profile.tpl.html -->
<md-content class="md-padding" layout="column">
  <md-card>
    <md-card-title>
      <md-card-title-media>
        <div class="md-media-lg card-media" layout-padding><img ng-src="{{ user.profile.picture }}"></div>
        <md-card-actions layout="column" layout-align="end center">
          <md-button ng-click="user.getPublicQuote()">Get Public Quote </md-button>
        <md-button ng-click="user.getSecretQuote()">Get Secret Quote</md-button>
        </md-card-actions>
      </md-card-title-media>
      <md-card-title-text layout-padding>
        <span class="md-headline">{{ user.profile.nickname }}</span>
        <span class="md-subhead">{{ user.profile.email }}</span>
        <h3>{{ user.message }}</h3>
      </md-card-title-text>
    </md-card-title>
  </md-card>
</md-content>
// components/profile/profile.ctr.js

(function() {

  'use strict';

  angular
    .module('authApp')
    .controller('profileController', profileController);

  function profileController($http) {

    var vm = this;
    vm.getPublicQuote = getPublicQuote;
    vm.getSecretQuote = getSecretQuote;
    vm.message;

    vm.profile = JSON.parse(localStorage.getItem('profile'));

    // Makes a call to a public API route that
    // does not require authentication. We can
    // avoid sending the JWT as an Authorization
    // header with skipAuthorization: true
    function getPublicQuote() {
      $http.get('http://localhost:1337/api/random-quote', {
        skipAuthorization: true
      }).then(function(response) {
        vm.message = response.data.quote;
      });
    }

    // Makes a call to a private endpoint that does
    // require authentication. The JWT is automatically
    // sent with HTTP calls using jwtInterceptorProvider in app.js
    function getSecretQuote() {
      $http.get('http://localhost:1337/api/protected/random-quote').then(function(response) {
        vm.message = response.data.quote;
      });
    }

  }

})();

The next thing we need to do is set up our routing. At the same time, we’ll set some configuration that will automatically attach the JWT as an Authorization header when making HTTP calls.

我们需要做的下一件事是设置路由。 同时,我们将设置一些配置,这些配置将在进行HTTP调用时自动将JWT附加为Authorization标头。

// app.js

// app.js

...

.config(function(...) {

  ...

  $urlRouterProvider.otherwise("/home");

  $stateProvider
    .state('home', {
      url: '/home',
      templateUrl: 'components/home/home.tpl.html'
    })
    .state('profile', {
      url: '/profile',
      templateUrl: 'components/profile/profile.tpl.html',
      controller: 'profileController as user'
    });

  jwtInterceptorProvider.tokenGetter = function(store) {
    return store.get('token');
  }

  $httpProvider.interceptors.push('jwtInterceptor');

Here we have provided some routing configuration for $stateProvider and have defaulted to the home state when the profile state isn’t matched. The jwtInterceptorProvider is the HTTP interceptor that takes care of attaching the user’s JWT as an Authorization header on each request. For any HTTP request that is made, Angular will intercept it before it goes out and attach whatever is returned from the tokenGetter function which, in this case, is the user’s JWT from local storage.

在这里,我们为$ stateProvider提供了一些路由配置,并且在配置文件状态不匹配时默认为原始状态。 jwtInterceptorProvider是HTTP拦截器,负责将用户的JWT作为每个请求的授权标头附加。 对于发出的任何HTTP请求,Angular都会在发出请求之前对其进行拦截,并附加从tokenGetter函数返回的任何内容,在本例中,该功能是用户从本地存储的JWT。

获取报价 ( Getting the Quotes )

Now, open a new console and make sure you are in the root directory of the sails project. Run sails lift to ensure the SailsJs server is running.

现在,打开一个新控制台,并确保您位于sails项目的根目录中。 运行sails lift ,以确保服务器运行的是SailsJs。

Log in, and click on the Profile page, then click on the Get Public Quotes and Get Private Quotes buttons. We can retrieve both set of quotes successfully.

登录,然后单击“个人资料”页面,然后单击“获取公共报价”和“获取私人报价”按钮。 我们可以成功检索两组报价。

At the bottom of the picture above, you can see how the token is attached with the Bearer to the Authorization header. It is sent on every request.

在上图的底部,您可以看到令牌与承载者如何附加到Authorization标头。 它根据每个请求发送。

在未经授权的请求下重定向用户 ( Redirect User on Unauthorized Request )

If a user makes a request to our API with an expired or invalid JWT,a blank screen will be presented to the user. Let’s use an HTTP interceptor to look for any 401 errors returned in responses and then redirect the user to the /home route if it finds any.

如果用户使用过期或无效的JWT向我们的API请求,则会向用户显示空白屏幕。 让我们使用HTTP拦截器查找响应中返回的任何401错误,然后将用户重定向到/ home路由(如果发现)。

// app.js

// app.js

...

.config(function(...) {

...

function redirect($q, $injector, auth, store, $location) {
  return {
    responseError: function(rejection) {

      if (rejection.status === 401) {
        auth.signout();
        store.remove('profile');
        store.remove('token');
        $location.path('/home')
      }
      return $q.reject(rejection);
    }
  }
}
$provide.factory('redirect', redirect);
$httpProvider.interceptors.push('redirect');

The redirect function is used to check for a rejection.status of 401 on any responses that come back from HTTP requests. If one is found, we use auth.signout to set isAuthenticated to false, remove the user’s profile and JWT, and take them to the home state.

重定向功能用于检查从HTTP请求返回的任何响应的401.status.status状态。 如果找到一个,我们使用auth.signout将isAuthenticated设置为false,删除用户的配置文件和JWT,并将它们带回原始状态。

设置页面刷新 ( Set Up Page Refreshing )

The last thing we need to take care of is page refreshing. Because everything we’ve done thus far relies on some state being persisted in the user’s browser, things will get messed up if they refresh the page. For example, if we log in and then refresh the page, the isAuthenticated boolean value that gets set on login isn’t persisted, and thus our Login button comes back, even though we are actually authenticated.

我们需要照顾的最后一件事是页面刷新。 因为到目前为止,我们所做的一切都依赖于用户浏览器中保持的某种状态,所以如果刷新页面,事情将会变得混乱。 例如,如果我们登录然后刷新页面,则登录时设置的isAuthenticated布尔值不会持久化,因此即使我们已通过身份验证,“登录”按钮也会返回。

We can fix this by doing a check for the user’s JWT on $locationChangeStart. // app.js

我们可以通过在$ locationChangeStart上检查用户的JWT来解决此问题。 // app.js

...

.run(function($rootScope, $state, auth, store, jwtHelper, $location) {

  $rootScope.$on('$locationChangeStart', function() {
    // Get the JWT that is saved in localStorage
    // and if it is there, check whether it is expired.
    // If it isn't, set the user's auth state
    var token = store.get('token');
    if (token) {
      if (!jwtHelper.isTokenExpired(token)) {
        if (!auth.isAuthenticated) {
          auth.authenticate(store.get('profile'), token);
        }
      }
    }
    else {
      // Otherwise, redirect to the home route
      $location.path('/home');
    }
  });

});

...

The callback in $locationChangeStart gets evaluated every time the page is refreshed, or when a new URL is reached. Inside the callback we are looking for a saved JWT, and if there is one, we check whether it is expired. If the JWT isn’t expired, we set the user’s auth state with their profile and token. If the JWT is expired, we redirect to the home route. Normally, we would be able to test out these redirections by going to the /profile route and removing the JWT from localStorage. If we were to then send a request to the protected API endpoint, we would be redirected to the home route because no JWT would be sent with the request, resulting in a 401.

每次刷新页面或访问新URL时,都会评估$ locationChangeStart中的回调。 在回调内部,我们正在寻找一个已保存的JWT,如果有,请检查它是否已过期。 如果JWT没有过期,我们将使用其个人资料和令牌设置用户的身份验证状态。 如果JWT过期,我们将重定向到本地路由。 通常,我们可以通过转到/ profile路由并从localStorage中删除JWT来测试这些重定向。 如果随后将请求发送到受保护的API端点,则会将我们重定向到本地路由,因为不会随请求一起发送JWT,结果为401。

结论 ( Conclusion )

With the help of SailsJS, AngularJS and Auth0, we have been able to spin up a functional and secure API. We have also been able to build a complete app with authentication. When trying to flesh out APIs with ease, SailsJS is the go-to Nodejs framework.

借助SailsJS,AngularJS和Auth0,我们已经能够启动功能强大且安全的API。 我们还能够构建具有身份验证的完整应用程序。 当尝试轻松充实API时,SailsJS是首选的Nodejs框架。

翻译自: https://scotch.io/tutorials/build-a-sailsjs-app-from-api-to-authentication

jenkins 构建api

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值