如何在自定义Spring Security 3.0身份验证中正确处理异常?

时间:2014-03-19 19:48:13

标签: spring rest spring-mvc spring-security

我正在开发基于令牌的REST服务。当用户通过curl使用用户和密码转到../rest/authenticate时,获取有效令牌以使用整个API。

当用户忘记在其他方法中插入用户名,密码或令牌时出现我的问题,因为我没有设法按我的意愿处理身份验证例外。

我可以处理异常但tomcat会获得响应并插入一些我不期望的HTML。

curl

这是tomcat的典型响应。 tomcat

是否有可能收到200 OK这样没有此HTML代码的响应?

在妈妈那里,这是我的配置:

AuthenticationProcessingFilter

决定网址是否安全。如果必须保护,请调用身份验证管理器以验证它。如果收到身份验证异常,则调用AuthenticationEntryPoint

public class AuthenticationTokenProcessingFilter extends GenericFilterBean {

    private final Collection<String> nonTokenAuthUrls = Lists.newArrayList("/rest","/rest/authenticate");

    TokenAuthenticationManager tokenAuthenticationManager;
    RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    public AuthenticationTokenProcessingFilter(TokenAuthenticationManager tokenAuthenticationManager, RestAuthenticationEntryPoint restAuthenticationEntryPoint) {
        this.tokenAuthenticationManager = tokenAuthenticationManager;
        this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest)request;
        HttpServletResponse httpResponse = (HttpServletResponse)response;
        try{
            if(!nonTokenAuthUrls.contains(httpRequest.getRequestURI())){ //Auth by token
                 String hash = httpRequest.getHeader("token");
                 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(hash, null);
                 authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request));
                 SecurityContextHolder.getContext().setAuthentication(tokenAuthenticationManager.authenticate(authentication));
            }
            response.reset();
            chain.doFilter(request, response); 
        }catch(AuthenticationException authenticationException){
            SecurityContextHolder.clearContext();
            restAuthenticationEntryPoint.commence(httpRequest, httpResponse, authenticationException);
        }
    }

的AuthenticationManager

public class TokenAuthenticationManager implements AuthenticationManager{

    @Autowired
    UserService userService;

    @Autowired
    TokenService tokenService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Object hash = authentication.getPrincipal();
        if(hash == null)
            throw new BadCredentialsException("Token is required");
        User user = tokenService.getUserFromTokenHash((String)hash); 
        if(user == null)
            throw new BadCredentialsException("Non-existent token");
        if(!tokenService.validate((String)hash)) 
            throw new BadCredentialsException("Expired Token");
        org.springframework.security.core.userdetails.User userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getUserGrantedAuthorities(user.getRoles()));
        return new UsernamePasswordAuthenticationToken(userDetails, user.getPassword(), getUserGrantedAuthorities(user.getRoles()));
    }

的AuthenticationEntryPoint

此课程正常。收到的代码是未经授权的401,但消息是在tomcat html

公共类RestAuthenticationEntryPoint实现AuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
    response.setContentType("application/json");
    response.sendError( HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage() );
    response.getOutputStream().println("{ \"error\": \"" + authenticationException.getMessage() + "\" }");
}

}

也没有调用RestAccessDeniedHanler。这很困难,因为有很多课程需要实施。

我回顾了stackoverflow和其他网站中的一些帖子,我的方法是捕获AuthenticationProcessingFilter中的异常并手动调用AuthenticationEntryPoint。我决定这样做,因为我试图在applicationContext-security.xml中配置它,但没有成功。

appliacionContext-security.xml文件

<b:bean id="restAuthenticationEntryPoint" class="...web.security.RestAuthenticationEntryPoint" /> 

<b:bean id="tokenAuthenticationManager" class="...dp.web.security.TokenAuthenticationManager"/>

<b:bean id="AuthenticationTokenProcessingFilter" class="...web.security.AuthenticationTokenProcessingFilter">
    <b:constructor-arg type="...dp.web.security.TokenAuthenticationManager" ref="tokenAuthenticationManager"></b:constructor-arg>
    <b:constructor-arg type="...dp.web.security.RestAuthenticationEntryPoint" ref="restAuthenticationEntryPoint"></b:constructor-arg>
</b:bean>

<b:bean id="accessDeniedHandler" class="...dp.web.security.RestAccessDeniedHandler">
</b:bean>

<http realm="Protected REST API" pattern="/rest/**" use-expressions="true" auto-config="false" create-session="stateless"  entry-point-ref="restAuthenticationEntryPoint">
    <custom-filter ref="AuthenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
    <access-denied-handler ref="accessDeniedHandler"/>
</http> 

如何使用错误代码和消息发送干净的响应?

1 个答案:

答案 0 :(得分:2)

您可以在web.xml中使用错误页面拦截Tomcat的错误页面。例如,

<error-page>
    <error-code>404</error-code>
    <location>/404</location>
</error-page>

现在,您使用RequestMapping将/ 404映射到一个页面,该页面返回您的JSON响应,而不包含任何HTML:

@RequestMapping(value = "/404", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
@ResponseBody
public ResponseEntity<ResponseStatus> handle404() {

    HttpStatus status = null;
    ResponseStatus responseStatus = new ResponseStatus("404", "Wrong path to resource.");
    status = HttpStatus.NOT_FOUND;

    ResponseEntity<ResponseStatus> response = new ResponseEntity<ResponseStatus>(responseStatus, status);

    return response;
}

这将只返回一个名为Response Status的JSON对象,其中包含错误代码和错误消息作为字段。