1

I'm trying to create a javascript application communicating with the backend written with spring (spring-boot) only through the rest interface (@RestController). The problem is that the request is cross domain and in the end the session doesn't seem to be established after the user logs into the application. When I try to send a new http request to the backend, after logging in, the Principal is empty.

Here's my security configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/**").permitAll()
                .and()
                .formLogin()
                    .passwordParameter("password")
                    .usernameParameter("email")
                    .loginPage("/")
                    .failureHandler(new JsonAuthenticationFailureHandler())
                    .successHandler(new JsonAuthenticationSuccessHandler())
                .and()
                .rememberMe()
                .and()
                .logout()
                    .deleteCookies("remember-me")
                    .logoutSuccessUrl("/")
                    .logoutUrl("/logout")
                .and()
                .csrf()
                    .disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth, PasswordEncoder encoder) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(encoder);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }
}

Here's my CORS configuration:

@Component
public class SimpleCORSFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        chain.doFilter(req, res);
    }

    public void init(FilterConfig filterConfig) {}

    public void destroy() {}

}

And an example controller that I try to call after successful login:

@RestController
@RequestMapping("/rest/users")
public class UserController extends AbstractRestController {

    @Autowired
    private UserService userService;

    /**
     * Exposed public method for registering the user
     *
     * @param email    email to log in
     * @param password password to log in
     * @return BooleanResponse.result == true if the user registration succeeded without any problems
     */
    @RequestMapping(value = "/register", method = {RequestMethod.POST, RequestMethod.GET})
    public RegistrationResponse register(@RequestParam String email, @RequestParam String password) {
        try {
            userService.createUser(email, password);
        } catch (UserRegistrationException e) {
            return new RegistrationResponse(e.getErrors());
        }
        return new RegistrationResponse(Arrays.asList(RegistrationStatus.SUCCESS));
    }

    /**
     * Determines whether a user with a specific email address already exists
     *
     * @param email email address to be searched for
     * @return BooleanResponse.result = true if the user with the specific email already exists
     */
    @RequestMapping(value = "/userExists", method = {RequestMethod.POST, RequestMethod.GET})
    public BooleanResponse userExists(@RequestParam String email) {
        boolean response = userService.userWithEmailExists(email);
        return new BooleanResponse(response);
    }

    /**
     * Searches for the user info
     *
     * @param principal uniquely identifying the logged in user
     * @return user info for the user with the specified email address
     */
    @RequestMapping(value = "/userInfo", method = RequestMethod.GET)
    public UserInfo getUserInfo(Principal principal) {
        return userService.findUserInfo(principal.getName());
    }


    /**
     * Update info of the currently logged in user
     *
     * @param userInfo new user info provided in the request body
     */
    @RequestMapping(value = "/userInfo", method = RequestMethod.POST)
    public void submitUserInfo(@RequestBody UserInfo userInfo, Principal principal) {
        userService.updateUserInfo(principal.getName(), userInfo);
    }
}

so submitUserInfo and getUserInfo should receive the Principal object if the user is logged in, but they don't. I also tried to add session attributes in JsonAuthenticationSuccessHandler, but when I add the appropriate @SessionAttributes to my controller and try to get the attributes inside the method with @ModelAttribute, I still cannot get it, hence the assumption that my http session was not properly established.

Kamil Janowski
  • 1,872
  • 2
  • 21
  • 43

1 Answers1

0

Not sure how you do AJAX call in JS.

If you are using AngularJS, you need add credential when doing AJAX call,

$http({
                    method: 'PUT',
                    url: <rest_api>,
                    withCredentials: true,
                    data: {},
                })..then(function successCallback(response) {
Kane
  • 8,035
  • 7
  • 46
  • 75
  • I think I've seen a similar solution somewhere before, but with jQuery... still, do you know what this thing actually changes about the request itself? does it modify the header in some funny way? I'm also trying to write jbehave acceptance tests for the backend that would literally generate the http requests to the server, but they seem to have the same problem – Kamil Janowski Oct 24 '15 at 06:01
  • The magic of AngularJS's `withCredentials: true` doing is taking `JSession`/`Session` cookie when requesting your RestAPI served by spring boot app. – Kane Oct 24 '15 at 06:20
  • Also see [this answer](http://stackoverflow.com/a/17847313/390513) for jquery @KamilMilkaJanowski – Kane Oct 24 '15 at 06:24