1

I have set up my spring server to respond to a POST request containing a Message object as:

@RequestMapping(value = "/signup", method = RequestMethod.POST, consumes = "application/json")
    public @ResponseBody Message signUp(@RequestBody Message message) {
        logger.info("Accessing protected resource");
        return new Message(100, "Congratulations!", "You have signed up. msg:"+message.toString());
    }

The Android client is set up to send the request as:

@Override
    protected Message doInBackground(Void... params) {
        // TODO Auto-generated method stub
        final String url = "http://10.0.2.2:8080/signup";
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<Message> requestEntity = new HttpEntity<Message>(signupmsg, requestHeaders);

        // Create a new RestTemplate instance
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());

        try {
            // Make the network request
            Log.d(TAG, url);
            ResponseEntity<Message> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, Message.class);
            return response.getBody();
        } catch (HttpClientErrorException e) {
            Log.e(TAG, e.getLocalizedMessage(), e);
            return new Message(0, e.getStatusText(), e.getLocalizedMessage());
        } catch (ResourceAccessException e) {
            Log.e(TAG, e.getLocalizedMessage(), e);
            return new Message(0, e.getClass().getSimpleName(), e.getLocalizedMessage());
        }
    }

However the server always returns a 403 Forbidden error. I expect it to return another Message object The Message object is custom defined in a separate class. However, if I send a GET request with no encapsulated object, it works. I am new to Spring. What am I missing here?

Update: I diagnosed this problem, and it is happening when I enable spring web security. With no security, the POST request succeeds. I tried disabling the security in the configuration but it still does not work. (GET requests work fine though.) I currently have the following in my WebSecurityConfiguration.java

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // @formatter:off
        auth.inMemoryAuthentication()
                .withUser("roy")
                .password("spring")
                .roles("USER");
        // @formatter:on
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
                .authorizeRequests()
                .anyRequest().permitAll()
                .and()
                .httpBasic().disable();
        // @formatter:on
    }

I enabled web security by adding the following to my build.gradle compile("org.springframework.boot:spring-boot-starter-security")

I tried sending a POST request using browser using a REST client and it gives the error: HTTP Status 403 - Expected CSRF token not found. Has your session expired?

Fixed it temporarily by using .csrf().disable()

What is the best way to solve this?

doomguy
  • 401
  • 10
  • 26
  • can you access http://10.0.2.2:8080/signup through a browser in the device/emulator to see if it gives you access? – Aeseir Dec 09 '14 at 23:46
  • I tried doing it by writing it on the browser and it says error 405 , Request method GET not supported which should be expected as in my controller, I only have the POST version. – doomguy Dec 10 '14 at 00:06
  • ok my other advice is use your computers actual IP to connect to it. So if it is 192.168.1.10 then plug that in instead of 10.0.2.2 – Aeseir Dec 10 '14 at 00:58
  • I tried it, it did not work. Could this be because I am doing this on my office laptop and they may have blocked some local connections? Does the code look fine? – doomguy Dec 10 '14 at 10:38
  • Definitely could be it. 403 also indicates that server has blocked you as well. – Aeseir Dec 10 '14 at 11:43
  • Was this the problem? – Aeseir Dec 11 '14 at 04:53
  • Not sure. I am now trying to migrate my project to my home machine on Android Studio. I am having some problems there. After that I can confirm this – doomguy Dec 11 '14 at 05:52
  • Ok cool. I think that's the issue and potentially the ip address. I used similar setup as you to test it out and only worked on 192.1.1.9 which is my router subnet. Ill make this official answer later for you when I test something out. – Aeseir Dec 11 '14 at 05:54
  • The issue has something to do with the web security config file. I have updated the question. Could you please take a look... – doomguy Dec 15 '14 at 07:44
  • For this problem you would need to firstly obtain the csrf token from your application, then attach it to header before submitting it. It seems like a common issue out there. – Aeseir Dec 15 '14 at 11:14
  • Are you using Spring Security? If so, refer my [answer](http://stackoverflow.com/a/34319194/583237). – Pradip Kharbuja Dec 16 '15 at 18:13

1 Answers1

0

I see it is an old question, if anybody still has this problem the solution is simple, you need to add the "_csrf" token to your request as a parameter. Disabling it is not a solution. exchange() method accepts a map of parameters as well.

    Map<String, Object> parameters = new HashMap<>();
    CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
    parameters.put("_csrf", token);
    restTemplate.exchange("url", HttpMethod.POST, requestEntity, Message.class, parameters);

UPDATE How to retrieve the csrf token:

  1. you can change your method to accept one more parameter, String csrfToken and retrieve the token from the request before you call the method.
  2. @Autowire HttpServletRequest in the class where you have "doInBackground()", which is not good practice, it is better to pass the request object only in the methods that use it.
  3. Spring exposes the session and the request objects through an object called "ServletRequestAttributes", so you can do:

    HttpServletRequest request =
    ((ServletRequestAttributes)RequestContextHolder
                    .currentRequestAttributes())
                    .getRequest();
    
Bobernac Alexandru
  • 169
  • 1
  • 2
  • 11