Spring 启动 CORS 过滤器无法与 AngularJS 一起工作

Spring boot CORS Filter not working with AngularJS

我创建了一个 Spring 引导应用程序,它有一个 AngularJS 前端,但我没有使用任何 Spring 安全模块。

我已经在我的控制器中添加了@CrossOrigin注解,如下所示

@CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
@RestController
public class MyController {
   public static final Logger logger = LoggerFactory.getLogger(MyController.class);
   .....
   .....

我还创建了一个 CORS 过滤器,如下所示。

@Component
public class CORSFilter 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() {}

}

我的 angularJS 前端

出现以下选项错误
XMLHttpRequest cannot load http://localhost:8080/abc/books/. Response for preflight has invalid HTTP status code 401 (edited)

[12:10] 
zone.js:2744 OPTIONS http://localhost:8080/abc/books/ 401 ()

我的 Spring 启动应用程序与 PostMan 一起工作正常,当我使用 Chrome 时会发生此预检错误。我该如何解决?

编辑

我还使用 activiti-spring-boot-starter-rest-api,它有以下内容:

import org.activiti.engine.IdentityService;
import org.activiti.engine.identity.User;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class AuthenticationService {

@Bean
InitializingBean usersAndGroupsInitializer(final IdentityService identityService) {

    return new InitializingBean() {
        public void afterPropertiesSet() throws Exception {
            User admin = identityService.newUser("admin");
            admin.setPassword("admin");
            identityService.saveUser(admin);
            }
        };
    }

}

这是一个 Spring CORS 过滤器,可以很好地与 AngularJS

配合使用
public class CORSFilter extends OncePerRequestFilter {

    private final Logger LOG = LoggerFactory.getLogger(CORSFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {
        LOG.info("Adding CORS Headers ........................");        
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        res.setHeader("Access-Control-Max-Age", "3600");
        res.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER,Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization");
        res.addHeader("Access-Control-Expose-Headers", "xsrf-token");
        if ("OPTIONS".equals(req.getMethod())) {
            res.setStatus(HttpServletResponse.SC_OK);
        } else { 
            chain.doFilter(req, res);
        }        
    }

我从 post Spring cors allow all origins

中找到了这个

如评论中所述,问题是 activiti-spring-boot-starter-rest-api 具有开箱即用的安全性。这里的问题是他们在根上下文上应用了安全性,这意味着任何其他端点也将具有身份验证。

选项 1:添加您自己的安全性

要解决这个问题,您可以创建自己的具有更高优先级的 SecurityConfig class 并允许所有请求转到 /abc/**(或其他任何请求),例如:

@Order(Ordered.HIGHEST_PRECEDENCE) // This should "overrule" Activiti's security
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/abc/**")
            .authorizeRequests().anyRequest().permitAll();
    }
}

选项 2:禁用 Activiti 的安全性

或者,您可以禁用 Activiti 的安全自动配置:

@EnableAutoConfiguration(exclude = {
    org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class,
    org.activiti.spring.boot.SecurityAutoConfiguration.class
})

这将禁用 Activiti 和 Spring 引导本身的安全自动配置。如果您只禁用 Activiti 的安全配置,Spring 启动的自动配置将启动,您仍然可以在所有端点上进行身份验证。

但是,您可能仍想在 Activiti 的某些端点上应用安全性,但这允许您自己自定义它,而不是依赖自动配置。在您的情况下,您可以选择对所有 /identity/**/management/**/history/**/repository/**/query/**/runtime/** 路径应用安全性,这正在被 Activiti 使用。


选项 3:在 OPTIONS

的情况下覆盖过滤器链

第三种选择是绕过整个安全过滤器链。如 所示,您可以通过不调用 chain.doFilter(req, res) 来跳出过滤器链。这可以通过在过滤器中添加以下内容来完成:

if (HttpMethod.OPTIONS.name().equalsIgnoreCase(req.getMethod()) {
    res.setStatus(HttpServletResponse.SC_OK);
} else {
    chain.doFilter(req, res); // Only apply doFilter() when not OPTIONS
}

NOTE: Be aware though, this will only allow the OPTIONS call to go through. The security also affects the other endpoints you defined in your controller, and not just the OPTIONS call.

通过有条件地调用 doFilter(),您可以跳过整个安全过滤器链,以防出现 OPTIONS 请求。您必须将 reqres 转换为HttpServletRequestHttpServletResponse 分别。

此外,您必须确保在 安全过滤器链之前 调用此过滤器。为此,请将 CORSFilter 的顺序更改为选定的值,例如:

@Component
@Order(1) // You can choose the value yourself
public class CORSFilter implements Filter {
    // ...
}

现在通过将 application.properties 中的 security.filter-order 属性 设置为更高的值来更改安全过滤器链顺序:

security.filter-order=2