使用CORS的Spring Boot Rest Spring安全认证问题

时间:2017-04-16 01:44:56

标签: spring-boot spring-rest

我正在使用Spring Boot开发Spring REST应用程序。我正在使用角度JS客户端来使用Spring REST API。

我正在使用的堆栈是:

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

我的问题是:

1)登录时,用户会成功通过身份验证。自定义身份验证成功处理程序返回200状态代码。

2)认证成功后,当clint发送另一个请求来访问一个已获取的资源时,HttpSession为NULL

3)当SecurityContextPersistenceFilter尝试从SecurityContext检索HttpSession时,它将返回为null并且  并且服务器再次向/login资源发送请求

4)所以我的问题是:

1)为什么第二个请求的httpsesion为空?

2)我观察到Spring安全性在第一次成功验证后返回JSESSIONID作为cookie。

3)但之后,客户端不会在请求中发送此JSESSIONID,因此第二个请求不会在同一个会话中。

4)如果是这种情况,如果SecurityContext未建立,将如何检索SESSION

请帮忙,因为我无法继续

编辑1:

我在弹簧安全性提供的内存用户中使用默认值。我的安全配置如下:

@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);
    }

}

我作为经过身份验证的用户获得了以下用户:

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

请求现在失败:AbstractSecurityInterceptorAccessDecisionVoterauthenticated表达式返回false并抛出访问被拒绝的异常

2 个答案:

答案 0 :(得分:1)

没有会话休息,因为RESTFUL服务不维护状态,读取此堆栈溢出帖子:

Do sessions really violate RESTfulness?

如果您想知道如何正确构建基于Spring的Spring安全服务,请查看本教程:

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

希望这会有所帮助。

您正在使用formLogin security ,这意味着您需要添加某种数据存储来对其进行身份验证。

以下是spring文档中内存数据存储的示例:

@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;
}

}

根据此示例,您假设使用用户名和密码

成功进行身份验证

您正尝试将angularJS作为前端技术应用安全性,这是一个很好的教程,如何实现这一点,我已经在我的项目中实现了它,这里是链接:

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

对于初学者,您需要使用httpBasic而非formLogin作为安全身份验证方法。

答案 1 :(得分:0)

长时间敲打头后,我终于找到了解决办法。

步骤是: 1)基于表单的登录需要建立会话。验证成功后,服务器会发送Set-Cookie:JSESSIONID=3974317C6DE34865B6726FCFD4C98C08; Path=/springbootrest; HttpOnly标头作为响应。

2)浏览器必须在每个请求中发送此标头,否则将不会建立会话并且身份验证将失败

3)但是由于我使用CORS(我的服务器和ui app驻留在本地系统上的不同端口上),浏览器未在请求中发送Cookie:JSESSIONID=3974317C6DE34865B6726FCFD4C98C08未通过身份验证。

4)在浏览器开始在请求中发送标题之后,我必须在我的角度js客户端中添加以下行$httpProvider.defaults.withCredentials = true;

5)在服务器端,我必须在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);
        }