Atul
Atul

Reputation: 1590

Spring Boot Rest Spring Security Authentication Issue with CORS

I am developing a Spring REST application using Spring Boot. I am consuming the Spring REST APIs using angular JS client.

The stack I am using is :

Spring Boot 1.4.2 , Spring Security , Angular JS , Tomcat 8 

My Issue Is :

1)At login , user gets succesfully authenticated. Custom authentication success handler returns 200 status code.

2)After authentication success , when clint sends another request to access a procted resource , HttpSession is NULL

3)Due to which when SecurityContextPersistenceFilter tries to retrieve the SecurityContext from HttpSession it is returned as null and and again server sends request to /login resource

4)So my questions are :

1)Why is httpsesion is null for second request ?

2)I have observed that Spring security returns JSESSIONID as cookie after first succesfull authentication.

3)But after that , client is NOT sending this JSESSIONID in request hence second request won't be in same session.

4)If that is the case , how is SecurityContext will be retrieved if SESSION is not established ?

Please help as I am not able to proceed here

EDIT 1 :

I am using default in memory user provided by spring security. My security configuration is bellow :

@Configuration
@EnableWebSecurity
@ComponentScan({"com.atul.security"})
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private RESTAuthenticationSuccessHandler authenticationSuccessHandler;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
          http
            .formLogin().successHandler(authenticationSuccessHandler)
            .and()
            .csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS,"/*").permitAll()
            .antMatchers("/index.html", "/home.html", "/login.html", "/").permitAll()
            .anyRequest().authenticated()
            .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

}

I am getting below user as authenticated user :

org.springframework.security.authentication.AnonymousAuthenticationToken@9055c2bc: 
Principal: anonymousUser; Credentials: [PROTECTED]; 
Authenticated: true; 
Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: 
RemoteIpAddress: 0:0:0:0:0:0:0:1; 
SessionId: null; 
Granted Authorities: ROLE_ANONYMOUS

The request is now failing at : AbstractSecurityInterceptor : AccessDecisionVoter returning false for authenticated expression and access denied exception is thrown

Upvotes: 0

Views: 2645

Answers (2)

Atul
Atul

Reputation: 1590

After banging head for a long time , I am finally able to find out the solution.

Steps are : 1) Form based login requires session to be established . When authentication succeeds , server sends Set-Cookie:JSESSIONID=3974317C6DE34865B6726FCFD4C98C08; Path=/springbootrest; HttpOnly header in response.

2)Browser has to send this header in every request else there will be no session established and authentication will fail

3)But since I was using CORS (my server and ui app reside on different port on local system) , browser was not sending Cookie:JSESSIONID=3974317C6DE34865B6726FCFD4C98C08 in request failing the authentication.

4)I have to add below line $httpProvider.defaults.withCredentials = true; in my angular js client after that browser started sending above header in request.

5)At server side I have to add below code in CORSFilter.

 response.setHeader("Access-Control-Allow-Credentials","true");
        response.setHeader("Access-Control-Allow-Origin", "http://localhost:8186");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, WWW-Authenticate, Authorization, Origin, Content-Type, Version");
        response.setHeader("Access-Control-Expose-Headers", "X-Requested-With, WWW-Authenticate, Authorization, Origin, Content-Type");

       final HttpServletRequest request = (HttpServletRequest) req;
       if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }

Upvotes: 1

Moshe Arad
Moshe Arad

Reputation: 3733

There is no session in rest because RESTFUL services don't maintain state, read this stack overflow post:

Do sessions really violate RESTfulness?

If you want to know how properly build spring security service based REST examine this tutorial:

http://www.baeldung.com/securing-a-restful-web-service-with-spring-security

Hope that this helps.

You are using formLogin security, which means you need to add some kind of data store to authenticate users against it.

Here is an example for in-memory data store from spring documentation:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public UserDetailsService userDetailsService() throws Exception {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(User.withUsername("user").password("password").roles("USER").build());
    manager.createUser(User.withUsername("admin").password("password").roles("USER","ADMIN").build());
    return manager;
}

}

according to this example, you suppose to authenticate successfully with a username of user and password of password

You are trying to apply security with angularJS as your front technology, there's a great tutorial how to achieve that, I already implemented it on my project, here's the link:

https://spring.io/guides/tutorials/spring-security-and-angular-js/

For starter you need to use httpBasic and not formLogin as your security authentication method.

Upvotes: 1

Related Questions