springspring-securityspring-android

How to have sessions in http basic authentication in a spring based server


I am trying to create a Spring server for an android client and am using basic authentication. I have a controller as follows:

@RequestMapping(value = "login", method = RequestMethod.GET, produces = "application/json")
public @ResponseBody Message login(HttpSession session) {
    logger.info("Accessing protected resource sid:"+session.getId());
    return new Message(100, "Congratulations!", "You have logged in.");
}

@RequestMapping(value = "play", method = RequestMethod.GET, produces = "application/json")
public @ResponseBody Message play(HttpSession session) {
    logger.info("Accessing protected play resource");
    return new Message(100, "Congratulations!", "Launching play.");
}

Once the client has authenticated, during login, I don't want it to need to reauthenticate while calling play.

My security config is:

 protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/signup","/about").permitAll()
                .anyRequest().authenticated()
                .and()
                .httpBasic().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);

        // @formatter:on
    }

}

I have tried to enable sessions above but if I try to print the session id in the request handlers in login and play, I get different ids after logging in with authentication. I am using HttpBasic security. Is it possible to have sessions in HttpBasic security? I read some articles which seemed to indicate that it is stateless and one cannot. Is there a workaround or do I have to switch to a different security model?

Client code:

On the client side, I send requests as follows for login.

@Override
    protected Message doInBackground(Void... params) {
        //final String url = getString(R.string.base_uri) + "/getmessage";
        final String url = "http://10.0.2.2:8080/login";

        // Populate the HTTP Basic Authentitcation header with the username and password
        HttpAuthentication authHeader = new HttpBasicAuthentication(username, password);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setAuthorization(authHeader);
        requestHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));

        // 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.GET, new HttpEntity<Object>(requestHeaders), Message.class);

            if(response.getBody().getId() == 100)
                login_success = true;
            return response.getBody();

I am trying to write the client code for 'play' similarly but without the need to pass the authentication header. I feel, I should somehow pass a session related parameter in the request header to notify the server that it is part of the same session but cant figure out how to do that.


Solution

  • You are trying to archive stateful communication with sessions, but it won't work since RestTemplate is working in a stateless manner. It will not keep the state (i.e. the session id) from the login to the next call. Only by setting a custom ClientHttpRequestFactory I could see this work (I'm not sure on Android).

    If you want communication with state, you could look at Apache HttpClient. By using it with a HttpContext that you re-use between requests, it will keep the state.

    There is a possibility to fiddle with the session id yourself, but I can't recommend it.