使用REST功能编写AuthenticationFilter的最佳方法是什么?

时间:2016-08-28 19:20:44

标签: java spring spring-mvc spring-security servlet-filters

以下是我到目前为止所做的,但它并没有按照我的意愿行事。我想要的是415,如果内容类型不是json,400杰克逊不能反序列化或验证是错误的。目前当然这都是401s,我在反序列化方面做错了(将错误的类型传递给j​​son)。我想可能有一些方法可以利用Spring MVC在常规控制器下的功能。

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

1 个答案:

答案 0 :(得分: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参数的所有引用来调整代码以满足您的需求。