Spring Boot CORS 过滤器不适用于 AngularJS

Posted

技术标签:

【中文标题】Spring Boot CORS 过滤器不适用于 AngularJS【英文标题】:Spring boot CORS Filter not working with AngularJS 【发布时间】:2018-03-09 17:52:26 【问题描述】:

我创建了一个带有 AngularJS 前端的 SpringBoot 应用程序,但我没有使用任何 Spring Security 模块。

我在控制器中添加了@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 前端中的 OPTIONS 出现以下错误

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 ()

我的 SpringBoot 应用程序在 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);
            
        ;
    

【问题讨论】:

@g00glen00b 我没有启用弹簧安全 @g00glen00b 这是有道理的。我已经编辑了我的问题。我没有意识到 activiti 依赖可能导致问题。有没有办法可以禁用 OPTIONS 的要求授权? 【参考方案1】:

这是一个 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);
                
    

我是从Spring cors allow all origins 帖子中找到的

【讨论】:

据我所知,这只有在过滤器在安全过滤器链之前排序时才有效。【参考方案2】:

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

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

要解决这个问题,您可以创建自己的具有更高优先级的 SecurityConfig 类,并允许所有请求转到 /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 boot 本身的安全自动配置。如果你只禁用 Activiti 的安全配置,Spring boot 的自动配置就会启动,你仍然可以在所有端点上进行身份验证。

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


选项 3:在 OPTIONS 的情况下覆盖过滤器链

第三种选择是绕过整个安全过滤器链。如this answer 所示,您可以通过不调用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

注意:但请注意,这只会允许 OPTIONS 调用通过。安全性还会影响您在控制器中定义的其他端点,而不仅仅是OPTIONS 调用。

通过有条件地调用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

【讨论】:

我尝试了选项 1。但它给了我同样的错误。我在我的应用程序上启用了调试模式,这就是我能看到的。名称为“dispatcherServlet”的 DispatcherServlet 正在处理 [/books/error] 的 OPTIONS 请求。当您尝试使用 POSTMAN 时,它如何返回正确的 url 那个端点是/books/error,我猜它不属于/abc/**匹配器?所以这可能是问题所在。 我暂时选择了选项 2。如果我想为某些 url 启用它,我是否必须为这些端点创建一个单独的安全配置? 如果他们没有类似的路径,可能是的。在这些情况下,最好选择选项 2 并定义自己的 SecurityConfig 来保护 Activiti 本身的路径。

以上是关于Spring Boot CORS 过滤器不适用于 AngularJS的主要内容,如果未能解决你的问题,请参考以下文章

cors 不适用于我的 Spring Boot 应用程序

Spring Boot JWT Cors 不适用于 Angular 8

身份验证过滤器不适用于 Spring Boot

Spring Security 保护路由 hasRole,hasAuthority 不适用于 CORS http 请求

Ldap 身份验证不适用于 Spring Boot

@CrossOrigin 不适用于 spring-boot-starter-web 2.5.2