顾 静

顾 静

1659351900

使用令牌认证和 Javascript 下载文件

最近在我的一个项目中,我遇到了一个问题:我需要允许我们的用户从我们的后端下载文件的 ZIP 存档,基于复杂的授权规则,遵循四个步骤:

  1. 前端将使用要下载的文件名列表调用后端。
  2. 后端将使用他们的 JWT 令牌对用户进行身份验证。
  3. 然后它将检查是否允许用户检索这些文件。
  4. 最后,它会将档案发回给他们。

到目前为止,我们一直依赖“通过默默无闻的安全性”,这意味着我们信任唯一生成的文件名,以防止用户窥探并下载他们不应该检索的文件。

但这还不够:我们如何撤销用户对文件的权限?我们如何确定用户不会访问他们不应该访问的文件?我们需要向路由添加身份验证。

未经证实的案件

首先,让我向您展示我们迄今为止使用的代码。在这种情况下,后端是 Spring Boot 微服务,前端是 Angular SPA。

以下是未经身份验证案例的简化版本。它的作用是定义一个/files/download以一组文件 ID 作为参数的 GET 路由,然后检索、压缩文件并将其发送回客户端。

@GetMapping("/files/download")
public ResponseEntity<ByteArrayResource> download(
    @RequestParam Set<UUID> fileIds
) {
    ByteArrayResource zippedFiles = new ByteArrayResource(
        fileService.getZippedFiles(fileIds)
    );

    return ResponseEntity
        .ok()
        .contentLength(zippedFiles.contentLength())
        .header(HttpHeaders.CONTENT_TYPE, "application/zip")
        .header(
            HttpHeaders.CONTENT_DISPOSITION,
            ContentDisposition
                .builder("attachment")
                .filename("archive.zip")
                .build()
                .toString()
        )
        .body(zippedFiles)
    ;
}

所以现在,如果您向 发出 GET 请求/files/download?fileIds=UUID,您将收到名称为UUIDzipped 的文件。这允许您保持前端代码非常简单,因为指向后端路由的 URL的简单锚元素将足以触发客户端浏览器中的下载弹出窗口,这要归功于Content-Disposition 标头

所以你的前端组件可以检索所需的文件名,以某种方式连接它们,然后简单地显示一个到后端 URL 的链接,如下所示:

<a
  href="{{ backendUrl }}/files/download?fileIds={{ fileNames$ | async }}"
>Download your archive</a>

这一切都很好,但是当您这样做时,您无法转发 JWT 令牌,因此您无法验证您的用户:您需要以其他方式下载文件。

向路由添加身份验证

因此,首先,您需要添加某种身份验证和授权。尽管我的项目需要复杂的授权规则,但假设您只想确保用户在发送文件之前已登录。这只是将装饰器添加到后端路由的问题:

@GetMapping("/files/download")
@PreAuthorize("hasRole(T(fr.theodo.security.Roles).USER)")
public ResponseEntity<ByteArrayResource> download(…) {…}

这是简单的部分。现在,如果您尝试使用上述链接,您会收到一个错误:由于没有提供身份验证令牌,后端会以 401 Unauthorized 响应进行响应。我们如何解决这个问题?

使用 JavaScript 检索文件

下载文件将分两步完成:首先,您将使用 JavaScript 下载文件,允许您设置身份验证令牌,然后将文件“转发”给您的用户。

或者,请注意,您可以专门为此路由设置不同的身份验证方法,例如基于 cookie 的身份验证方法,但这意味着您的项目有两种不同的身份验证方法,其中一种由单个路由使用……所以我会反对它。

下载文件

假设您已经使用某种 API 客户端对后端执行了经过身份验证的调用,下载文件将很简单:您将在组件中实例化您的客户端,并调用您的后端(这里是用 完成的this.apiClient.downloadZipFile)。

RxJSswitchMap运算符允许您在this.project$更改时丢弃任何先前的调用;您可以在 Learn RxJS 上阅读有关它的更多信息。

export class MainPanelComponent {
  @Input() project$!: Observable<Project>;

  constructor(private apiClient: ApiClientService) {}

  private getZip(): Observable<string> {
    return this.project$.pipe(
      switchMap(project => this.apiClient.downloadZipFile(project.files)),
    );
  }
}

将文件“转发”给您的用户

现在您已经检索了文件,并作为 Observable by 返回switchMap,您需要在用户的计算机上触发保存。这只是在 RxJS 管道中链接两个新操作符的问题:

…
return this.project$.pipe(
  switchMap(project => this.apiClient.downloadZipFile(project.files)),
  map(data => window.URL.createObjectURL(data)),
  tap(url => {
    window.open(url, '_blank');
    window.URL.revokeObjectURL(url);
  })
);
…

首先,您map将检索到的数据发送到一个对象 URL(一个简单的string)。MDN将更详细地解释它们,但您需要了解的主要内容是它们是一种创建指向浏览器内存的本地 URL的方法……而且 Internet Explorer 不支持它们,因此您可能希望将其保留在介意取决于您的目标受众。

最后,您tap进入这个新的 Observable 以将文件公开给您的用户(只需使用此本地 URL 打开一个新窗口即可)并通过删除对象 URL 来清理您自己(这允许浏览器在文件释放后释放一些内存)已下载)。

触发下载

您可能已经注意到,现在您有一个getZip方法,但您没有调用它。为了解决这个问题,您只需要在组件中添加两行新代码,在其模板中添加一行:

export class MainPanelComponent {
  @Input() project$!: Observable<Project>;

  zipDownloadTrigger = new EventEmitter<void>();

  constructor(private apiClient: ApiClientService) {
    this.zipDownloadTrigger.pipe(exhaustMap(this.getZip)).subscribe();
  }

  …
}
<button (click)="zipDownloadEmitter.emit()">Download your archive</button>

这定义了一个事件发射器,它在每次单击按钮后发射。您将这个发射器与一个exhaustMapRxJS 操作符链接,该操作符将调用该getZip方法,并等待它完成,然后再继续监听事件发射器(因此是“耗尽”;再次,请参阅学习 RxJS了解更多详细信息)。

这意味着当组件下载文件时,按钮上的所有点击都将被忽略。请注意,您可以不这样做,在这种情况下,您可以简单地将getZip方法作为按钮上的单击回调传递。

请注意,为了简单起见,我在这里使用了一个按钮,但就可访问性而言,这是一种不好的做法:您应该始终使用下载链接来下载文件。在这里,您可以有一个按钮来触发下载,然后显示带有本地 URL 作为其href属性的链接。

UX 细节和最终代码

如果你把所有这些放在一起并添加一个简单的加载切换,你会得到以下代码:

export class MainPanelComponent {
  @Input() project$!: Observable<Project>;

  isTheArchiveLoading: boolean = false;
  zipDownloadTrigger = new EventEmitter<void>();

  constructor(private apiClient: ApiClientService) {
    this.zipDownloadTrigger.pipe(exhaustMap(this.getZip)).subscribe();
  }

  private getZip(): Observable<string> {
    this.isTheArchiveLoading = true;

    return this.project$.pipe(
      switchMap(project =>
        this.apiClient
          .downloadZipFile(project.files)
          .pipe(finalize(() => { this.isTheArchiveLoading = false; }))
      ),
      map(data => window.URL.createObjectURL(data)),
      tap(url => {
        window.open(url, '_blank');
        window.URL.revokeObjectURL(url);
      })
    );
  }
}

isTheArchiveLoading这确保了如果为真,您可以向用户显示加载器。finalize管道中的操作符在解析downloadZipFile时被调用downloadZipFile,很像Promise.finally()方法。

在这里,您有了一个功能齐全的下载方法,带有经过身份验证的路由,从您的前端调用!

来源:https ://blog.theodo.com/2021/03/authenticated-file-download-with-javascript/

#javascript 

What is GEEK

Buddha Community

使用令牌认证和 Javascript 下载文件

Rahul Jangid

1622207074

What is JavaScript - Stackfindover - Blog

Who invented JavaScript, how it works, as we have given information about Programming language in our previous article ( What is PHP ), but today we will talk about what is JavaScript, why JavaScript is used The Answers to all such questions and much other information about JavaScript, you are going to get here today. Hope this information will work for you.

Who invented JavaScript?

JavaScript language was invented by Brendan Eich in 1995. JavaScript is inspired by Java Programming Language. The first name of JavaScript was Mocha which was named by Marc Andreessen, Marc Andreessen is the founder of Netscape and in the same year Mocha was renamed LiveScript, and later in December 1995, it was renamed JavaScript which is still in trend.

What is JavaScript?

JavaScript is a client-side scripting language used with HTML (Hypertext Markup Language). JavaScript is an Interpreted / Oriented language called JS in programming language JavaScript code can be run on any normal web browser. To run the code of JavaScript, we have to enable JavaScript of Web Browser. But some web browsers already have JavaScript enabled.

Today almost all websites are using it as web technology, mind is that there is maximum scope in JavaScript in the coming time, so if you want to become a programmer, then you can be very beneficial to learn JavaScript.

JavaScript Hello World Program

In JavaScript, ‘document.write‘ is used to represent a string on a browser.

<script type="text/javascript">
	document.write("Hello World!");
</script>

How to comment JavaScript code?

  • For single line comment in JavaScript we have to use // (double slashes)
  • For multiple line comments we have to use / * – – * /
<script type="text/javascript">

//single line comment

/* document.write("Hello"); */

</script>

Advantages and Disadvantages of JavaScript

#javascript #javascript code #javascript hello world #what is javascript #who invented javascript

Hire Dedicated JavaScript Developers -Hire JavaScript Developers

It is said that a digital resource a business has must be interactive in nature, so the website or the business app should be interactive. How do you make the app interactive? With the use of JavaScript.

Does your business need an interactive website or app?

Hire Dedicated JavaScript Developer from WebClues Infotech as the developer we offer is highly skilled and expert in what they do. Our developers are collaborative in nature and work with complete transparency with the customers.

The technology used to develop the overall app by the developers from WebClues Infotech is at par with the latest available technology.

Get your business app with JavaScript

For more inquiry click here https://bit.ly/31eZyDZ

Book Free Interview: https://bit.ly/3dDShFg

#hire dedicated javascript developers #hire javascript developers #top javascript developers for hire #hire javascript developer #hire a freelancer for javascript developer #hire the best javascript developers

Niraj Kafle

1589255577

The essential JavaScript concepts that you should understand

As a JavaScript developer of any level, you need to understand its foundational concepts and some of the new ideas that help us developing code. In this article, we are going to review 16 basic concepts. So without further ado, let’s get to it.

#javascript-interview #javascript-development #javascript-fundamental #javascript #javascript-tips

Ajay Kapoor

1626321063

JS Development Company India | JavaScript Development Services

PixelCrayons: Our JavaScript web development service offers you a feature-packed & dynamic web application that effectively caters to your business challenges and provide you the best RoI. Our JavaScript web development company works on all major frameworks & libraries like Angular, React, Nodejs, Vue.js, to name a few.

With 15+ years of domain expertise, we have successfully delivered 13800+ projects and have successfully garnered 6800+ happy customers with 97%+ client retention rate.

Looking for professional JavaScript web app development services? We provide custom JavaScript development services applying latest version frameworks and libraries to propel businesses to the next level. Our well-defined and manageable JS development processes are balanced between cost, time and quality along with clear communication.

Our JavaScript development companies offers you strict NDA, 100% money back guarantee and agile/DevOps approach.

#javascript development company #javascript development services #javascript web development #javascript development #javascript web development services #javascript web development company

Nat  Grady

Nat Grady

1670062320

How to Use Zapier with MongoDB

I’m a huge fan of automation when the scenario allows for it. Maybe you need to keep track of guest information when they RSVP to your event, or maybe you need to monitor and react to feeds of data. These are two of many possible scenarios where you probably wouldn’t want to do things manually.

There are quite a few tools that are designed to automate your life. Some of the popular tools include IFTTT, Zapier, and Automate. The idea behind these services is that given a trigger, you can do a series of events.

In this tutorial, we’re going to see how to collect Twitter data with Zapier, store it in MongoDB using a Realm webhook function, and then run aggregations on it using the MongoDB query language (MQL).

The Requirements

There are a few requirements that must be met prior to starting this tutorial:

  • A paid tier of Zapier with access to premium automations
  • A properly configured MongoDB Atlas cluster
  • A Twitter account

There is a Zapier free tier, but because we plan to use webhooks, which are premium in Zapier, a paid account is necessary. To consume data from Twitter in Zapier, a Twitter account is necessary, even if we plan to consume data that isn’t related to our account. This data will be stored in MongoDB, so a cluster with properly configured IP access and user permissions is required.

You can get started with MongoDB Atlas by launching a free M0 cluster, no credit card required.

While not necessary to create a database and collection prior to use, we’ll be using a zapier database and a tweets collection throughout the scope of this tutorial.

Understanding the Twitter Data Model Within Zapier

Since the plan is to store tweets from Twitter within MongoDB and then create queries to make sense of it, we should probably get an understanding of the data prior to trying to work with it.

We’ll be using the “Search Mention” functionality within Zapier for Twitter. Essentially, it allows us to provide a Twitter query and trigger an automation when the data is found. More on that soon.

As a result, we’ll end up with the following raw data:

{
    "created_at": "Tue Feb 02 20:31:58 +0000 2021",
    "id": "1356701917603238000",
    "id_str": "1356701917603237888",
    "full_text": "In case anyone is interested in learning about how to work with streaming data using Node.js, I wrote a tutorial about it on the @MongoDB Developer Hub. https://t.co/Dxt80lD8xj #javascript",
    "truncated": false,
    "display_text_range": [0, 188],
    "metadata": {
        "iso_language_code": "en",
        "result_type": "recent"
    },
    "source": "<a href='https://about.twitter.com/products/tweetdeck' rel='nofollow'>TweetDeck</a>",
    "in_reply_to_status_id": null,
    "in_reply_to_status_id_str": null,
    "in_reply_to_user_id": null,
    "in_reply_to_user_id_str": null,
    "in_reply_to_screen_name": null,
    "user": {
        "id": "227546834",
        "id_str": "227546834",
        "name": "Nic Raboy",
        "screen_name": "nraboy",
        "location": "Tracy, CA",
        "description": "Advocate of modern web and mobile development technologies. I write tutorials and speak at events to make app development easier to understand. I work @MongoDB.",
        "url": "https://t.co/mRqzaKrmvm",
        "entities": {
            "url": {
                "urls": [
                    {
                        "url": "https://t.co/mRqzaKrmvm",
                        "expanded_url": "https://www.thepolyglotdeveloper.com",
                        "display_url": "thepolyglotdeveloper.com",
                        "indices": [0, 23]
                    }
                ]
            },
            "description": {
                "urls": ""
            }
        },
        "protected": false,
        "followers_count": 4599,
        "friends_count": 551,
        "listed_count": 265,
        "created_at": "Fri Dec 17 03:33:03 +0000 2010",
        "favourites_count": 4550,
        "verified": false
    },
    "lang": "en",
    "url": "https://twitter.com/227546834/status/1356701917603237888",
    "text": "In case anyone is interested in learning about how to work with streaming data using Node.js, I wrote a tutorial about it on the @MongoDB Developer Hub. https://t.co/Dxt80lD8xj #javascript"
}

The data we have access to is probably more than we need. However, it really depends on what you’re interested in. For this example, we’ll be storing the following within MongoDB:

{
    "created_at": "Tue Feb 02 20:31:58 +0000 2021",
    "user": {
        "screen_name": "nraboy",
        "location": "Tracy, CA",
        "followers_count": 4599,
        "friends_count": 551
    },
    "text": "In case anyone is interested in learning about how to work with streaming data using Node.js, I wrote a tutorial about it on the @MongoDB Developer Hub. https://t.co/Dxt80lD8xj #javascript"
}

Without getting too far ahead of ourselves, our analysis will be based off the followers_count and the location of the user. We want to be able to make sense of where our users are and give priority to users that meet a certain followers threshold.

Developing a Webhook Function for Storing Tweet Information with MongoDB Realm and JavaScript

Before we start connecting Zapier and MongoDB, we need to develop the middleware that will be responsible for receiving tweet data from Zapier.

Remember, you’ll need to have a properly configured MongoDB Atlas cluster.

We need to create a Realm application. Within the MongoDB Atlas dashboard, click the Realm tab.

MongoDB Realm Applications

For simplicity, we’re going to want to create a new application. Click the Create a New App button and proceed to fill in the information about your application.

From the Realm Dashboard, click the 3rd Party Services tab.

Realm Dashboard 3rd Party Services

We’re going to want to create an HTTP service. The name doesn’t matter, but it might make sense to name it Twitter based on what we’re planning to do.

Because we plan to work with tweet data, it makes sense to call our webhook function tweet, but the name doesn’t truly matter.

Realm Tweet Webhook

With the exception of the HTTP Method, the defaults are fine for this webhook. We want the method to be POST because we plan to create data with this particular webhook function. Make note of the Webhook URL because it will be used when we connect Zapier.

The next step is to open the Function Editor so we can add some logic behind this function. Add the following JavaScript code:

exports = function (payload, response) {

    const tweet = EJSON.parse(payload.body.text());

    const collection = context.services.get("mongodb-atlas").db("zapier").collection("tweets");

    return collection.insertOne(tweet);

};

In the above code, we are taking the request payload, getting a handle to the tweets collection within the zapier database, and then doing an insert operation to store the data in the payload.

There are a few things to note in the above code:

  1. We are not validating the data being sent in the request payload. In a realistic scenario, you’d probably want some kind of validation logic in place to be sure about what you’re storing.
  2. We are not authenticating the user sending the data. In this example, we’re trusting that only Zapier knows about our URL.
  3. We aren’t doing any error handling.

When we call our function, a new document should be created within MongoDB.

By default, the function will not deploy when saving. After saving, make sure to review and deploy the changes through the notification at the top of the browser window.

Creating a “Zap” in Zapier to Connect Twitter to MongoDB

So, we know the data we’ll be working with and we have a MongoDB Realm webhook function that is ready for receiving data. Now, we need to bring everything together with Zapier.

For clarity, new Twitter matches will be our trigger in Zapier, and the webhook function will be our event.

Within Zapier, choose to create a new “Zap,” which is an automation. The trigger needs to be a Search Mention in Twitter, which means that when a new Tweet is detected using a search query, our events happen.

Zapier Twitter Search Mention

For this example, we’re going to use the following Twitter search query:

url:developer.mongodb.com -filter:retweets filter:safe lang:en -from:mongodb -from:realm

The above query says that we are looking for tweets that include a URL to developer.mongodb.com. The URL doesn’t need to match exactly as long as the domain matches. The query also says that we aren’t interested in retweets. We only want original tweets, they have to be in English, and they have to be detected as safe for work.

In addition to the mentioned search criteria, we are also excluding tweets that originate from one of the MongoDB accounts.

In theory, the above search query could be used to see what people are saying about the MongoDB Developer Hub.

With the trigger in place, we need to identify the next stage of the automation pipeline. The next stage is taking the data from the trigger and sending it to our Realm webhook function.

Zapier to Realm Webhook

As the event, make sure to choose Webhooks by Zapier and specify a POST request. From here, you’ll be prompted to enter your Realm webhook URL and the method, which should be POST. Realm is expecting the payload to be JSON, so it is important to select JSON within Zapier.

We have the option to choose which data from the previous automation stage to pass to our webhook. Select the fields you’re interested in and save your automation.

The data I chose to send looks like this:

{
    "created_at": "Tue Feb 02 20:31:58 +0000 2021",
    "username": "nraboy",
    "location": "Tracy, CA",
    "follower_count": "4599",
    "following_count": "551",
    "message": "In case anyone is interested in learning about how to work with streaming data using Node.js, I wrote a tutorial about it on the @MongoDB Developer Hub. https://t.co/Dxt80lD8xj #javascript"
}

The fields do not match the original fields brought in by Twitter. It is because I chose to map them to what made sense for me.

When deploying the Zap, anytime a tweet is found that matches our query, it will be saved into our MongoDB cluster.

Analyzing the Twitter Data in MongoDB with an Aggregation Pipeline

With tweet data populating in MongoDB, it’s time to start querying it to make sense of it. In this fictional example, we want to know what people are saying about our Developer Hub and how popular these individuals are.

To do this, we’re going to want to make use of an aggregation pipeline within MongoDB.

Take the following, for example:

[
    {
        "$addFields": {
            "follower_count": {
                "$toInt": "$follower_count"
            },
            "following_count": {
                "$toInt": "$following_count"
            }
        }
    }, {
        "$match": {
            "follower_count": {
                "$gt": 1000
            }
        }
    }, {
        "$group": {
            "_id": {
                "location": "$location"
            },
            "location": {
                "$sum": 1
            }
        }
    }
]

There are three stages in the above aggregation pipeline.

We want to understand the follower data for the individual who made the tweet, but that data comes into MongoDB as a string rather than an integer. The first stage of the pipeline takes the follower_count and following_count fields and converts them from string to integer. In reality, we are using $addFields to create new fields, but because they have the same name as existing fields, the existing fields are replaced.

The next stage is where we want to identify people with more than 1,000 followers as a person of interest. While people with fewer followers might be saying great things, in this example, we don’t care.

After we’ve filtered out people by their follower count, we do a group based on their location. It might be valuable for us to know where in the world people are talking about MongoDB. We might want to know where our target audience exists.

The aggregation pipeline we chose to use can be executed with any of the MongoDB drivers, through the MongoDB Atlas dashboard, or through the CLI.

Conclusion

You just saw how to use Zapier with MongoDB to automate certain tasks and store the results as documents within the NoSQL database. In this example, we chose to store Twitter data that matched certain criteria, later to be analyzed with an aggregation pipeline. The automations and analysis options that you can do are quite limitless.

If you enjoyed this tutorial and want to get engaged with more content and like-minded developers, check out the MongoDB Community.

This content first appeared on MongoDB.

Original article source at: https://www.thepolyglotdeveloper.com/

#mongodb #zapier