博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Build a Basic CRUD App with Vue.js and nodejs
阅读量:5782 次
发布时间:2019-06-18

本文共 18459 字,大约阅读时间需要 61 分钟。

https://developer.okta.com/blog/2018/02/15/build-crud-app-vuejs-node#add-authentication-with-okta

 

I’ve danced the JavaScript framework shuffle for years starting with jQuery, then on to Angular. After being frustrated with Angular’s complexity, I found React and thought I was in the clear. What seemed simple on the surface ended up being a frustrating mess. Then I found Vue.js. It just felt right. It worked as expected. It was fast. The documentation was incredible. Templating was eloquent. There was a unanimous consensus around how to handle state management, conditional rendering, two-way binding, routing, and more.

This tutorial will take you step by step through scaffolding a Vue.js project, offloading secure authentication to , locking down protected routes, and performing CRUD operations through a backend REST API server. This tutorial uses the following technologies but doesn’t require intimate knowledge to follow along:

  • Vue.js with , , and
  • Node with , , , and

Vue.js is a robust but simple Javascript framework. It has one of the lowest barriers to entry of any modern framework while providing all the required features for high performance web applications.

Vue.js Homepage

This tutorial covers two primary builds, a frontend web app and backend REST API server. The frontend will be a single page application (SPA) with a homepage, login and logout, and a posts manager.

will handle our web app’s authentication through the use of . If an unauthenticated user navigates to the posts manager, the web app should attempt to authenticate the user.

The server will run with and . At a high level, with Sequelize and Epilogue you can quickly generate dynamic REST endpoints with just a few lines of code.

You will use JWT-based authentication when making requests from the web app and in an Express middleware to validate the token. Your app will expose the following endpoints which all require requests to have a valid access token.

- GET /posts- GET /posts/:id- POST /posts- PUT /posts/:id- DELETE /posts/:id

To get your project off the ground quickly you can leverage the scaffolding functionality from . For this tutorial, you are going to use the that includes a handful of features including , , CSS extraction, and unit testing.

If you’re not familiar with the tenets of PWA, check out our .

To install vue-cli run:

npm install -g vue-cli@2.9.3

Next, you need to initialize your project. When you run the vue init command just accept all the default values.

vue init pwa my-vue-appcd ./my-vue-appnpm installnpm run dev

Point your favorite browser to and you should see the fruits of your labor:

Welcome to Your Vue.js PWA

Extra Credit: Check out the other available for vue-cli.

Let’s install so you can take advantage of the various premade (plus you can keep the focus on functionality and not on custom CSS):

npm i bootstrap-vue@2.0.0-rc.7 bootstrap@4.1.0

To complete the installation, modify ./src/main.js to include and import the required CSS files. Your ./src/main.js file should look like this:

// The Vue build version to load with the `import` command// (runtime-only or standalone) has been set in webpack.base.conf with an alias.import Vue from 'vue' import App from './App' import router from './router' import BootstrapVue from 'bootstrap-vue' import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' Vue.use(BootstrapVue) Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, template: '
', components: { App } })

Dealing with authentication in a web app is the bane of every developer’s existence. That’s where Okta comes in to secure your web applications with minimal code. To get started, you will need to create an OIDC application in Okta. (or log in if you already have one).

Okta Developer Sign Up

Once logged in, create a new application by clicking “Add Application”.

Add Application

Select the “Single-Page App” platform option.

New Application Options

The default application settings should be the same as those pictured.

Okta Application Settings

To install the Okta Vue SDK, run the following command:

npm i @okta/okta-vue@1.0.0

Open ./src/router/index.js and replace the entire file with the following code.

import Vue from 'vue'import Router from 'vue-router' import Hello from '@/components/Hello' import PostsManager from '@/components/PostsManager' import Auth from '@okta/okta-vue' Vue.use(Auth, { issuer: '}/oauth2/default', client_id: '{clientId}', redirect_uri: '', scope: 'openid profile email' }) Vue.use(Router) let router = new Router({ mode: 'history', routes: [ { path: '/', name: 'Hello', component: Hello }, { path: '/implicit/callback', component: Auth.handleCallback() }, { path: '/posts-manager', name: 'PostsManager', component: PostsManager, meta: { requiresAuth: true } } ] }) router.beforeEach(Vue.prototype.$auth.authRedirectGuard()) export default router

You’ll need to replace {yourOktaDomain} and {clientId} which can be found on your application overview page in the Okta Developer Console. This will inject an authClient object into your Vue instance which can be accessed by calling this.$auth anywhere inside your Vue instance.

Vue.use(Auth, { issuer: '}/oauth2/default', client_id: '{clientId}', redirect_uri: '', scope: 'openid profile email' })

The final step of Okta’s authentication flow is redirecting the user back to your app with the token values in the URL. The Auth.handleCallback() component included in the SDK handles the redirect and persists the tokens on the browser.

{  path: '/implicit/callback', component: Auth.handleCallback() }

You also need to lock down protected routes from being accessed by unauthenticated users. This is accomplished by implementing a . As the name suggests, navigation guards are primarily used to guard navigations either by redirecting or canceling.

The SDK comes with the method auth.authRedirectGuard() that checks matched routes’ metadata for the key requiresAuth and redirects the user to the authentication flow if they are not authenticated.

router.beforeEach(Vue.prototype.$auth.authRedirectGuard())

With this navigation guard installed, any route that has the following metadata will be protected.

meta: {  requiresAuth: true }

The web app’s layout is located in a component ./src/App.vue. You can use the component to render the matched component for the given path.

For the main menu, you’ll want to change the visibility of certain menu items based on the status of the activeUser:

  • Not Authenticated: Show only Login
  • Authenticated: Show only Logout

You can toggle the visibility of these menu items using the v-if directive in Vue.js that checks the existence of activeUser on the component. When the component is loaded (which calls created()) or when a route changes we want to refresh the activeUser.

Open ./src/App.vue and copy/paste the following code.

Every login must have a logout. The following snippet will logout your user, refresh the active user (which is now null), and then redirect the user to the homepage. This method is called when a user clicks on the logout link in the nav.

async logout () {  await this.$auth.logout() await this.refreshActiveUser() this.$router.push('/') }

are the building blocks within Vue.js. Each of your pages will be defined in the app as a component. Since the vue-cli webpack template utilizes , your component source files have a convention that separates template, script, and style ().

Now that you’ve added vue-bootstrap, modify ./src/components/Hello.vue to remove the boilerplate links vue-cli generates.

At this point you can stub out the Post Manager page to test your authentication flow. Once you confirm authentication works, you’ll start to build out the API calls and components required to perform CRUD operations on your Posts model.

Create a new file ./src/components/PostsManager.vue and paste the following code:

In your terminal run npm run dev (if it’s not already running). Navigate to and you should see the new homepage.

Hello World

If you click Posts Manager or Login you should be directed to Okta’s flow. Enter your Okta dev account credentials.

NOTE: If you are logged in to your Okta Developer Account you will be redirected automatically back to the app. You can test this by using incognito or private browsing mode.

Okta Sign-In

If successful, you should return to the homepage logged in.

Homepage after logging in

Clicking on Posts Manager link should render the protected component.

Posts Manager

Now that users can securely authenticate, you can build the REST API server to perform CRUD operations on a post model. Add the following dependencies to your project:

npm i express@4.16.3 cors@2.8.4 @okta/jwt-verifier@0.0.11 sequelize@4.37.6 sqlite3@4.0.0 epilogue@0.7.1 axios@0.18.0

Then, create the file ./src/server.js and paste the following code.

const express = require('express') const cors = require('cors') const bodyParser = require('body-parser') const Sequelize = require('sequelize') const epilogue = require('epilogue') const OktaJwtVerifier = require('@okta/jwt-verifier') const oktaJwtVerifier = new OktaJwtVerifier({ clientId: '{clientId}', issuer: '}/oauth2/default' }) let app = express() app.use(cors()) app.use(bodyParser.json()) // verify JWT token middleware app.use((req, res, next) => { // require every request to have an authorization header if (!req.headers.authorization) { return next(new Error('Authorization header is required')) } let parts = req.headers.authorization.trim().split(' ') let accessToken = parts.pop() oktaJwtVerifier.verifyAccessToken(accessToken) .then(jwt => { req.user = { uid: jwt.claims.uid, email: jwt.claims.sub } next() }) .catch(next) // jwt did not verify! }) // For ease of this tutorial, we are going to use SQLite to limit dependencies let database = new Sequelize({ dialect: 'sqlite', storage: './test.sqlite' }) // Define our Post model // id, createdAt, and updatedAt are added by sequelize automatically let Post = database.define('posts', { title: Sequelize.STRING, body: Sequelize.TEXT }) // Initialize epilogue epilogue.initialize({ app: app, sequelize: database }) // Create the dynamic REST resource for our Post model let userResource = epilogue.resource({ model: Post, endpoints: ['/posts', '/posts/:id'] }) // Resets the database and launches the express app on :8081 database .sync({ force: true }) .then(() => { app.listen(8081, () => { console.log('listening to port localhost:8081') }) })

Make sure to replace the variables {yourOktaDomain} and {clientId} in the above code with values from your OIDC app in Okta.

is a promise-based ORM for Node.js. It supports the dialects PostgreSQL, MySQL, SQLite, and MSSQL and features solid transaction support, relations, read replication, and more.

For ease of this tutorial, you’re going to use SQLite to limit external dependencies. The following code initializes a Sequelize instance using SQLite as your driver.

let database = new Sequelize({ dialect: 'sqlite', storage: './test.sqlite' })

Each post has a title and body. (The fields createdAt, and updatedAt are added by Sequelize automatically). With Sequelize, you define models by calling define() on your instance.

let Post = database.define('posts', {  title: Sequelize.STRING,  body: Sequelize.TEXT})

creates flexible REST endpoints from Sequelize models within an Express app. If you ever coded REST endpoints you know how much repetition there is. D.R.Y. FTW!

// Initialize epilogueepilogue.initialize({ app: app, sequelize: database }) // Create the dynamic REST resource for our Post model let userResource = epilogue.resource({ model: Post, endpoints: ['/posts', '/posts/:id'] })

This is the most crucial component of your REST API server. Without this middleware any user can perform CRUD operations on our database. If no authorization header is present, or the access token is invalid, the API call will fail and return an error.

// verify JWT token middlewareapp.use((req, res, next) => { // require every request to have an authorization header if (!req.headers.authorization) { return next(new Error('Authorization header is required')) } let parts = req.headers.authorization.trim().split(' ') let accessToken = parts.pop() oktaJwtVerifier.verifyAccessToken(accessToken) .then(jwt => { req.user = { uid: jwt.claims.uid, email: jwt.claims.sub } next() }) .catch(next) // jwt did not verify! })

Open a new terminal window and run the server with the command node ./src/server. You should see debug information from Sequelize and the app listening on port 8081.

Now that the REST API server is complete, you can start wiring up your posts manager to fetch posts, create posts, edit posts, and delete posts.

I always centralize my API integrations into a single helper module. This keeps the code in components much cleaner and provides single location in case you need to change anything with the API request.

Create a file ./src/api.js and copy/paste the following code into it:

import Vue from 'vue'import axios from 'axios' const client = axios.create({ baseURL: '', json: true }) export default { async execute (method, resource, data) { // inject the accessToken for each request let accessToken = await Vue.prototype.$auth.getAccessToken() return client({ method, url: resource, data, headers: { Authorization: `Bearer ${ accessToken}` } }).then(req => { return req.data }) }, getPosts () { return this.execute('get', '/posts') }, getPost (id) { return this.execute('get', `/posts/${ id}`) }, createPost (data) { return this.execute('post', '/posts', data) }, updatePost (id, data) { return this.execute('put', `/posts/${ id}`, data) }, deletePost (id) { return this.execute('delete', `/posts/${ id}`) } }

When you authenticate with OIDC, an access token is persisted locally in the browser. Since each API request must have an access token, you can fetch it from the authentication client and set it in the request.

let accessToken = await Vue.prototype.$auth.getAccessToken() return client({ method, url: resource, data, headers: { Authorization: `Bearer ${ accessToken}` } })

By creating the following proxy methods inside your API helper, the code outside the helper module remains clean and semantic.

getPosts () {  return this.execute('get', '/posts') }, getPost (id) { return this.execute('get', `/posts/${ id}`) }, createPost (data) { return this.execute('post', '/posts', data) }, updatePost (id, data) { return this.execute('put', `/posts/${ id}`, data) }, deletePost (id) { return this.execute('delete', `/posts/${ id}`) }

You now have all the components required to wire up your posts manager component to make CRUD operations via the REST API. Open ./src/components/PostsManager.vue and copy/paste the following code.

You’ll use api.getPosts() to fetch posts from your REST API server. You should refresh the list of posts when the component is loaded and after any mutating operation (create, update, or delete).

async refreshPosts () {  this.loading = true this.posts = await api.getPosts() this.loading = false }

The attribute this.loading is toggled so the UI can reflect the pending API call. You might not see the loading message since the API request is not going out to the internet.

A form is included in the component to save a post. It’s wired up to call savePosts() when the form is submitted and its inputs are bound to the model object on the component.

When savePost() is called, it will perform either an update or create based on the existence of model.id. This is mostly a shortcut to not have to define two separate forms for creating and updating.

async savePost () {  if (this.model.id) { await api.updatePost(this.model.id, this.model) } else { await api.createPost(this.model) } this.model = {} // reset form await this.refreshPosts() }

When updating a post, you first must load the post into the form. This sets model.id which will the trigger an update in savePost().

async populatePostToEdit (post) { this.model = Object.assign({}, post) }

Important: The Object.assign() call copies the value of the post argument rather than the reference. When dealing with mutation of objects in Vue, you should always set to the value, not reference.

To delete a post simply call api.deletePost(id). It’s always good to confirm before delete so let’s throw in a native confirmation alert box to make sure the click was intentional.

async deletePost (id) { if (confirm('Are you sure you want to delete this post?')) { await api.deletePost(id) await this.refreshPosts() } }

Make sure both the server and frontend are running.

Terminal #1

node ./src/server

Terminal #2

npm run dev

Navigate to and give it a whirl.

New Post

New Hello World Post

Delete Post

As I said at the top of this post, I think Vue stands head and shoulders above other frameworks. Here are five quick reasons why:

  • and native
  • Widely agreed upon ways to handle , , , and
  • Massive community supported
  • Vue feels very similar to (without the JSX!) which lowers the barrier to entry for those with React experience. Moving between React and Vue isn’t very difficult.

I covered a lot of material in this tutorial but don’t feel bad if you didn’t grasp everything the first time. The more you work with these technologies, the more familiar they will become.

To learn more about Vue.js head over to or check out these other great resources from the :

You can find the source code for the application developed in this post at .

Hit me up in the comments with any questions, and as always, follow on Twitter to see all the cool content our dev team is creating.

  • Apr 16, 2018: Updated to use the latest dependencies, including Okta’s Vue SDK 1.0.0. See the code changes in . Changes to this article can be viewed in .
  • Mar 12, 2018: Updated to use the latest dependencies, including Bootstrap 4.0.0. See the code changes in . Changes to this article can be viewed in .

 

转载地址:http://whcyx.baihongyu.com/

你可能感兴趣的文章
Java架构师面试题系列整理(大全)
查看>>
延伸产业链 中国产粮大省向“精深”问发展
查看>>
消费贷用户70%月收入低于5000元 80、90后是主要人群
查看>>
2018年内蒙古外贸首次突破1000亿元
查看>>
CTOR有助于BCH石墨烯技术更上一层楼
查看>>
被遗忘的CSS
查看>>
Webpack中的sourcemap以及如何在生产和开发环境中合理的设置sourcemap的类型
查看>>
做完小程序项目、老板给我加了6k薪资~
查看>>
java工程师linux命令,这篇文章就够了
查看>>
关于React生命周期的学习
查看>>
webpack雪碧图生成
查看>>
搭建智能合约开发环境Remix IDE及使用
查看>>
Spring Cloud构建微服务架构—服务消费基础
查看>>
RAC实践采坑指北
查看>>
runtime运行时 isa指针 SEL方法选择器 IMP函数指针 Method方法 runtime消息机制 runtime的使用...
查看>>
LeetCode36.有效的数独 JavaScript
查看>>
Scrapy基本用法
查看>>
PAT A1030 动态规划
查看>>
自制一个 elasticsearch-spring-boot-starter
查看>>
软件开发学习的5大技巧,你知道吗?
查看>>