2013-08-18 8 views
14

Ich baue eine AngularJS-Webanwendung, die eine RESTful-API (Jersey) verwendet.So authentifizieren Sie einen AngularJS-Client ordnungsgemäß an den Server

Auf der Serverseite benutze ich einen Java Application Server (im Detail Glassfish 4).

Mein Setup ist wie folgt:

  • AngularJS Webapp als eine einzelne WAR-Datei auf der Java EE Application Server bereitgestellt wird.
  • Die RESTfulAPI (und die Back-End-Logik) wird auch als WAR-Datei auf demselben Java EE-Anwendungsserver bereitgestellt.
  • Die AngularJS-Webanwendung ruft die REST-API auf, um die benötigten Daten zu erhalten.
  • Die RESTful API:
    • /api/public/* für Öffentlichkeit nicht eingeschränkten Zugang (z/api/public/Benutzer/Anmeldungen registrieren sich als neuer Benutzer). Jeder Benutzer darf auf diesen Bereich zugreifen. Kein Login
    • erforderlich
    • /api/private/* für eingeschränkten Zugriff (zB/api/private/Konto/{id} einige kontospezifischen Daten abzurufen)
  • Die privaten Ressourcen werden mit den Java EE interne Sicherheitskonzepte geschützt (siehe unten für web.xml Details).

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> 

Dieses Setup funktioniert für mich, aber nur, wenn ich die URLs manuell im Browser aufrufen. Wenn ich nicht authentifiziert bin und einen geschützten Bereich adressiere, fragt der Browser nach Benutzername und Passwort. Wenn die angegebenen Zugangsdaten gültig sind, habe ich von dort Zugriff auf den eingeschränkten Bereich: Gut.

Aber wie bekomme ich das mit AngularJS arbeiten? Um sich anzumelden, gibt es einen POST API-Aufruf: /api/public/users/login, wo Sie Anmeldeinformationen aus einem Formular (Benutzername und Passwort) angeben müssen.

Der Client-Code ist:

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

Die Client-Seite Code zu arbeiten scheint. Ich habe auch einige Routen eingerichtet, um Inhalte auf der Client-Seite zu sichern, und so weiter. Es gibt mehrere Posts, die den clientseitigen Code im Detail beschreiben. Also sollte dieses "Snippet" genug sein.

Der Server-Side-Code ist:

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

Aber dies jedoch "authentifiziert" nicht die Client-Seite für weitere Anfragen. Wenn ich nach erfolgreichem Login einen API-Aufruf in einem eingeschränkten Bereich mache, bekomme ich eine 403 FORBIDDEN Antwort von der Serverseite. Also nehme ich an, dass ich etwas falsch mache. Haben Sie irgendwelche Ideen, wie Sie den Client für weitere Anfragen beim Server authentifizieren können?

Eine mögliche Lösung könnte sein, auf FORM-basierte Authentifizierung zu wechseln und einfach j_security_check mit j_username und j_password aufzurufen, aber jetzt möchte ich bei der BASIC-Authentifizierung bleiben und die Authentifizierung manuell über die RESTful-API durchführen.

Die Einstellung $httpProvider.defaults.withCredentials = true; scheint irgendwie keine Wirkung zu haben.

Update:

Dank der Spitze lossleader löste ich das Problem. Ich habe die Methode login() entfernt, weil ich möchte, dass der Java EE-Server sich um die Authentifizierung kümmert.

Um auf geschützte Bereiche zugreifen zu können, muss die AngularJS-Webapp den HTTP-Header korrekt einstellen. In meinem Fall $http.defaults.headers.common['Authorization'] = 'Basic <username:pw>';, wo username:password Base64 codiert werden muss.

Nachdem ich die Header richtig eingestellt habe, wird keine Passworteingabeaufforderung angezeigt und die AngularJS Webapp kann auf die REST API zugreifen.

+1

Ich sehe nicht, wie die request.login() wird dazu führen, dass der Client http Auth-Header bekommen (wirklich nur Ihre 200 kommt zurück richtig?) Während request.authenticate() ganz klar funktioniert mit der Antwort und kann Setzen Sie deshalb Header. Ich würde entweder authentifizieren und den Browser seine Arbeit machen lassen oder Formulare verwenden. Wenn Sie wirklich etwas dazwischen haben wollen, sollten Sie besser auf die Antwort achten und sehen, ob überhaupt Header gesetzt werden. – lossleader

+1

Hey Lossleader, danke für deine Antwort. Natürlich hast du recht, ich habe die Header nicht richtig gesetzt und das war natürlich mein Problem. Nach Ihrem Hinweis habe ich die Authentifizierung erhalten, um mit der BASIC- und FORM-basierten Authentifizierung zu arbeiten. – zip

Antwort

5

Dank der Spitze der Lossleader habe ich das Problem gelöst. Ich habe die login() -Methode entfernt, weil ich möchte, dass der Java EE-Server sich um die Authentifizierung kümmert.

Um auf geschützte Bereiche zugreifen zu können, muss die AngularJS-Webapp den HTTP-Header korrekt einstellen. In meinem Fall $ http.defaults.headers.common ['Authorization'] = 'Basic'; wo Benutzername: Passwort muss Base64-codiert sein.

Nachdem ich die Header richtig eingestellt habe, wird keine Passworteingabeaufforderung angezeigt und die AngularJS Webapp kann auf die REST API zugreifen.

+1

Hallo, ich fragte eine ähnliche Frage wie in: http://stackoverflow.com/questions/24129848/how-to-integrate-angularjs-and-java-jaas-based-authentication - verwenden Sie tatsächlich eine Login-Seite mit Formular für den Login-Mechanismus? Können Sie den Codeabschnitt veröffentlichen, an dem Sie sich tatsächlich anmelden, und für nachfolgende Aufrufe an die API die Kopfzeile mit Base 64-Codierung füllen? Ich verwende AngularJS Resource for Restful API, das funktioniert auch mit Ihrem Ansatz? Vielen Dank. – guilhebl

Verwandte Themen