2016-08-28 1 views
1

Unten ist was ich bisher habe aber es tut nicht was ich will. Was ich will, ist eine 415, wenn der Inhaltstyp nicht json ist, und eine 400 jackson kann nicht deserialisieren oder wenn die Validierung falsch ist. Momentan sind das alles 401s und ich mache etwas falsches mit der Deserialisierung (hat den falschen Typ an json übergeben). Ich denke, es könnte eine Möglichkeit geben, das zu nutzen, was Spring MVC unter der Haube für einen normalen Controller tun würde.Was ist der beste Weg, um einen AuthenticationFilter mit REST-Funktionen zu schreiben?

@Component 
public class JsonAuthenticationFilter extends AbstractAuthenticationProcessingFilter { 

    private final ObjectMapper objectMapper; 
    private final Validator validator; 

    protected JsonAuthenticationFilter(final ObjectMapper objectMapper, final Validator validator) { 
     super(new AntPathRequestMatcher("/authentication/password", "POST")); 
     this.objectMapper = objectMapper; 
     this.validator = validator; 
    } 

    @Override 
    public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response) 
     throws AuthenticationException, IOException, ServletException { 

     if (request.getContentType() == null 
      || !MediaType.APPLICATION_JSON.isCompatibleWith(MediaType.parseMediaType(request.getContentType()))) { 
      response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); 
      throw new AuthenticationServiceException(
       "Media Type not supported: " + request.getContentType()); 
     } 

     PasswordCredentials credentials = objectMapper.readValue(request.getReader(), PasswordCredentials.class); 
     DataBinder dataBinder = new DataBinder(credentials); 
     dataBinder.setValidator(validator); 
     dataBinder.validate(); 

     AbstractAuthenticationToken authRequest = credentials.toAuthenticationToken(); 

     setDetails(request, authRequest); 

     return this.getAuthenticationManager().authenticate(authRequest); 
    } 

    /** 
    * Provided so that subclasses may configure what is put into the authentication 
    * request's details property. 
    * 
    * @param request  that an authentication request is being created for 
    * @param authRequest the authentication request object that should have its details 
    *     set 
    */ 
    protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) { 
     authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); 
    } 

    @Override 
    @Autowired 
    public void setAuthenticationManager(final AuthenticationManager authenticationManager) { 
     super.setAuthenticationManager(authenticationManager); 
    } 
} 
+0

Können Sie das PasswordCredentials-Objekt und ein JSON-Nachrichtenformat, das Sie senden, bereitstellen? – jlumietu

+1

Schreiben Sie nicht selbst, suchen Sie nach bewährten Beispielen. Gute Quelle wäre JHipster für Start (https://github.com/jhipster/jhipster-sample-app-token) – Vaelyr

+0

@jlumietu http://Stackoverflow.com/q/39166509/206466 Ich glaube nicht, dass die Struktur ist Besonders relevant, nur Änderung ist, dass ich 'toAuthentication' zu' toAuthentaitonitonToken' ändere – xenoterracide

Antwort

0

Ich vermute, Sie verwenden JWT-Authentifizierung oder zumindest eine Art Token-basierte Authentifizierung, oder? Es ist besser, wenn Sie eine @RestController verwenden, die die Authentifizierung an die delegiert, als wenn Sie mit Filter-Semantik auf niedrigerer Ebene arbeiten.

Hier ist ein Controller, den ich zuvor verwendet habe, ein bisschen modifiziert, um mit Ihrem DTO zu entsprechen. Sie werden feststellen, es viel wie the suggested JHipster example aussieht:

@RestController 
@RequestMapping(value = "/api") 
public class LoginController { 

    private final Logger logger = LoggerFactory.getLogger(getClass()); 

    private final AuthenticationManager authenticationManager; 
    private final TokenAuthenticationService tokenAuthenticationService; 

    public LoginController(
     final AuthenticationManager authenticationManager, 
     final TokenAuthenticationService tokenAuthenticationService) { 
     this.authenticationManager = authenticationManager; 
     this.tokenAuthenticationService = tokenAuthenticationService; 
    } 

    @RequestMapping(
     value = "/login", 
     method = RequestMethod.POST, 
     consumes = MediaType.APPLICATION_JSON_VALUE) // will cause 415 errors 
    public ResponseEntity<String> authenticate(
     @Valid @RequestBody PasswordCredentials request) { // validated implicitly 
     try { 
      final User principal = doAuthenticate(request); 
      return ResponseEntity.ok(tokenFor(principal)); 
     } catch (Exception e) { 
      logger.error("Error authenticating user", e); 
      return new ResponseEntity<>(HttpStatus.FORBIDDEN); 
     } 
    } 

    private String tokenFor(final User principal) { 
     return tokenAuthenticationService.createTokenForUser(principal); 
    } 

    private User doAuthenticate(PasswordCredentials request) { 
     final Authentication authentication = request.toAuthenticationToken(); 
     final Authentication authenticated = authenticationManager.authenticate(authentication); 
     return (User) authenticated.getPrincipal(); 
    } 
} 

Um zu sehen, wie Spring Controller lösen, analysieren und JSON Anfrage Körperparameter validieren, sollten Sie bei RequestResponseBodyMethodProcessor aussehen, seine speziell resolveArgument Methode und implementieren etwas ähnliches in Ihrem Filter . Da Ihr Filter jedoch kein Controller ist, sollten Sie den Code an Ihre Anforderungen anpassen, indem Sie alle Verweise auf den Parameter MethodParameter entfernen.

+0

Ich mache eigentlich keine Tokens, nur authentifizieren über json, und dann lassen Frühling Sitzung übernehmen (in diesem Fall mache ich nur Kekse mit SESSION), und meine vorherige Frage, zeigt, dass ich einen Controller schrieb, aber ich fand heraus, es war nicht Frühling Sitzung nach. Dies hat viel damit zu tun, dass statischer Inhalt bereitgestellt wird, der zur Anzeige authentifiziert werden muss, und dass es einfacher ist, ihn einfach mit einer SESSION bedienen zu lassen, als ihn mit "ajax" zu umhüllen. Grundsätzlich verarbeite ich nur das Login-Formular mit JSON statt URL-codiert. – xenoterracide

+0

Nun, selbst in diesem Fall sind Sie besser mit einem Controller-Ansatz, Sie hätten noch weniger Code. Aber wenn Sie weiterhin mit Filtern arbeiten möchten, ist Ihre eigene Herangehensweise besser, als zu versuchen, die eigenen Komponenten von Spring zu verwenden, die auf Controller angewendet werden - weniger Code und wahrscheinlich auch eine etwas bessere Leistung. Ich werde meine Antwort aktualisieren, um auch Hinweise darauf zu geben, wie man Spring verwenden kann. –

+0

Ich meine, ich könnte mit der Controller-Ansatz besser dran sein, aber es nicht die Sitzung geeignet einstellen, nicht sicher, ob es nichts anderes tut – xenoterracide

Verwandte Themen