clsbartek
clsbartek

Reputation: 206

Spring Security for rest application. POST always return 403 code

this is my spring-security.xml:

    <security:http pattern="/eklienci/**"
        authentication-manager-ref="authenticationManager" entry-point-ref="restAuthenticationEntryPoint"
        create-session="stateless">
        <security:intercept-url pattern="/eklienci/**"
            access="hasAnyAuthority('ADMIN','USER','VIEWER')" />
        <form-login
            authentication-success-handler-ref="mySuccessHandler"
            authentication-failure-handler-ref="myFailureHandler"
        />
        <security:custom-filter ref="restServicesFilter"
            before="PRE_AUTH_FILTER" />
    </security:http>
    <!-- other stuff --!>
  <beans:bean id="restAuthenticationEntryPoint"
      class="pl.aemon.smom.config.RestAuthenticationEntryPoint" />
  <!-- Filter for REST services. -->
  <beans:bean id="restServicesFilter"
    class="pl.aemon.smom.config.RestUsernamePasswordAuthenticationFilter">
    <beans:property name="postOnly" value="true" />
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="authenticationSuccessHandler" ref="mySuccessHandler" />
 </beans:bean>

This is my RestUsernamePasswordAuthenticationFilter:

public class RestUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{

    @Autowired
    private CustomAuthenticationProvider authenticationProvider;


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

        Enumeration<String> names = request.getHeaderNames();
        while(names.hasMoreElements())
        {
            System.out.println(names.nextElement());
        }
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        System.out.println("Username " + username + " password: " + password);
        if(username!=null)
        {
            username = username.trim();
        }

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        return authenticationProvider.authenticateRest(authRequest);

//        System.out.println(auth.toString());
//        return auth;
    }



    @Override
    protected String obtainPassword(HttpServletRequest request) {
        return request.getHeader("password");
    }

    @Override
    protected String obtainUsername(HttpServletRequest request) {
        return request.getHeader("username");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        Authentication auth = attemptAuthentication(httpRequest, httpResponse);
        SecurityContextHolder.getContext().setAuthentication(auth);

        chain.doFilter(request, response);

    }
}

And this is my RestController methods

@RestController
@RequestMapping("/eklienci")
public class EklientRestController
{
    @RequestMapping(value="/get/{eklientid}")
    public Eklient get(@PathVariable String eklientid) 
    {
        return userService.findById(eklientid);
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST, produces="application/json", consumes="application/json")
    @ResponseBody
    public String add(@RequestBody String json) 
    {
        System.out.println(json);
        Eklient pj = new Eklient();
        ObjectMapper mapper = new ObjectMapper();
        try
        {
            pj = mapper.readValue(json, Eklient.class);
            return mapper.writeValueAsString(pj);
        } catch (JsonParseException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JsonMappingException e)
        {
        // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return "Error";
    }
}

When i try to call /get/{eklientid}, it always works fine. All GET calls always returns at least infomration about UNARTHORIZED access (401) and i see logs from RestUsernamePasswordAuthenticationFilter.

But when i try any POST call (for example /eklienci /add} my application always returns 403 code and doesn't producde any log. What is the reason? How to fix it ?

Upvotes: 2

Views: 1813

Answers (2)

holmis83
holmis83

Reputation: 16604

Turn off CSRF protection. It is not needed when you have create-session="stateless".

<security:http ...>
  <security:csrf disabled="true"/>
  <!-- the rest same as before -->
</security:http>

Upvotes: -2

Glen Mazza
Glen Mazza

Reputation: 788

CSRF is activated by default for the common non-GET methods like POST, PUT, DELETE causing 403s if you haven't placed the CSRF headers in your REST calls. You can (temporarily!) shut off CSRF in your security.xml to confirm that's the issue. Mainly you'll need to add the CSRF headers into your REST calls and <sec:csrfInput/> JSPs tags for any client-server calls. FYI, additional classes I needed to implement in my open-source project, perhaps useful for you:

  1. CsrfSecurityRequestMatcher to turn off CSRF for certain POSTs/PUTs, etc., that don't require authorization. As configured here in my security.xml.
  2. CustomAccessDeniedHandlerImpl to route CSRF 403's resulting from session timeouts to a login page instead.

Upvotes: 3

Related Questions