2013-08-18 13 views
14

Buduję aplikację internetową AngularJS, która używa RESTful API (Jersey).Jak poprawnie uwierzytelnić klienta AngularJS na serwerze

Po stronie serwera korzystam z Java Application Server (szczegółowo Glassfish 4).

Moja konfiguracja wygląda następująco:

  • angularjs webapp jest stosowane jako pojedynczy plik war do serwera aplikacji Java EE.
  • RESTfulAPI (i logika zaplecza) jest również wdrażana jako plik wojny na tym samym serwerze aplikacji Java EE.
  • Aplikacja webowa AngularJS wywołuje interfejs REST API, aby uzyskać potrzebne dane.
  • relaksującego API:
    • /api/public/* do publicznego nie ograniczonym dostępie (np/api/public/Users/rejestracja zarejestrować się jako nowy użytkownik). Każdy użytkownik może uzyskać dostęp do tego obszaru. Nie logowanie wymagane
    • /api/private/* o ograniczonym dostępie (np/api/private/konto/{id} aby pobrać jakieś konto konkretnych danych)
  • Prywatne zasoby są chronione przy użyciu Java EE koncepcjach bezpieczeństwa wewnętrznego (patrz niżej dla szczegółów 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> 

Ta konfiguracja działa dla mnie, ale tylko wtedy, gdy przywołuję URL ręcznie w przeglądarce. Jeśli nie jestem uwierzytelniony i adres zabezpieczonego obszaru, przeglądarka prosi o podanie nazwy użytkownika i hasła. Jeśli podane poświadczenia są ważne, od tej chwili mam dostęp do zastrzeżonego obszaru: Dobra.

Ale w jaki sposób mogę pracować z AngularJS? Do zalogowania się jest wywołanie funkcji POST API: /api/public/users/login, gdzie należy podać dane uwierzytelniające z formularza (nazwa użytkownika i hasło).

Klient kod strony jest:

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()); 
     }); 
    }; 
}); 

Klient Kod strona wydaje się działać. Mam również pewne trasy do zabezpieczenia zawartości po stronie klienta i tak dalej. Istnieje kilka postów, które opisują szczegółowo kod strony klienta. Więc ten "fragment" powinien wystarczyć.

kodu po stronie serwera jest:

@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(); 
    } 
} 

Ale to jednak nie „uwierzytelnienia” po stronie klienta do dalszych żądań. Kiedy wykonuję wywołanie API na ograniczonym obszarze po pomyślnym zalogowaniu, otrzymuję odpowiedź 403 FORBIDDEN od strony serwera. Zakładam więc, że robię coś złego. Czy masz jakieś pomysły na to, jak uwierzytelnić klienta na serwerze w przypadku kolejnych żądań?

Możliwym rozwiązaniem może być przejście na uwierzytelnianie oparte na FORMIE i po prostu wywołanie j_security_check z j_username i j_password, ale na razie chcę trzymać się BASIC-Authentication i przeprowadzić uwierzytelnianie ręcznie za pomocą RESTful API.

Ustawienie $httpProvider.defaults.withCredentials = true; jakoś nie wydaje się mieć żadnego efektu.

Aktualizacja:

Dzięki końcówce lossleader I rozwiązać problem. Usunąłem metodę login(), ponieważ chcę, aby serwer Java EE zajmował się uwierzytelnianiem.

Aby uzyskać dostęp do zabezpieczonych obszarów, aplikacja webowa AngularJS musi poprawnie ustawić nagłówek HTTP. W moim przypadku $http.defaults.headers.common['Authorization'] = 'Basic <username:pw>'; gdzie username:password musi być zakodowany w Base64.

Po prawidłowym ustawieniu nagłówków, nie pojawi się monit o podanie hasła, a aplikacja internetowa AngularJS może uzyskać dostęp do interfejsu API REST.

+1

Nie widzę, w jaki sposób request.login() spowoduje, że klient otrzyma nagłówki http auth (tak naprawdę po prostu wraca 200?), Podczas gdy request.authenticate() dość wyraźnie działa z odpowiedzią i może dlatego ustaw nagłówki. Chciałbym użyć uwierzytelnienia i pozwolić przeglądarce wykonać swoją pracę lub użyć formularzy. Jeśli naprawdę chcesz czegoś pomiędzy, myślę, że lepiej obejrzyj odpowiedź i zobacz, czy nawet nagłówki są ustawione. – lossleader

+1

Hej, spadochronie, dzięki za twoją odpowiedź. Oczywiście masz rację, nie ustawiłem nagłówków w prawo i to oczywiście był mój problem. Po twojej podpowiedzi mam uwierzytelnienie do pracy z uwierzytelnianiem opartym na BASIC i FORMULARZ. – zip

Odpowiedz

5

Dzięki końcówce stratyfikatora rozwiązałem problem. Usunąłem metodę login(), ponieważ chcę, aby serwer Java EE zajmował się uwierzytelnianiem.

Aby uzyskać dostęp do zabezpieczonych obszarów, aplikacja webowa AngularJS musi poprawnie ustawić nagłówek HTTP. W moim przypadku $ http.defaults.headers.common ['Authorization'] = 'Basic'; gdzie nazwa użytkownika: hasło musi być zakodowane w Base64.

Po prawidłowym ustawieniu nagłówków, nie pojawi się monit o podanie hasła, a aplikacja internetowa AngularJS może uzyskać dostęp do interfejsu API REST.

+1

Witam, zadałem nieco podobne pytanie jak w: http://stackoverflow.com/questions/24129848/how-to-integrate-angularjs-and-java-jaas- based-authentication - czy faktycznie używasz strony logowania z formularz dla mechanizmu logowania? Czy możesz opublikować fragment kodu, w którym faktycznie się logujesz, i do kolejnych wywołań interfejsu API zapełniasz nagłówek kodowaniem Base64? Używam AngularJS Resource for Restful API, które również będzie działać z twoim podejściem? Dzięki. – guilhebl

Powiązane problemy