deno使用rust_使用身份验证构建您的第一个Deno应用

deno使用rust

The creator of Node.js, Ryan Dahl has authored a new framework for designing web applications. He went back and fixed some mistakes he made in hindsight, taking advantage of new technologies that were not available at the time he originally wrote Node. The result is Deno (pronounced DEH-no), a framework for writing "Node-like" web applications in TypeScript. Here, I will walk you through creating a basic web application with authentication.

Node.js的创建者Ryan Dahl创建了一个用于设计Web应用程序的新框架。 他回过头来,利用在最初编写Node时尚不可用的新技术,纠正了事后发现的一些错误。 结果就是Deno (发音为DEH-no),这是一个用TypeScript编写“类似于节点的” Web应用程序的框架。 在这里,我将引导您创建带有身份验证的基本Web应用程序。

You can find almost all the information you need at the Deno website—along with information on all the third-party libraries that are currently available for Deno. That is really the biggest drawback to the framework right now. It just hit version 1.0 on May 13th of 2020, so even though there are quite a few essential libraries, there are not nearly as many libraries as there are for Node. For those who are proficient in Node however, the transition to Deno should be pretty easy.

您可以在Deno网站上找到几乎所需的所有信息,以及有关当前可用于Deno的所有第三方库的信息。 这确实是当前框架的最大缺点。 它只是在2020年5月13日发行了1.0版,因此即使有很多基本库,也没有Node的库那么多。 对于那些精通Node的人,向Deno的过渡应该很容易。

You can find the installation instructions at https://deno.land/#installation.

您可以在https://deno.land/#installation中找到安装说明。

创建您的Deno应用程序 ( Create Your Deno Application )

There aren't any basic scaffolding libraries that I could find, so I just started with an empty folder. In the application's root folder, create a file called index.ts that will be the starting point of your Deno application. You'll use Opine, which is an Express clone for Deno to make building and routing easier.

我找不到任何基本的脚手架库,因此我只是从一个空文件夹开始。 在应用程序的根文件夹中,创建一个名为index.ts文件,该文件将成为您的Deno应用程序的起点。 您将使用Opine ,它是Deno的Express克隆版本,可简化构建和布线。

One thing that is different about Deno is that there are no package managers for bringing in third-party libraries. You do this by using the library's full URL. Do that at the top of the index.ts file, then set up a basic web application.

与Deno不同的一件事是,没有用于引入第三方库的程序包管理器。 您可以通过使用库的完整URL来完成此操作。 在index.ts文件的顶部执行index.ts操作,然后设置一个基本的Web应用程序。

import { opine } from 'https://deno.land/x/opine@0.12.0/mod.ts';

const app = opine();

app.get('/', (req, res) => {
  res.send('Deno Sample');
});

app.listen(3000);
console.log('running on port 3000');

You can then run this very basic application by going to the terminal in the application's folder and entering:

然后,可以通过转到应用程序文件夹中的终端并输入以下内容来运行此非常基本的应用程序:

deno run -A index.ts

The -A is a shortcut for development purposes. Deno is completely locked down by default, so you'll need to pass arguments to the run command to allow access like --allow-net to allow networking, and --allow-read to allow the application to read from the file system. The -A used here allows everything, effectively disabling all security. When you run this application and then go to http://localhost:3000 you should be greeted with Deno Sample on a blank page.

-A是用于开发目的的快捷方式。 默认情况下,Deno完全处于锁定状态,因此您需要将参数传递给run命令以允许访问--allow-net以允许联网,以及--allow-read以允许应用程序从文件系统读取。 此处使用的-A允许所有操作,从而有效地禁用了所有安全性。 当您运行此应用程序然后转到http://localhost:3000您应该在空白页上看到Deno Sample

使用Deno构建真实的Web应用程序 ( Build a Real Web Application with Deno )

While this is a good first step, it's not very useful. You'll want to add some real functionality that's a little more "real-world", so change the index.ts file so that the contents are:

虽然这是一个很好的第一步,但不是很有用。 您将需要添加一些实际功能,这些功能稍微有些“真实世界”,因此请更改index.ts文件,以使内容为:

import { opine, serveStatic } from 'https://deno.land/x/opine@0.12.0/mod.ts';
import { renderFileToString } from 'https://deno.land/x/dejs@0.7.0/mod.ts';
import { join, dirname } from 'https://deno.land/x/opine@main/deps.ts';

import { ensureAuthenticated } from './middleware/authmiddleware.ts';
import users from './controllers/usercontroller.ts';
import auth from './controllers/authcontroller.ts';

const app = opine();
const __dirname = dirname(import.meta.url);

app.engine('.html', renderFileToString);
app.use(serveStatic(join(__dirname, 'public')));
app.set('view engine', 'html');

app.get('/', (req, res) => {
  res.render('index', { title: 'Deno Sample' });
});

app.use('/users', ensureAuthenticated, users);
app.use('/auth', auth)

app.listen(3000);
console.log('running on port 3000');

You'll notice some more import statements which bring in some third-party libraries. Here, I am using dejs which is an EJS port for Deno. I've also included some utility classes from the Opine library for manipulating directory names. I will explain what the three files imported locally are in a moment. For now, just know that you're importing them.

您会注意到一些引入一些第三方库的import语句。 在这里,我使用的是dejs ,这是Deno的EJS端口。 我还包括了Opine库中的一些实用程序类,用于处理目录名称。 我将介绍一下本地导入的三个文件。 现在,只知道您正在导入它们。

The line below the instantiation of the opine() app creates a reference to the local directory. The three lines below use this to set the view engine to DEJS for processing the HTML-like files, similar to the way EJS does for Node. The next section has been changed slightly to render one of those HTML template files, and the last two lines bring in some external routes. One thing of note is that the /users route has an ensureAuthenticated() middleware function. This will force users to log in before being allowed to visit the page. You'll create that middleware shortly.

opine()应用程序实例化下面的行创建对本地目录的引用。 下面的三行代码将视图引擎设置为DEJS,以处理类似HTML的文件,类似于EJS对Node的处理方式。 下一部分已稍作更改以呈现这些HTML模板文件之一,并且最后两行引入了一些外部路由。 需要注意的一件事是/users路由具有ensureAuthenticated()中间件功能。 这将迫使用户先登录,然后才能访问该页面。 您将很快创建该中间件。

填写您的Deno应用程序 ( Fill In Your Deno Application )

Now, you'll want to create some of the missing pieces that you imported above. Start with the routes. Create a folder called controllers in the root of the application. Then add a usercontroller.ts file inside that folder with the following contents:

现在,您将要创建一些您在上面导入的缺失片段。 从路线开始。 在应用程序的根目录中创建一个名为controllers的文件夹。 然后在该文件夹内添加一个usercontroller.ts文件,其中包含以下内容:

import { Router } from 'https://deno.land/x/opine@0.12.0/mod.ts';

const users = new Router();

// users routes
users.get('/me', (req, res) => {
  res.render('users/me', { title: 'My Profile', user: res.app.locals.user });
});

export default users;

This is a simple routing file. It gets the router from Opine and creates a new instance to hang routes from. Then there is code to add a route for /me to render the HTML view in users/me. The render() call also passes a title and the logged-in user to the page. This page will be protected so that there will always be a user to pass to the page.

这是一个简单的路由文件。 它从Opine获取路由器,并创建一个新实例来挂起路由。 然后有代码为/me添加路由以在users/me呈现HTML视图。 render()调用还将标题和登录用户传递到页面。 该页面将受到保护,以便始终有用户可以访问该页面。

Next, create some views to show when the routes are hit. In the root folder, add a views folder. Inside that, create a shared folder and a users folder. In the shared folder create a header.html and footer.html file. In the users folder add a me.html file. Finally, in the views folder itself create an index.html file.

接下来,创建一些视图以显示何时点击路线。 在根文件夹中,添加一个views文件夹。 在其中创建一个shared文件夹和一个users文件夹。 在shared文件夹中,创建一个header.htmlfooter.html文件。 在users文件夹中添加一个me.html文件。 最后,在views文件夹本身中创建一个index.html文件。

These are pretty bare-bones, but it demonstrates how to create views that can be reused by other views. In the shared/header.html file add the following:

这些是非常简单的方法,但是它演示了如何创建可被其他视图重用的视图。 在shared/header.html文件中添加以下内容:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title><%= title %></title>
</head>

<body>

This outputs the top of an HTML page and injects the title into the page. Next, add the following to the shared/footer.html file:

这将输出HTML页面的顶部,并将标题注入页面。 接下来,将以下内容添加到shared/footer.html文件:

</body>

</html>

Now you can use those partials in the index.html file:

现在,您可以在index.html文件中使用这些局部变量:

<%- await include('views/shared/header.html', { title }); %><a href="/users/me">My Profile</a>

<%- await include('views/shared/footer.html'); %>

This includes the footer and header partials and adds a link to the profile page. The contents of the users/me.html file are:

这包括页脚和页眉的部分内容,并向个人资料页面添加了链接。 users/me.html文件的内容为:

<%- await include('views/shared/header.html', { title }); %><h1>My Profile</h1>

<ul>
<% for(var p in user){ %>
  <li><strong><%= p %>: </strong><%= user[p] %></li>
<% } %>
</ul>

<%- await include('views/shared/footer.html'); %>

Again, this page includes the header and footer, and loops through the properties of the user object. Granted, it's not a super-sexy profile page, but it will let you know that the authentication steps all worked.

同样,此页面包含页眉和页脚,并循环遍历user对象的属性。 当然,这不是一个超级性感的个人资料页面,但是它将使您知道身份验证步骤全部有效。

使用Okta添加身份验证 ( Add Authentication with Okta )

If you don't already have an Okta account, you can get a free developer account here. Once you've signed into Okta, you'll land on the dashboard. You'll need to create an Okta application to take advantage of Okta as an Identity Provider for your project.

如果您还没有Okta帐户,则可以在此处获得免费的开发者帐户。 登录Okta后,您将进入仪表板。 您需要创建一个Okta应用程序,以利用Okta作为项目的身份提供者。

Click on Applications in the menu, then Add Application. This will take you to the application wizard. Choose Web for your platform, then click Next. The next page is the Application Settings page. Give your application a name (I named mine DenoExample). Change all the URLs to use port 3000 instead of 8080, then change the Login Redirect URIs to http://localhost:3000/auth/callback. This is a route you'll be implementing shortly. Finally, click Done to finish creating the application in Okta.

点击菜单中的应用程序 ,然后添加应用程序 。 这将带您进入应用程序向导。 为您的平台选择Web ,然后单击Next 。 下一页是“ 应用程序设置”页面。 为您的应用程序命名(我命名为DenoExample)。 将所有URL更改为使用端口3000而不是8080 ,然后将“ 登录重定向URI”更改为http://localhost:3000/auth/callback 。 这是您即将实施的一条路线。 最后,单击“完成”以在Okta中创建应用程序。

Once you're on the page for your newly-created application, make sure you're on the General Settings tab and scroll to the bottom until you see a Client Credentials section. You'll be using these values momentarily, so keep this window open.

进入新创建的应用程序页面后,请确保位于“ 常规设置”选项卡上并滚动到底部,直到看到“ 客户端凭据”部分。 您将暂时使用这些值,因此请保持此窗口打开。

Back in your application, create a new file in the root of the application called .env. The contents of the file will be:

回到您的应用程序中,在应用程序的根目录中创建一个名为.env的新文件。 该文件的内容将是:

issuer=https://{yourOktaOrgUrl}/oauth2/default
clientId={yourClientID}
clientSecret={yourClientSecret}
redirectUrl=http://localhost:3000/auth/callback
state=SuPeR-lOnG-sEcReT

Copy the Client ID and Client Secret from the Client Credentials section of your Okta application. Then go back to the dashboard and copy your Okta org URL from the right-hand side just below the menu.

从Okta应用程序的“ 客户端凭据”部分复制“客户端ID和客户端密钥”。 然后返回到信息中心,然后从菜单下方的右侧复制您的Okta组织网址。

Now you're ready to start talking to Okta for authentication. Unfortunately, I couldn't find any OpenID Connect (OIDC) libraries to make authentication with OAuth 2.0 and OIDC easier than this, so you'll have to create it by hand. However, this can be an awesome exercise to help understand how OAuth and OIDC work. In the root folder of your application, create a new folder called middleware and add a file called authmiddleware.ts. Then add this content:

现在您可以开始与Okta进行身份验证了。 不幸的是,我找不到任何OpenID Connect(OIDC)库使使用OAuth 2.0和OIDC进行身份验证比这更容易,因此您必须手动创建它。 但是,这可能是一个很棒的练习,可以帮助您了解OAuth和OIDC的工作方式。 在应用程序的根文件夹中,创建一个名为middleware的新文件夹,并添加一个名为authmiddleware.ts的文件。 然后添加以下内容:

import { config } from 'https://deno.land/x/dotenv/mod.ts';

export const ensureAuthenticated = async (req:any, res:any, next:any) => {
  const user = req.app.locals.user;
  if(!user){
    const reqUrl = req.originalUrl;
    const {issuer, clientId, redirectUrl, state} = config();
    const authUrl = `${issuer}/v1/authorize?client_id=${clientId}&response_type=code&scope=openid%20email%20profile&redirect_uri=${encodeURIComponent(redirectUrl)}&state=${state}:${reqUrl}`;
    res.location(authUrl).sendStatus(302);
  }
  next();
}

First, bring a library for reading the .env file. The dotenv does this beautifully. Then you'll implement the ensureAuthenticated() middleware that starts the first step of the authentication process. First, it checks to make sure the user isn't already logged in. If they are, it just calls next() because there is nothing to do.

首先,带来一个用于读取.env文件的库。 dotenv得很漂亮。 然后,您将实现ensureAuthenticated()中间件,该中间件开始身份验证过程的第一步。 首先,它检查以确保用户尚未登录。如果已经登录,则它只调用next()因为没有任何事可做。

If there isn't a currently logged in user, it builds a URL made up of the issuer, clientId, redirectUrl, and state properties from the .env file. It makes a call to the /v1/authorize endpoint of the issuer's URL. It then redirects to that URL. This is a login page hosted by Okta. Kind of like when you're redirected to Google to log in with Google as the identity provider. The URL that it will call when login is done is the http://localhost:3000/auth/callback URL that's in the .env file. I've also tagged the original URL that the user was going to when they were redirected to the state query parameter. This will make it easy to direct them back there once they've logged in.

如果没有当前登录的用户,它将构建一个URL,该URL由.env文件中的.env ,clientId,redirectUrl和state属性组成。 它调用发行者URL的/v1/authorize端点。 然后,它重定向到该URL。 这是Okta托管的登录页面。 有点像当您重定向到Google以使用Google作为身份提供者登录时。 登录完成后将调用的URL是.env文件中的http://localhost:3000/auth/callback URL。 我还标记了用户重定向到state查询参数时要使用的原始URL。 一旦他们登录,这将使将他们直接引导回到那里变得容易。

Next, you'll need to implement the auth/callback route to handle the result from the login page and exchange the authorization code that you'll receive from Okta. Create a file called authcontroller.ts in the controllers folder with the contents:

接下来,您将需要实现auth/callback路由,以处理来自登录页面的结果,并交换将从Okta获得的授权代码。 在controllers文件夹中创建一个名为authcontroller.ts的文件,内容如下:

import { Router } from 'https://deno.land/x/opine@0.12.0/mod.ts';
import { config } from "https://deno.land/x/dotenv/mod.ts";

const auth = new Router();

// users routes
auth.get('/callback', async (req, res) => {
 const { issuer, clientId, clientSecret, redirectUrl, state } = config();

 if (req.query.state.split(':')[0] !== state) {
   res.send('State code does not match.').sendStatus(400);
 }

 const tokenUrl: string = `${issuer}/v1/token`;
 const code: string = req.query.code;

 const headers = new Headers();
 headers.append('Accept', 'application/json');
 headers.append('Authorization', `Basic ${btoa(clientId + ':' + clientSecret)}`);
 headers.append('Content-Type', 'application/x-www-form-urlencoded');

 const response = await fetch(tokenUrl, {
   method: 'POST',
   headers: headers,
   body: `grant_type=authorization_code&redirect_uri=${encodeURIComponent(redirectUrl)}&code=${code}`
 });

 const data = await response.json();
 if (response.status !== 200) {
   res.send(data);
 }
 const user = parseJwt(data.id_token);
 req.app.locals.user = user;
 req.app.locals.isAuthenticated = true;
 res.location(req.query.state.split(':')_[_1] || '/').sendStatus(302);
});


function parseJwt (token:string) {
  const base64Url = token.split('.')_[_1];
  const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
  const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
      return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
  }).join(''));

  return JSON.parse(jsonPayload);
};

export default auth;

There is actually a lot less going on here than you might think. First, the imports bring in the Router from Opine and read in the .env file again. Then they instantiate the router like in the usercontroller.ts file. The next thing I did was deconstruct the config object to make it easier to use the values. Next, I checked the state query parameter to make sure it matches. This helps ensure that Okta is the one who sent the authorization code. Then the authorization code gets pulled off the query string with req.query.code.

实际上,这里发生的事情比您想象的要少得多。 首先,导入从Opine引入Router ,然后再次读取.env文件。 然后,它们像在usercontroller.ts文件中一样实例化路由器。 我要做的下一件事是解构config对象,以使其更易于使用值。 接下来,我检查了state查询参数以确保其匹配。 这有助于确保Okta是发送授权码的人。 然后,使用req.query.code从查询字符串中提取授权码。

What happens next is a call to the token endpoint. You'll send the authorization code in a POST request to Okta to exchange for an ID Token. So, here I've built some headers for the request. The most important is the Authorization header that has a value of Basic {yourClientId}:{yourClientSecret} the client ID and secret are base64 encoded. Then the POST call is finally made to the token endpoint with those headers and a body with a grant_type of authorization_code—the same redirect URL as before—and the authorization code I just received from Okta.

接下来发生的是对令牌端点的调用。 您将在POST请求中将授权码发送给Okta,以交换ID令牌。 因此,这里我为请求构建了一些标题。 最重要的是Authorization标头,其值为Basic {yourClientId}:{yourClientSecret}客户端ID和密码是base64编码的。 然后POST调用终于与那些头令牌端点和机身采用了由grant_typeauthorization_code -the相同的重定向网址为之前和我刚刚从1563收到的授权码。

The fetch() call returns a promise resolved with the then() function. I get the response object's JSON value, make sure the call was successful, parse the id_token value with the parseJwt() function below and stick it into a local variable called user. Finally, the user is sent to the URL they originally requested before being redirected for authentication.

fetch()调用返回一个用then()函数解析的promise。 我获得了response对象的JSON值,确保调用成功,使用下面的parseJwt()函数解析id_token值,并将其粘贴到名为user的局部变量中。 最后,在重定向到身份验证之前,将用户发送到他们最初请求的URL。

运行Deno应用程序 ( Run the Deno Application )

You can now run the application from the terminal again with:

现在,您可以使用以下命令从终端再次运行该应用程序:

deno run -A index.ts

Once it's running you will be able to click on the profile link on the home page and will be redirected to Okta's hosted login page. Once you've logged in, you'll be directed back to the profile page and you'll see your ID token's properties displayed in a list

一旦运行,您将能够单击主页上的配置文件链接,并将其重定向到Okta的托管登录页面。 登录后,将直接回到个人资料页面,您会看到ID令牌的属性显示在列表中

了解有关Deno和Node.js的更多信息 ( Learn More About Deno and Node.js )

If you want to learn more about Deno and Node, check out the links below.

如果您想了解有关Deno和Node的更多信息,请查看下面的链接。

If you liked this post, follow us on Twitter and subscribe to our YouTube Channel so you never miss any of our awesome content!

如果您喜欢这篇文章,请在Twitter上关注我们并订阅我们的YouTube频道,以免您错过我们的任何精彩内容!

翻译自: https://scotch.io/tutorials/build-your-first-deno-app-with-authentication

deno使用rust

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值