最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Using Angular.js - how to serve binary data from a backend that requires authentication? - Stack Overflow

programmeradmin7浏览0评论

In my angularjs application I am municating with a backend server that requires basic access authentication via http header. I have implemented the authentication mechanism on the client side as described here.

angular.module('myAuthModule')
.config(['$httpProvider', '$stateProvider',
    function ($httpProvider, $stateProvider) {
        $httpProvider.interceptors.push('securityInterceptor');
    }])
.factory('securityInterceptor', ['$location', '$window', '$q',
    function ($location, $window, $q) {
        return {
            request: function (config) {
                config.headers = config.headers || {};
                if ($window.sessionStorage.token) {
                    config.headers['Auth-Key'] = $window.sessionStorage.token;
                }
                return config;
            },
            response: function (response) {
                if (response.status === 401 || response.status === 403) {
                    $location.path('/login');
                }
                return response || $q.when(response);
            }
        };
    }
]);

So far so good, handling xhr requests within the angular app works as expected.

The problem is that I need to provide a download link for pdf documents. My backend server has a /Document/Pdf/:id resource that serves a application/pdf response with ContentDisposition: attachment which also requires authentication. I understand that I cannot initiate a download using xhr, however both providing a link to the document download via ngHref and calling a function that does for example $window.open('/Document/Pdf/13') lead to a 401 Unauthorized response by the server.

What am I missing here?

In my angularjs application I am municating with a backend server that requires basic access authentication via http header. I have implemented the authentication mechanism on the client side as described here.

angular.module('myAuthModule')
.config(['$httpProvider', '$stateProvider',
    function ($httpProvider, $stateProvider) {
        $httpProvider.interceptors.push('securityInterceptor');
    }])
.factory('securityInterceptor', ['$location', '$window', '$q',
    function ($location, $window, $q) {
        return {
            request: function (config) {
                config.headers = config.headers || {};
                if ($window.sessionStorage.token) {
                    config.headers['Auth-Key'] = $window.sessionStorage.token;
                }
                return config;
            },
            response: function (response) {
                if (response.status === 401 || response.status === 403) {
                    $location.path('/login');
                }
                return response || $q.when(response);
            }
        };
    }
]);

So far so good, handling xhr requests within the angular app works as expected.

The problem is that I need to provide a download link for pdf documents. My backend server has a /Document/Pdf/:id resource that serves a application/pdf response with ContentDisposition: attachment which also requires authentication. I understand that I cannot initiate a download using xhr, however both providing a link to the document download via ngHref and calling a function that does for example $window.open('/Document/Pdf/13') lead to a 401 Unauthorized response by the server.

What am I missing here?

Share Improve this question edited Feb 10, 2014 at 7:47 jhnwsk asked Feb 7, 2014 at 16:28 jhnwskjhnwsk 95312 silver badges15 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

Having explored the possibilities given by @Geoff Genz with the addition of a fourth - data-uri option, which unfortunately does not allow defining filenames - I decided to go for a different approach.

I added a method to the API which generates a one-time download link based on a normally authenticated request and download it straight away. The angular handler bees very simple

.factory('fileFactory', ['$http', '$window',
    function ($http, $window) {
        return {
            downloadFile: function (fileId) {
                return $http(
                    {
                        method: "POST",
                        data: fileId,
                        url: '/api/Files/RequestDownloadLink',
                        cache: false
                    }).success(function (response) {
                        var url = '/api/File/' + response.downloadId;
                        $window.location = url;
                    });
            }
        };
    }]);

This is not perfect but I feel is least hack-ish. Also this works for me because I have full control of the front- and back-end.

There is not a simple solution to this. You've already discovered that you cannot download via Ajax, so you can't set a custom header that way. Nor can you set a custom header on a browser generated GET (like an href) or POST (like a form submit). I can suggest three different approaches, all of which will require some modifications on your server:

(1) Use Basic or Digest auth on your web page, so the browser will generate and send the Authorization header with those credentials.

(2) Set the token in "authorization" cookie that will be passed with the request and validate the token server side.

(3) Finally, the way we've implemented this is to use a POST request instead of a GET for the download. We POST to a hidden IFrame on the same page and have the server set the appropriate Content-Disposition header such as "attachment; filename="blah.pdf"" on the response. We then send the authorization token as a hidden field in the form.

None of these are ideal, and I know our solution feels kind of hacky, but I've not seen any more elegant approaches.

发布评论

评论列表(0)

  1. 暂无评论