Как правильно аутентифицировать клиента AngularJS на сервере

Я создаю веб-приложение AngularJS, в котором используется API RESTful (Джерси).

На стороне сервера я использую сервер приложений Java (подробно Glassfish 4).

Моя настройка выглядит следующим образом:

  • AngularJS webapp развертывается как один файл войны на сервере приложений Java EE.
  • RESTfulAPI (и бэкэнд-логика) также развертывается как военный файл на тот же сервер приложений Java EE.
  • Функция AngularJS webapp вызывает API REST для получения требуемых данных.
  • API RESTful:
    • /api/public/* для общедоступного неограниченного доступа (например,/api/public/users/signup для регистрации в качестве нового пользователя). Любой пользователь имеет доступ к этой области. Нет входа в систему
    • /api/private/* для ограниченного доступа (например,/api/private/account/{id} для получения определенных данных учетной записи)
  • Частные ресурсы защищены внутренними концепциями безопасности Java EE (см. ниже подробности для web.xml).

web.xml

<security-constraint>
    <web-resource-collection>
        <web-resource-name>PRIVATE REST API</web-resource-name>
        <url-pattern>/private/*</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
        <http-method>HEAD</http-method>
        <http-method>PUT</http-method>
        <http-method>OPTIONS</http-method>
        <http-method>TRACE</http-method>
        <http-method>DELETE</http-method>
    </web-resource-collection>
    <auth-constraint>
        <description>Have to be a USER</description>
        <role-name>USERS</role-name>
    </auth-constraint>
</security-constraint>
<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>userauth</realm-name>
</login-config>
<security-role>
    <description/>
    <role-name>USERS</role-name>
</security-role>

Эта настройка работает для меня, но только если я вызываю URL-адреса вручную в браузере. Если я не аутентифицирован и не адресую защищенную область, браузер запрашивает имя пользователя и пароль. Если предоставленные учетные данные действительны, я оттуда имею доступ к запрещенной зоне: Хорошо.

Но как мне получить работу с AngularJS? Для входа в систему есть вызов API POST: /api/public/users/login, где вы должны предоставить учетные данные из формы (имя пользователя и пароль).

Код на стороне клиента:

ctrls.controller('LoginController', function($scope, $http, $log, $location, UserService, RemoteServerHelperService) {
    $scope.loginDataWrong = undefined;
    $scope.login = function(credentials) {
    $http.post(RemoteServerHelperService.buildURL('/public/users/login'), credentials)
        .success(function (data, status) {
            UserService.setLoggedIn(credentials.email);
            $scope.loginDataWrong = undefined;
            $location.path('/app');
            $log.info("login attempt: successfull. User.loggedIn:" + UserService.isLoggedIn());
        }).error(function (data, status, headers, config) {
            UserService.invalidate();
            $scope.loginDataWrong = true;
            $log.info("login attempt: failed. User.loggedIn:" + UserService.isLoggedIn());
        });
    };
});

Клиентский код, похоже, работает. У меня также есть несколько путей для защиты контента на стороне клиента и т.д. Есть несколько сообщений, которые подробно описывают код на стороне клиента. Поэтому этого "фрагмента" должно быть достаточно.

Код на стороне сервера:

@POST
@Path("/login")
@Consumes(MediaType.APPLICATION_JSON)
public Response login(Credentials credentials, @Context HttpServletRequest request) {
    if (isAlreadyAuthenticated(request)) {
        try {
            request.logout();
        } catch (ServletException ex) {
            return Response.status(Status.CONFLICT).build();
        }
    }

    // not authenticated
    try {
        request.login(credentials.getEmail(), credentials.getPassword());
        return Response.ok().build();
    } catch (ServletException ex) {
        return Response.status(Status.CONFLICT).build();
    }
}

Но, однако, это не "аутентифицирует" клиентскую сторону для дальнейших запросов. Когда я делаю вызов API в ограниченной области после успешного входа в систему, я получаю ответ 403 FORBIDDEN со стороны сервера. Поэтому я предполагаю, что я делаю что-то неправильно. Есть ли у вас какие-либо идеи о том, как аутентифицировать клиента на сервере для получения дополнительных запросов?

Возможным решением может быть переход на аутентификацию на основе FORM и просто вызов j_security_check с j_username и j_password, но на данный момент я хочу придерживаться BASIC-аутентификации и выполнять проверку подлинности вручную через RESTful API.

Настройка $httpProvider.defaults.withCredentials = true; каким-то образом не имеет никакого эффекта.

Update:

Благодаря кончику потери, я решил проблему. Я удалил метод login(), потому что я хочу, чтобы Java EE-Server позаботился об аутентификации.

Для доступа к защищенным областям веб-браузер AngularJS должен правильно настроить HTTP-заголовок. В моем случае $http.defaults.headers.common['Authorization'] = 'Basic <username:pw>';, где username:password должен быть закодирован Base64.

После того, как я правильно установил заголовки, никакая подсказка пароля не отображается, и веб-приложение AngularJS может получить доступ к API REST.

Ответы

Ответ 1

Благодаря кончику потери, я решил проблему. Я удалил метод login(), потому что я хочу, чтобы Java EE-Server выполнял проверку подлинности.

Для доступа к защищенным областям веб-браузер AngularJS должен правильно настроить HTTP-заголовок. В моем случае $http.defaults.headers.common ['Authorization'] = 'Basic'; где username: пароль должен быть закодирован Base64.

После того, как я правильно установил заголовки, никакая подсказка пароля не отображается, и веб-приложение AngularJS может получить доступ к API REST.