CORS弹簧靴和angularjs无法正常工作

Jan*_*yne 71 rest spring-mvc cors angularjs spring-boot

我试图在另一个应用程序(spring-boot应用程序)上调用REST端点(angularjs).应用程序在以下主机和端口上运行.

  • REST应用程序,使用spring boot, http://localhost:8080
  • HTML应用程序,使用angularjs, http://localhost:50029

我也在使用spring-securityspring-boot应用程序.从HTML应用程序,我可以对REST应用程序进行身份验证,但此后,我仍然无法访问任何REST端点.例如,我有一个如下定义的angularjs服务.

adminServices.factory('AdminService', ['$resource', '$http', 'conf', function($resource, $http, conf) {
    var s = {};
    s.isAdminLoggedIn = function(data) {
        return $http({
            method: 'GET',
            url: 'http://localhost:8080/api/admin/isloggedin',
            withCredentials: true,
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        });
    };
    s.login = function(username, password) {
        var u = 'username=' + encodeURI(username);
        var p = 'password=' + encodeURI(password);
        var r = 'remember_me=1';
        var data = u + '&' + p + '&' + r;

        return $http({
            method: 'POST',
            url: 'http://localhost:8080/login',
            data: data,
            headers: {'Content-Type': 'application/x-www-form-urlencoded'}
        });
    };
    return s;
}]);
Run Code Online (Sandbox Code Playgroud)

angularjs控制器如下所示.

adminControllers.controller('LoginController', ['$scope', '$http', 'AdminService', function($scope, $http, AdminService) {
    $scope.username = '';
    $scope.password = '';

    $scope.signIn = function() {
        AdminService.login($scope.username, $scope.password)
            .success(function(d,s) {
                if(d['success']) {
                    console.log('ok authenticated, call another REST endpoint');
                    AdminService.isAdminLoggedIn()
                        .success(function(d,s) {
                            console.log('i can access a protected REST endpoint after logging in');
                        })
                        .error(function(d, s) { 
                            console.log('huh, error checking to see if admin is logged in');
                            $scope.reset();
                        });
                } else {
                    console.log('bad credentials?');
                }
            })
            .error(function(d, s) {
                console.log('huh, error happened!');
            });
    };
}]);
Run Code Online (Sandbox Code Playgroud)

在电话中http://localhost:8080/api/admin/isloggedin,我得到了一个401 Unauthorized.

在REST应用程序方面,我有一个CORS过滤器,如下所示.

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {

    @Override
    public void destroy() { }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;

        response.setHeader("Access-Control-Allow-Origin", "http://localhost:50029");
        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, X-Auth-Token");
        response.setHeader("Access-Control-Allow-Credentials", "true");

        if(!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
            chain.doFilter(req, res);
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException { }
}
Run Code Online (Sandbox Code Playgroud)

我的spring安全配置如下所示.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private JsonAuthSuccessHandler jsonAuthSuccessHandler;

    @Autowired
    private JsonAuthFailureHandler jsonAuthFailureHandler;

    @Autowired
    private JsonLogoutSuccessHandler jsonLogoutSuccessHandler;

    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;

    @Value("${rememberme.key}")
    private String rememberMeKey;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .exceptionHandling()
            .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and()
            .authorizeRequests()
                .antMatchers("/api/admin/**").hasRole("ADMIN")
                .antMatchers("/", "/admin", "/css/**", "/js/**", "/fonts/**", "/api/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .successHandler(jsonAuthSuccessHandler)
                .failureHandler(jsonAuthFailureHandler)
                .permitAll()
                .and()
            .logout()
                .deleteCookies("remember-me", "JSESSIONID")
                .logoutSuccessHandler(jsonLogoutSuccessHandler)
                .permitAll()
                .and()
            .rememberMe()
                .userDetailsService(userDetailsService)
                .tokenRepository(persistentTokenRepository)
                .rememberMeCookieName("REMEMBER_ME")
                .rememberMeParameter("remember_me")
                .tokenValiditySeconds(1209600)
                .useSecureCookie(false)
                .key(rememberMeKey);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .authenticationProvider(authenticationProvider);
    }
}
Run Code Online (Sandbox Code Playgroud)

所有处理程序正在执行的是{success: true}根据用户是否登录,无法进行身份验证或注销而写出JSON响应.在RestAuthenticationEntryPoint如下所示.

@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException ex)
            throws IOException, ServletException {
        resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }

}
Run Code Online (Sandbox Code Playgroud)

关于我遗失或做错的任何想法?

abo*_*cic 85

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class SimpleCORSFilter implements Filter {

private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);

public SimpleCORSFilter() {
    log.info("SimpleCORSFilter init");
}

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");

    chain.doFilter(req, res);
}

@Override
public void init(FilterConfig filterConfig) {
}

@Override
public void destroy() {
}

}
Run Code Online (Sandbox Code Playgroud)

无需额外定义此过滤器只需添加此类.Spring将扫描并为您添加它.SimpleCORSFilter.这是一个例子:spring-enable-cors

  • 这个答案本质上绕过了 CORS 的好处。通过将 Origin 标头反映到“Access-control-allow-origin”响应标头中并允许凭据请求,您只需将您的应用程序暴露给来自世界上任何站点的跨站点请求即可。*请读者谨慎使用此答案* (3认同)
  • 但它阻止执行AuthenticationEntryPoint ..请指导 (2认同)
  • 如果应用程序中有其他过滤器,则通过使用`@Order(Ordered.HIGHEST_PRECEDENCE)`注释过滤器,此过滤器必须具有最高优先级. (2认同)

Yog*_*Rai 29

我曾经遇到过类似的情况.经过研究和测试,这是我的发现:

  1. 使用Spring Boot,启用全局CORS的推荐方法是在Spring MVC中声明并结合细粒度@CrossOrigin配置:

    @Configuration
    public class CorsConfig {
    
        @Bean
        public WebMvcConfigurer corsConfigurer() {
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
                            .allowedHeaders("*");
                }
            };
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 现在,由于您使用的是Spring Security,因此您必须在Spring Security级别启用CORS,以允许它利用Spring MVC级别定义的配置:

    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.cors().and()...
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    是非常优秀的教程,解释了Spring MVC框架中的CORS支持.

  • ups可以处理此更改http .csrf().disable().cors().and() (2认同)

Edu*_*nis 14

如果你想在不使用过滤器或没有配置文件的情况下启用CORS,只需添加即可

@CrossOrigin
Run Code Online (Sandbox Code Playgroud)

到你的控制器顶部,它的工作原理.

  • 遵循这种方法有什么安全风险? (5认同)

Dee*_*pak 9

为了建立在上述其他答案的基础上,如果您有一个具有 Spring 安全性的 Spring Boot REST 服务应用程序(不是 Spring MVC),那么通过 Spring 安全性启用 CORS 就足够了(如果您使用 Spring MVC,那么使用WebMvcConfigurerYogen 提到的bean 可能是Spring 安全性将委托给其中提到的 CORS 定义的方式)

因此,您需要有一个执行以下操作的安全配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    //other http security config
    http.cors().configurationSource(corsConfigurationSource());
}

//This can be customized as required
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    List<String> allowOrigins = Arrays.asList("*");
    configuration.setAllowedOrigins(allowOrigins);
    configuration.setAllowedMethods(singletonList("*"));
    configuration.setAllowedHeaders(singletonList("*"));
    //in case authentication is enabled this flag MUST be set, otherwise CORS requests will fail
    configuration.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}
Run Code Online (Sandbox Code Playgroud)

}

此链接有更多信息:https : //docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors

笔记:

  1. 为生产部署的应用程序的所有来源 (*) 启用 CORS 可能并不总是一个好主意。
  2. CSRF 可以通过 Spring HttpSecurity 自定义启用,没有任何问题
  3. 如果您使用 Spring 在应用程序中启用了身份验证(UserDetailsService例如通过 a ),则configuration.setAllowCredentials(true);必须添加

测试 Spring boot 2.0.0.RELEASE(即 Spring 5.0.4.RELEASE 和 Spring security 5.0.3.RELEASE)


Sep*_* GH 8

我正在使用spring boot 2.1.0,对我有用的是

A. 通过以下方式添加 cors 映射:

@Configuration
public class Config implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("*");
    }
}
Run Code Online (Sandbox Code Playgroud)

B. 将以下配置添加到我HttpSecurity的 Spring Security 中

.cors().configurationSource(new CorsConfigurationSource() {

    @Override
    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedHeaders(Collections.singletonList("*"));
        config.setAllowedMethods(Collections.singletonList("*"));
        config.addAllowedOrigin("*");
        config.setAllowCredentials(true);
        return config;
    }
})
Run Code Online (Sandbox Code Playgroud)

同样在 Zuul 代理的情况下,您可以使用这个INSTEAD OF A 和 B(仅用于HttpSecurity.cors()在 Spring 安全中启用它):

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("HEAD");
    config.addAllowedMethod("GET");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("DELETE");
    config.addAllowedMethod("PATCH");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这对我有用:

@Configuration
public class MyConfig extends WebSecurityConfigurerAdapter  {
   //...
   @Override
   protected void configure(HttpSecurity http) throws Exception {

       //...         

       http.cors().configurationSource(new CorsConfigurationSource() {

        @Override
        public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowedHeaders(Collections.singletonList("*"));
            config.setAllowedMethods(Collections.singletonList("*"));
            config.addAllowedOrigin("*");
            config.setAllowCredentials(true);
            return config;
        }
      });

      //...

   }

   //...

}
Run Code Online (Sandbox Code Playgroud)


Den*_* II 5

这对我有用。

@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.cors();
    }

}

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
            .addMapping("/**")
            .allowedMethods("*")
            .allowedHeaders("*")
            .allowedOrigins("*")
            .allowCredentials(true);
    }

}
Run Code Online (Sandbox Code Playgroud)