AngularJS 和位于不同域的 Jersey Webservice 之间的通信。无法访问正确的会话

Posted

技术标签:

【中文标题】AngularJS 和位于不同域的 Jersey Webservice 之间的通信。无法访问正确的会话【英文标题】:Communication between AngularJS and a Jersey Webservice which are on a different domain. Can't access correct session 【发布时间】:2012-11-23 22:55:06 【问题描述】:

最近我一直在使用 AngularJS 和 Java EE 6。我使用 Jersey 构建了一个 Web 服务,并将该项目部署在 Glassfish 上。因为我需要某种身份验证,而 OAuth 实现或 JDBCRealm 似乎有点矫枉过正,所以我决定在用户​​成功登录的情况下创建一个会话。

@POST
@Path("/login")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response login(LoginDAO loginData, @Context HttpServletRequest req) 
    req.getSession().invalidate();
    loginData.setPassword(PasswordGenerator.hash(loginData.getPassword()));
    User foundUser = database.login(loginData);
    if(foundUser == null) 
        return Response.status(Status.CONFLICT).build();
    
    req.getSession(true).setAttribute("username", foundUser.getUsername());
    return Response.ok().build();


@GET
@Path("/ping")
public Response ping(@Context HttpServletRequest req) 
    if(req.getSession().getAttribute("username") == null) 
        return Response.ok("no session with an username attribute has been set").build();
    
    return Response.ok(req.getSession(true).getAttribute("username")).build();

这似乎工作正常,如果我从 Postman 或从部署在 glassfish 上的基本 jQuery 网页发布到 /login,我确实会得到正确的用户名并且已经放置了一个会话。如果我随后向 /ping 发送 GET 请求,我确实会取回我登录时使用的用户名。

我在需要登录的 node.js 网络服务器上部署了一个 AngularJS 应用程序。因为这个服务器在另一个端口上,它在另一个域上,我不得不经历启用 cors 的痛苦。我通过构建一个设置响应标头的容器响应过滤器来做到这一点。

public class CrossOriginResourceSharingFilter implements ContainerResponseFilter 
    @Override
    public ContainerResponse filter(ContainerRequest creq, ContainerResponse cresp) 
        cresp.getHttpHeaders().putSingle("Access-Control-Allow-Origin", "http://localhost:8000");
        cresp.getHttpHeaders().putSingle("Access-Control-Allow-Credentials", "true");
        cresp.getHttpHeaders().putSingle("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        cresp.getHttpHeaders().putSingle("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
        return cresp;
    

这确实让我可以将不同类型的 HTTP 请求从 AngularJS 发送到部署在 glassfish 上的 Java EE 6 应用程序。

问题是当我从 AngularJS 向 /login 方法发送一个 POST 请求时,会创建一个会话并且我确实会取回我的用户名。但是当我向 /ping 方法发送 GET 请求时,我收到“没有设置用户名属性的会话”通知。

我相信这与跨域预防有关,并且我必须在发送 xhr 请求时设置 withCredentials 标记。我一直在尝试在 AngularJS 中执行此操作,但还没有找到如何执行此操作。

function LoginCtrl($scope, $http) 
    $scope.login = function() 
        $http.post("glassfish:otherport/api/login", $scope.credentials).
            success(function(data) 
                console.log(data);
            ).
            error(function(data, error) 
                console.log(error);
            );
    ;
;

在另一个控制器中:

$scope.getUsername = function() 
    $http.get("glassfish:otherport/api/ping", ).
        success(function(data) 
            $scope.username = data;
        ).
        error(function() 
            $scope.username = "error";
        )
    

我尝试将 withCredentials 设置为 true

$http.defaults.withCredentials = true;

但这并没有解决我的问题。我还尝试将它与配置参数中的每个请求一起发送,但这也没有解决我的问题。

【问题讨论】:

【参考方案1】:

根据您使用的 AngularJS 版本,您可能需要在每个 $http 上设置它。

从 1.2 开始你可以这样做:

$http.get(url, withCredentials: true, ...)

从 1.1.1 开始你可以全局配置它:

config(['$httpProvider', function($httpProvider) 
  $httpProvider.defaults.withCredentials = true;
]).

如果您使用的是旧版本的 Angular,请尝试将配置对象传递给指定 withCredentials 的 $http。这应该适用于 1.1 之前的版本:

$http(withCredentials: true, ...).get(...)

另见 mruelans 的回答和:

https://github.com/angular/angular.js/pull/1209 http://docs.angularjs.org/api/ng.$http https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#section_5

【讨论】:

【参考方案2】:

只是对@iwein anwser 的更新,我们现在可以在配置本身中进行设置

config(['$httpProvider', function($httpProvider) 
  $httpProvider.defaults.withCredentials = true;
]).

https://github.com/angular/angular.js/pull/1209

(仅在不稳定版本:1.1.1后可用)

【讨论】:

非常有用的补充+1【参考方案3】:

在 1.2 版本中,这对我不起作用:

$http(withCredentials: true, ...).get(...) 

如果我阅读文档,快捷方法应该采用配置对象

$http.get(url, withCredentials: true, ...)

$http 是一个单例,这是混合使用和不使用凭据的相同应用程序请求的唯一方法。

【讨论】:

感谢您添加此内容 谢谢,他们一直在更改 API……

以上是关于AngularJS 和位于不同域的 Jersey Webservice 之间的通信。无法访问正确的会话的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS + Jersey RESTful 后端:身份验证和授权

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

从 angularJS 部分链接到具有不同域的外部 URL

启用从 AngularJS 到 Jersey 的 CORS 发布请求

AngularJS 对跨域资源执行 OPTIONS HTTP 请求

AngularJS 对跨域资源执行 OPTIONS HTTP 请求