okta使用
本文最初发布在Okta开发人员博客上 。 感谢您支持使SitePoint成为可能的合作伙伴。
从jQuery开始,到Angular,我已经对JavaScript框架改组已经进行了很多年。 在对Angular的复杂性感到沮丧之后,我找到了React,并以为我很清楚。 表面上看似简单的事情最终变成了令人沮丧的混乱。 然后我找到了Vue.js。 感觉还不错。 它按预期工作。 很快 该文档令人难以置信。 模板雄辩。 关于如何处理状态管理,条件渲染,双向绑定,路由等等,存在一致的共识。
本教程将逐步指导您完成Vue.js项目,将安全认证转移到Okta的OpenID Connect API(OIDC) ,锁定受保护的路由以及通过后端REST API服务器执行CRUD操作的步骤。 本教程使用以下技术,但不需要任何专业知识:
- 带有vue-cli , vue-router和Okta Vue SDK的 Vue.js
- Express , Okta JWT Verifier , Sequelize和Epilogue的节点
关于Vue.js
Vue.js是一个健壮但简单的Javascript框架。 它是所有现代框架进入的最低障碍之一,同时为高性能Web应用程序提供了所有必需的功能。
本教程涵盖两个主要构建,一个前端Web应用程序和一个后端REST API服务器。 前端将是一个具有主页,登录和注销以及帖子管理器的单页面应用程序(SPA)。
Okta的OpenID Connect(OIDC)将通过使用Okta的Vue SDK处理我们的Web应用程序的身份验证。 如果未经身份验证的用户导航到帖子管理器,则Web应用程序应尝试对用户进行身份验证。
该服务器将运行带有Sequelize和Epilogue的 Express 。 在较高的层次上,使用Sequelize和Epilogue,您只需几行代码即可快速生成动态REST端点。
从Web应用程序和Express中间件中的Okta的JWT验证程序发出请求以验证令牌时,将使用基于JWT的身份验证。 您的应用程序将公开以下端点,所有端点均要求请求具有有效的访问令牌。
- GET /posts
- GET /posts/:id
- POST /posts
- PUT /posts/:id
- DELETE /posts/:id
创建您的Vue.js应用
为了快速启动您的项目,您可以利用vue-cli的脚手架功能。 在本教程中,您将使用渐进式Web应用程序(PWA)模板 ,其中包括一些功能,包括webpack , 热重载 ,CSS提取和单元测试。
如果您不熟悉PWA的宗旨,请查看我们关于渐进式Web应用程序的终极指南 。
要安装vue-cli
运行:
npm install -g vue-cli
接下来,您需要初始化您的项目。 运行vue init
命令时,只需接受所有默认值即可。
vue init pwa my-vue-app
cd ./my-vue-app
npm install
npm run dev
将您喜欢的浏览器指向http://localhost:8080
,您应该看到您的工作成果:
额外的信用 :查看适用于vue-cli
的其他模板 。
安装Bootstrap
让我们安装bootstrap-vue,以便您可以利用各种预制组件 (此外,您可以专注于功能而不是自定义CSS):
npm i --save bootstrap-vue bootstrap
要完成安装,请修改./src/main.js
以包含bootstrap-vue并导入所需CSS文件。 您的./src/main.js
文件应如下所示:
// 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: '<App/>',
components: { App }
})
使用Okta添加身份验证
在Web应用程序中处理身份验证是每个开发人员生存的祸根。 这就是Okta用最少的代码保护您的Web应用程序安全的地方。 首先,您需要在Okta中创建一个OIDC应用程序。 注册一个永久免费的开发者帐户 (如果已有,请登录)。
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
登录后,单击“添加应用程序”创建一个新的应用程序。
选择“单页应用”平台选项。
默认的应用程序设置应与图片相同。
要安装Okta Vue SDK,请运行以下命令:
npm i --save @okta/okta-vue
打开./src/router/index.js
并将整个文件替换为以下代码。
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: 'https://{yourOktaDomain}.com/oauth2/default',
client_id: '{yourClientId}',
redirect_uri: 'http://localhost:8080/implicit/callback',
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
您需要替换{yourOktaDomain}
和{yourClientId}
,它们可以在Okta开发者控制台的应用程序概述页面上找到。 这会将authClient
对象注入到Vue实例中,可以通过在Vue实例内的任何位置调用this.$auth
来访问它。
Vue.use(Auth, {
issuer: 'https://{yourOktaDomain}.com/oauth2/default',
client_id: '{yourClientId}',
redirect_uri: 'http://localhost:8080/implicit/callback',
scope: 'openid profile email'
})
Okta身份验证流程的最后一步是使用URL中的令牌值将用户重定向回您的应用程序。 SDK中包含的Auth.handleCallback()
组件可处理重定向并在浏览器中保留令牌。
{
path: '/implicit/callback',
component: Auth.handleCallback()
}
您还需要锁定受保护的路由,以防止未经身份验证的用户访问。 这可以通过实现导航卫士来实现。 顾名思义, 导航卫士主要用于通过重定向或取消来保护导航。
该SDK自带的方法auth.authRedirectGuard()
支票匹配路由的元数据的键requiresAuth
并且如果它们不认证的用户重定向至身份验证流程。
router.beforeEach(Vue.prototype.$auth.authRedirectGuard())
安装此导航保护后,具有以下元数据的任何路线都将受到保护。
meta: {
requiresAuth: true
}
在Vue中自定义您的应用布局
Web应用程序的布局位于组件./src/App.vue
。 您可以使用router-view组件来呈现给定路径的匹配组件。
对于主菜单,您将需要根据activeUser
的状态更改某些菜单项的可见性:
- 未验证:仅显示登录
- 已验证:仅显示注销
您可以使用Vue.js中的v-if
指令切换这些菜单项的可见性,该指令检查组件上是否存在activeUser
。 当组件加载时(调用created()
)或路由更改时,我们要刷新activeUser
。
打开./src/App.vue
并复制/粘贴以下代码。
<template>
<div id="app">
<b-navbar toggleable="md" type="dark" variant="dark">
<b-navbar-toggle target="nav_collapse"></b-navbar-toggle>
<b-navbar-brand to="/">My Vue App</b-navbar-brand>
<b-collapse is-nav id="nav_collapse">
<b-navbar-nav>
<b-nav-item to="/">Home</b-nav-item>
<b-nav-item to="/posts-manager">Posts Manager</b-nav-item>
<b-nav-item href="#" @click.prevent="login" v-if="!activeUser">Login</b-nav-item>
<b-nav-item href="#" @click.prevent="logout" v-else>Logout</b-nav-item>
</b-navbar-nav>
</b-collapse>
</b-navbar>
<!-- routes will be rendered here -->
<router-view />
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
activeUser: null
}
},
async created () {
await this.refreshActiveUser()
},
watch: {
// everytime a route is changed refresh the activeUser
'$route': 'refreshActiveUser'
},
methods: {
login () {
this.$auth.loginRedirect()
},
async refreshActiveUser () {
this.activeUser = await this.$auth.getUser()
},
async logout () {
await this.$auth.logout()
await this.refreshActiveUser()
this.$router.push('/')
}
}
}
</script>
每次登录都必须注销。 以下代码段将注销您的用户,刷新活动用户(现在为null),然后将用户重定向到首页。 当用户单击导航中的注销链接时,将调用此方法。
async logout () {
await this.$auth.logout()
await this.refreshActiveUser()
this.$router.push('/')
}
组件是Vue.js中的构建块。 您的每个页面都将在应用程序中定义为组件。 由于vue-cli Webpack模板使用vue-loader ,因此组件源文件具有将模板,脚本和样式分开的约定( 请参见此处 )。
现在,您已经添加了vue-bootstrap,修改./src/components/Hello.vue
以删除vue-cli生成的样板链接。
<template>
<div class="hero">
<div>
<h1 class="display-3">Hello World</h1>
<p class="lead">This is the homepage of your vue app</p>
</div>
</div>
</template>
<style>
.hero {
height: 90vh;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.hero .lead {
font-weight: 200;
font-size: 1.5rem;
}
</style>
此时,您可以将“邮递管理器”页面存根,以测试身份验证流程。 确认身份验证成功后,您将开始构建在Posts模型上执行CRUD操作所需的API调用和组件。
创建一个新文件./src/components/PostsManager.vue
并粘贴以下代码:
<template>
<div class="container-fluid mt-4">
<h1 class="h1">Posts Manager</h1>
<p>Only authenticated users should see this page</p>
</div>
</template>
使用您的Vue.js前端和身份验证流程进行测试
在终端中运行npm run dev
(如果尚未运行)。 导航到http://localhost:8080
,您应该看到新的主页。
如果单击Posts Manager或Login ,则应该直接转到Okta的流程。 输入您的Okta开发人员帐户凭据。
注意:如果您登录到Okta开发者帐户,您将被自动重定向回该应用程序。 您可以使用隐身或私人浏览模式对此进行测试。
如果成功,您应该返回登录的主页。
单击Posts Manager链接应呈现受保护的组件。
添加后端REST API服务器
现在,用户可以安全地进行身份验证,您可以构建REST API服务器以在帖子模型上执行CRUD操作。 将以下依赖项添加到您的项目中:
npm i --save express cors @okta/jwt-verifier sequelize sqlite3 epilogue axios
然后,创建文件./src/server.js
并粘贴以下代码。
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: '{yourClientId}',
issuer: 'https://{yourOktaDomain}.com/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')
})
})
确保使用来自Okta的OIDC应用中的值替换上面代码中的变量{yourOktaDomain}
和{clientId}
。
添加续集
Sequelize是Node.js的基于承诺的ORM。 它支持方言PostgreSQL,MySQL,SQLite和MSSQL,并具有可靠的事务支持,关系,读取复制等功能。
为了简化本教程,您将使用SQLite限制外部依赖关系。 以下代码使用SQLite作为驱动程序初始化Sequelize实例。
let database = new Sequelize({
dialect: 'sqlite',
storage: './test.sqlite'
})
每个帖子都有title
和body
。 (字段createdAt
,和updatedAt
由Sequelize自动添加)。 使用Sequelize,您可以通过在实例上调用define()
来定义模型。
let Post = database.define('posts', {
title: Sequelize.STRING,
body: Sequelize.TEXT
})
添加结尾
Epilogue通过Express应用程序中的Sequelize模型创建灵活的REST端点。 如果您曾经编码过REST端点,您就会知道有多少重复。 干FTW!
// 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']
})
验证您的JWT
这是REST API服务器中最关键的组件。 没有此中间件,任何用户都可以在我们的数据库上执行CRUD操作。 如果不存在授权头,或者访问令牌无效,则API调用将失败并返回错误。
// 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!
})
运行服务器
打开一个新的终端窗口,并使用命令node ./src/server
运行服务器。 您应该从Sequelize中看到调试信息,并且该应用程序正在侦听端口8081。
完成帖子管理器组件
现在REST API服务器已经完成,您可以开始连接帖子管理器以获取帖子,创建帖子,编辑帖子和删除帖子。
我总是将我的API集成集中到单个帮助程序模块中。 这样可以使组件中的代码更加整洁,并提供单个位置,以防您需要通过API请求进行任何更改。
创建文件./src/api.js
并将以下代码复制/粘贴到其中:
import Vue from 'vue'
import axios from 'axios'
const client = axios.create({
baseURL: 'http://localhost:8081/',
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}`)
}
}
当您使用OIDC进行身份验证时,访问令牌将在浏览器中本地保留。 由于每个API请求都必须具有访问令牌,因此您可以从身份验证客户端获取它,并将其设置在请求中。
let accessToken = await Vue.prototype.$auth.getAccessToken()
return client({
method,
url: resource,
data,
headers: {
Authorization: `Bearer ${accessToken}`
}
})
通过在API帮助器中创建以下代理方法,帮助器模块外部的代码将保持清晰和语义。
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}`)
}
现在,您具有连接帖子管理器组件以通过REST API进行CRUD操作所需的所有组件。 打开./src/components/PostsManager.vue
并复制/粘贴以下代码。
<template>
<div class="container-fluid mt-4">
<h1 class="h1">Posts Manager</h1>
<b-alert :show="loading" variant="info">Loading...</b-alert>
<b-row>
<b-col>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Updated At</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr v-for="post in posts" :key="post.id">
<td>{{ post.id }}</td>
<td>{{ post.title }}</td>
<td>{{ post.updatedAt }}</td>
<td class="text-right">
<a href="#" @click.prevent="populatePostToEdit(post)">Edit</a> -
<a href="#" @click.prevent="deletePost(post.id)">Delete</a>
</td>
</tr>
</tbody>
</table>
</b-col>
<b-col lg="3">
<b-card :title="(model.id ? 'Edit Post ID#' + model.id : 'New Post')">
<form @submit.prevent="savePost">
<b-form-group label="Title">
<b-form-input type="text" v-model="model.title"></b-form-input>
</b-form-group>
<b-form-group label="Body">
<b-form-textarea rows="4" v-model="model.body"></b-form-textarea>
</b-form-group>
<div>
<b-btn type="submit" variant="success">Save Post</b-btn>
</div>
</form>
</b-card>
</b-col>
</b-row>
</div>
</template>
<script>
import api from '@/api'
export default {
data () {
return {
loading: false,
posts: [],
model: {}
}
},
async created () {
this.refreshPosts()
},
methods: {
async refreshPosts () {
this.loading = true
this.posts = await api.getPosts()
this.loading = false
},
async populatePostToEdit (post) {
this.model = Object.assign({}, post)
},
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()
},
async deletePost (id) {
if (confirm('Are you sure you want to delete this post?')) {
// if we are editing a post we deleted, remove it from the form
if (this.model.id === id) {
this.model = {}
}
await api.deletePost(id)
await this.refreshPosts()
}
}
}
}
</script>
上市帖子
您将使用api.getPosts()
从REST API服务器中获取帖子。 加载组件时以及任何更改操作(创建,更新或删除)之后,您应该刷新帖子列表。
async refreshPosts () {
this.loading = true
this.posts = await api.getPosts()
this.loading = false
}
切换属性this.loading
,以便UI可以反映挂起的API调用。 您可能看不到加载消息,因为API请求没有发送到互联网。
创建帖子
组件中包含一个表格来保存帖子。 提交表单并将其输入绑定到组件上的model
对象时,它会连接调用savePosts()
。
调用savePost()
,它将基于model.id
的存在执行更新或创建。 这通常是一种快捷方式,不必定义两个单独的表单即可进行创建和更新。
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()
}
更新帖子
更新帖子时,首先必须将帖子加载到表单中。 这将设置model.id
,它将触发savePost()
的更新。
async populatePostToEdit (post) {
this.model = Object.assign({}, post)
}
重要提示: Object.assign()
调用复制post参数而不是引用的值。 在Vue中处理对象变异时,应始终将其设置为值,而不是引用。
删除帖子
要删除帖子,只需调用api.deletePost(id)
。 在删除之前进行确认始终是一件好事,因此让我们在本机确认警报框中添加以确保单击是有意的。
async deletePost (id) {
if (confirm('Are you sure you want to delete this post?')) {
await api.deletePost(id)
await this.refreshPosts()
}
}
测试您的Vue.js + Node CRUD应用
确保服务器和前端都在运行。
1号航站楼
node ./src/server
2号航站楼
npm run dev
导航到http://localhost:8080
并进行旋转。
用Vue做更多事!
正如我在这篇文章的开头所说,我认为Vue领先于其他框架。 以下是五个快速原因:
- 简单的组件生命周期
- 基于HTML的模板和本机双向绑定
- 广泛同意处理路由 , 状态管理 , Webpack配置和同构Web应用程序的方法
- 大量社区支持的资源,组件,库和项目
- Vue与React (没有JSX!)非常相似,它降低了具有React经验的人员的进入门槛。 在React和Vue之间移动不是很困难。
我在本教程中介绍了很多内容,但是如果您第一次不掌握所有内容,也不会感到难过。 您使用这些技术的次数越多,他们就会变得越熟悉。
要了解有关Vue.js的更多信息,请转到https://vuejs.org或从@oktadev团队中查看以下其他重要资源:
您可以在https://github.com/oktadeveloper/okta-vue-node-example上找到本文中开发的应用程序的源代码。
与往常一样,在Twitter上关注@oktadev ,以查看我们的开发团队正在创建的所有精彩内容。
okta使用