2016-08-28 4 views
1

Ниже приведено то, что у меня есть до сих пор, но оно не делает то, что я хочу. Я хочу, чтобы 415, если тип содержимого не json, а 400 jackson не могут десериализоваться или если проверка неверна. В настоящее время, конечно, это все 401, и я делаю что-то неправильно с десериализацией (передавал неправильный тип json). Я думаю, что может быть какой-то способ использовать то, что Spring MVC будет делать под капотом для обычного контроллера.Каков наилучший способ создания AuthenticationFilter с функциями REST?

@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

Можете ли вы предоставить объект PasswordCredentials и формат сообщения json, который вы отправляете? – jlumietu

+1

Не пишите сами, найдите проверенные примеры. Хорошим источником будет JHipster для запуска (https://github.com/jhipster/jhipster-sample-app-token) – Vaelyr

+0

@jlumietu http://stackoverflow.com/q/39166509/206466 Я не думаю, что структура особенно релевантно, только изменение заключается в том, что я изменил значение 'toAuthentication' на' toAuthenticaitonToken' – xenoterracide

ответ

0

Я полагаю, вы используете аутентификацию JWT или, по крайней мере, какую-то аутентификацию на токенах, не так ли? Вам лучше использовать @RestController, который делегирует аутентификацию authenticationManager, чем работу с семантикой фильтра более низкого уровня.

Вот контроллер, который я использовал ранее, немного модифицированный, чтобы соответствовать вашему DTO. Вы заметите, что это выглядит как the suggested JHipster example:

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

Чтобы увидеть, как контроллеры Spring решительность, анализа и проверки параметров тела запроса JSON, вы должны смотреть на RequestResponseBodyMethodProcessor, в частности, его метод resolveArgument и реализовать что-то подобное в фильтре , Однако, поскольку ваш фильтр не является контроллером, вы должны адаптировать код в соответствии с вашими потребностями, удалив все ссылки на параметр MethodParameter.

+0

Я на самом деле не делаю токенов, просто аутентифицирую json, а затем разрешить весеннюю сессию (в этом случае я просто делаю файлы cookie с SESSION), и мой предыдущий вопрос показывает, что я пишу контроллер, но я узнал, что после этого не использовал весеннюю сессию. Это имеет много общего с обслуживанием статического контента, для которого требуется аутентификация, и чтобы его проще было просто позволить ему обслуживаться с СЕССИЕЙ, чем обертывать его «ajax». В основном, я просто обрабатываю форму входа в систему с помощью json вместо кодированного url. – xenoterracide

+0

Ну, даже в этом случае я думаю, что вам лучше с помощью подхода к контроллеру, у вас будет еще меньше кода. Но если вы все еще хотите пойти с фильтрами, я думаю, что ваш собственный подход лучше, чем пытаться использовать собственные компоненты Spring, которые применяются к контроллерам - меньше кода и, вероятно, немного лучше. Я обновлю свой ответ, также включу указатели о том, как использовать метод Spring. –

+0

Я имею в виду, мне может быть лучше с помощью подхода к контроллеру, но он не устанавливает сеанс должным образом, не уверен, что он ничего не делает. – xenoterracide