AngularJS $http、CORS 和 Jersey 的基本身份验证

Posted

技术标签:

【中文标题】AngularJS $http、CORS 和 Jersey 的基本身份验证【英文标题】:AngularJS $http, CORS and Basic authentication with Jersey 【发布时间】:2019-01-07 09:20:56 【问题描述】:

我正在学习 AngularJS。我已经使用CORS 和基本身份验证创建了示例项目。我的服务器端代码是 Jersey Rest。但我仍然收到 403 禁止错误。我不明白。我是不是错过了什么。我的代码如下。请帮我解决。


服务器代码--

/*
 * This is the controller level Code to get all UserlogInHistoryAngular
 * information
 */
@GET
@Path("/getall")
@Produces(
  MediaType.APPLICATION_XML,
  MediaType.APPLICATION_JSON
)
public Response getAllUserLogInHistoryAngular() 
  log.info("Controller layer for Get All UserlogInHistory");
  UserLoginHistoryService userLogInHistoryService = new UserLoginHistoryService();

  GenericEntity < List < UserLogInHistory >> userLoginHistory = new GenericEntity < List < UserLogInHistory >> (
    userLogInHistoryService.getAllUserlogInHisotry()) ;

  return Response
    .status(200)
    .entity(userLoginHistory)
    .header("Access-Control-Allow-Credentials", true)
    .header("Access-Control-Allow-Origin", "*")
    .header("Access-Control-Allow-Methods",
      "GET, POST, DELETE, PUT, OPTIONS")
    .header("Access-Control-Allow-Headers",
      "Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CSRF-Token, Accept-Version, Content-Length, Content-MD5,  Date, X-Api-Version, X-File-Name")
    .allow("OPTIONS").build();    
    


AngularJS 代码--

var myapp = angular
  .module('myapp', ['angularUtils.directives.dirPagination']);

myapp.config(function($httpProvider) 
  //Enable cross domain calls
  $httpProvider.defaults.withCredentials = true;
  $httpProvider.defaults.useXDomain = true;
);


myapp.controller('customerController', function($scope, $http) 
      var encodingstring = window.btoa("numery" + ":" + "password");

      console.log(encodingstring);

      $http(
        withCredentials: true,
        headers: 
          'Authorization': 'Basic ' + encodingstring,
          'Content-Type': 'application/json; charset=utf-8'
        ,
        method: "GET",
        url: "http://localhost:8080/userloginhistoryapi/rest/secured/userloginhistory/getall"
      ).then(function(response) 
        $scope.lstUser = response.data;
        //$log.info(response);

        console.log(response.data);
        console.log($scope.lstUser);
      )

      $scope.sortColumn = "name";
      $scope.reverseSort = false;

      $scope.sortData = function(column) 
        $scope.reverseSort = ($scope.sortColumn == column) ? !$scope.reverseSort : false;
        $scope.sortColumn = column;
      ;

      $scope.getSortColumn = function(column) 

        if ($scope.sortColumn == column) 
          return $scope.reverseSort ? 'arrow-down' : 'arrow-up';
        
        return '';
      ;

      function getSelectedIndex(id) 
        for (var i = 0; i < $scope.listCustomers.length; i++)
          if ($scope.listCustomers[i].id == id)
            return i
        return -1;
      ; 

错误--

angular.js:13018 OPTIONS http://localhost:8080/userloginhistoryapi/rest/secured/userloginhistory/getall 403 (Forbidden)
(anonymous) @ angular.js:13018
sendReq @ angular.js:12744
serverRequest @ angular.js:12485
processQueue @ angular.js:17396
(anonymous) @ angular.js:17444
$digest @ angular.js:18557
$apply @ angular.js:18945
bootstrapApply @ angular.js:1939
invoke @ angular.js:5108
doBootstrap @ angular.js:1937
bootstrap @ angular.js:1957
angularInit @ angular.js:1842
(anonymous) @ angular.js:35431
trigger @ angular.js:3491
(index):1 Failed to load http://localhost:8080/userloginhistoryapi/rest/secured/userloginhistory/getall: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8081' is therefore not allowed access. The response had HTTP status code 403.
angular.js:15018 Possibly unhandled rejection: "data":null,"status":-1,"config":"method":"GET","transformRequest":[null],"transformResponse":[null],"jsonpCallbackParam":"callback","withCredentials":true,"headers":"Authorization":"Basic bnVtZXJ5OnBhc3N3b3Jk","Accept":"application/json, text/plain, */*","url":"http://localhost:8080/userloginhistoryapi/rest/secured/userloginhistory/getall","statusText":"","xhrStatus":"error"
(anonymous) @ angular.js:15018
(anonymous) @ angular.js:11302
processChecks @ angular.js:17428
$digest @ angular.js:18557
$apply @ angular.js:18945
done @ angular.js:12799
completeRequest @ angular.js:13056
requestError @ angular.js:12972
error (async)
(anonymous) @ angular.js:12985
sendReq @ angular.js:12744
serverRequest @ angular.js:12485
processQueue @ angular.js:17396
(anonymous) @ angular.js:17444
$digest @ angular.js:18557
$apply @ angular.js:18945
bootstrapApply @ angular.js:1939
invoke @ angular.js:5108
doBootstrap @ angular.js:1937
bootstrap @ angular.js:1957
angularInit @ angular.js:1842
(anonymous) @ angular.js:35431
trigger @ angular.js:3491
VM2032:185 [CodeLive] HTTP detected: Connecting using WS
VM2032:109 [CodeLive] Connected to CodeLive at ws://127.0.0.1:42529
bundle.js:10 license url https://www.genuitec.com/go/webclipse-buy-now

【问题讨论】:

您是否检查过任何 REST 客户端(如 Postman)中的服务是否可以正常使用凭据? 您的基本身份验证在哪里进行? 服务在 PostMan 和 SoapUI 中运行良好。我已经检查了两者。我使用 ContainerRequestFilter 进行基本身份验证 【参考方案1】:

所以这就是正在发生的事情。 CORS Preflight request 是一个 OPTIONS 请求。此请求在实际请求之前发生。它与服务器检查以查看请求是否被允许。为了响应预检请求,服务器应该像在 getAllUserLogInHistoryAngular 方法中一样发回 Access-Control-X-X 标头。

因此,在预检期间,您的情况发生了什么,即它命中了基本身份验证过滤器并在不添加 CORS 响应标头的情况下自动被拒绝。您将 CORS 响应标头放在资源方法中什么也没做,也没用。通常,CORS 应在过滤器中处理,以便在访问资源方法之前处理所有请求。

这是你应该做的。就像您对 Basic Auth 所做的一样,使用 ContainerRequestFilter 来处理 CORS。您希望在 Auth 过滤器之前调用此过滤器,因为 CORS 预检不关心身份验证,它不会随请求发送身份验证凭据。在此过滤器中,您应该检查它是否是 CORS 预检请求。通常这可以通过检查OPTIONS 方法和Origin 标头的存在来完成。如果是预检,则中止请求并在ContainerResponseFilter 中添加 CORS 标头。它可能看起来像

@Provider
@PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter 

    @Override
    public void filter(ContainerRequestContext request) throws IOException 
        if (isPreflightRequest(request)) 
            request.abortWith(Response.ok().build());
            return;
        
    

    private static boolean isPreflightRequest(ContainerRequestContext request) 
        return request.getHeaderString("Origin") != null
                && request.getMethod().equalsIgnoreCase("OPTIONS");
    

    @Override
    public void filter(ContainerRequestContext request, ContainerResponseContext response)
            throws IOException 
        if (request.getHeaderString("Origin") == null) 
            return;
        
        if (isPreflightRequest(request)) 
            response.getHeaders().add("Access-Control-Allow-Credentials", "true");
            response.getHeaders().add("Access-Control-Allow-Methods",
                "GET, POST, PUT, DELETE, OPTIONS, HEAD");
            response.getHeaders().add("Access-Control-Allow-Headers",
                "Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CSRF-Token, " +
                "Accept-Version, Content-Length, Content-MD5,  Date, X-Api-Version, X-File-Name");
        
        response.getHeaders().add("Access-Control-Allow-Origin", "*");
    

注意它是一个@PreMatching 过滤器,这将导致它在您的身份验证过滤器之前被调用(假设过滤器不是预匹配过滤器,在这种情况下您还应该使用@Priority 注释)。

这个过滤器的作用是检查请求是否是预检请求,如果是,它会用 200 中止整个请求,这会导致请求跳​​过资源方法,跳过它之后的所有其他请求过滤器, 并跳转到响应过滤器。在响应过滤器中,我们通过检查我们之前设置的属性来检查它是否是预检。如果是,那么我们设置 CORS 响应标头。如果您想了解有关 CORS 的更多信息以及有关此特定过滤器实现的更多信息,请查看 this post。

所以不,这里是会发生什么的流程

Client                    Browser                    Server
------                    -------                    ------

Request endpoint -------> Remembers request -------> Sends back CORS
                          But first sends            response headers
                          CORS preflight             from CorsFilter
                                                             |      
                          Grabs original    <----------------+
                          request, sends it
                                  |
                                  +----------------> Skips CorsFilter,
                                                     Goes to Auth filter,
                                                     Goes to resource
                                                     Sends resource response
                                                             |
Handle response  <------- Receives response <----------------+
                          Gives it to client

【讨论】:

以上是关于AngularJS $http、CORS 和 Jersey 的基本身份验证的主要内容,如果未能解决你的问题,请参考以下文章

在 angularjs 中使用 $http.post() 时出现 CORS 问题

使用 python flask-restful 和消费 AngularJS(使用 $http)时出现 CORS(跨源...)错误

使用 CORS 的 Javascript 和 angularjs

AngularJS 和 Laravel - CORS

当我使用 CORS 时,AngularJS $http 似乎没有正确响应

angularjs简单实现$http.post(CORS)跨域