Связь между AngularJS и веб-сервисом Джерси, которые находятся в другом домене. Не удается получить доступ к правильному сеансу

В последнее время я играю с AngularJS и Java EE 6. Я создал веб-сервис на Джерси и развернул проект на 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 или с базовой веб-страницы jQuery, развернутой на glassfish, я получаю верное имя пользователя, и сеанс размещается. Если я затем отправляю запрос GET в /ping, я получаю имя пользователя, с которого я вошел.

У меня есть приложение AngularJS, развернутое на веб-сервере node.js, которое необходимо для входа. Потому что этот сервер находится на другом порту, а на другом домене, и мне пришлось пройти через боль включения мозгов. Я сделал это путем создания фильтра ответа контейнера, который устанавливает заголовки ответа.

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 в приложение Java EE 6, развернутое на glassfish.

Проблема в том, что когда я отправляю запрос POST от AngularJS методу /login, создается сеанс, и я получаю свое имя пользователя обратно. Но когда я отправляю запрос GET методу /ping, я получаю уведомление "сессия не установлена ​​с атрибутом имени пользователя".

Я считаю, что это связано с междоменной профилактикой и что мне нужно установить тег withCredentials при отправке запроса xhr. Я пытался сделать это в 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 это правда

$http.defaults.withCredentials = true;

Это, однако, не решило мою проблему. Я также пытался отправить его с каждым запросом в параметре config, но это тоже не решило мою проблему.

3 ответа

Решение

В зависимости от версии AngularJS, которую вы используете, вам может потребоваться установить ее для каждого $http.

С версии 1.2 вы можете сделать:

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

Начиная с версии 1.1.1 вы можете настроить его глобально:

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

Если вы используете более старую версию Angular, попробуйте передать объект конфигурации в $http, который указывает withCredentials. Это должно работать в версиях до 1.1:

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

См. Также ответ мрюеланов и:

Просто обновление @iwein anwser, которое мы теперь можем установить в самом конфиге

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

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

(доступно только после нестабильной версии: 1.1.1)

В версии 1.2 это не работает для меня:

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

если я прочитал документ, метод ярлыка должен взять объект конфигурации

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

$http - это одиночный файл. Это единственный способ смешивать запросы одного и того же приложения с учетными данными и без них.

Другие вопросы по тегам