【问题标题】:Why is my Spring webFilter blocking Cors?为什么我的 Spring webFilter 会阻塞 Cors?
【发布时间】:2020-01-05 12:36:10
【问题描述】:

我已经设置了一个向 Spring 服务器发送请求的 Angular 客户端。我刚刚克服了 CORS 阻塞,所有请求都顺利通过。直到我在 spring 端添加了一个 @webFilter,现在我得到了 CORS 错误,允许 GET 请求,但不允许其他请求。
如果我将 webFilter 放在评论中,代码可以正常工作(但不检查登录)。

我在服务器端添加了一个 restConfig 类,它允许来自我的客户端地址 (localhost:4200) 的所有方法。
即使我将@CrossOrigin 放在每个控制器上(甚至在过滤器文件上),它也不会改变效果。
我在 Angular 端(每个请求)和服务器端(在 restConfig 类和 @CrossOrigin 注释中)都添加了 withCredentials。 我在 pom.xml 中添加了会话依赖项。

我在 Angular 端也有一个拦截器,但这是在我添加拦截器之前发生的。

例如,当我尝试删除时,控制台会写入错误: 选项http://localhost:8080/CouponSystem/sec/admin/removecompany/3 出现 401 错误(即使我已登录)并详细说明: 从源“http://localhost:4200”访问“http://localhost:8080/CouponSystem/sec/admin/removecompany/3”处的 XMLHttpRequest 已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:它没有 HTTP ok 状态。

如前所述,获取请求(返回 200)通过正常。

在检查发送的请求时,这些是删除中显示的标头: 请求网址:http://localhost:8080/CouponSystem/sec/admin/removecompany/3 请求方法:选项 状态码:401 远程地址:[::1]:8080 推荐人政策:降级时无推荐人 访问控制允许凭据:true 访问控制允许方法:删除 访问控制允许来源:http://localhost:4200 访问控制最大年龄:1800 允许:GET、HEAD、POST、PUT、DELETE、TRACE、OPTIONS、PATCH 内容长度:0 日期:2019 年 9 月 2 日星期一 13:00:00 GMT 变化:来源、访问控制请求方法、访问控制请求标头 显示临时标题 访问控制请求方法:删除 来源:http://localhost:4200 推荐人:http://localhost:4200/admin/removecompany Sec-Fetch-Mode: no-cors

如果它与更改标题有关,我需要具体说明如何添加标题。

这是网络过滤器:

//Overcoming CORS while allowing cookies
@CrossOrigin(origins = "*", allowedHeaders = "*", allowCredentials = "true",
methods= {RequestMethod.DELETE, RequestMethod.GET, RequestMethod.HEAD, RequestMethod.OPTIONS, RequestMethod.PATCH, RequestMethod.POST, RequestMethod.PUT, RequestMethod.TRACE})
@WebFilter("/sec/*")
public class LoginFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpSession session = httpRequest.getSession(false);
        if (session == null) {
            httpResponse.sendError(401, "You are not logged in.");

        } else {
            chain.doFilter(httpRequest, httpResponse);
        }
    }
}

这是我的一个restControllers的相关代码:

@RestController
@RequestMapping("sec/admin")
//For overcoming CORS while allowing cookies
@CrossOrigin(origins = "http://localhost:4200", allowedHeaders = "*", allowCredentials = "true") 

public class AdminWebService {

    @Autowired
    AdminService adminService;

    @Autowired
    HttpSession session;

    //GET method - works fine
@RequestMapping(path = "companies") 
    public List<Company> findAllCompanies() {
        return adminService.getAllCompanies();
    }

    @PostMapping(path = "newcompany")//CORS ERROR
    public Company createCompany(@RequestBody Company company) throws IncompatibleInputException {
        return adminService.createCompany(company);
    }


        @PutMapping(path = "updatecompany/{id}") //CORS ERROR
    public void updateCompany(@PathVariable long id, @RequestBody Company company)
            throws IncompatibleInputException, ObjectNotFoundException {
        adminService.updateCompany(company, id);
    }

    @DeleteMapping(path = "removecompany/{id}")//CORS ERROR
    public boolean deleteCompany(@PathVariable long id) throws ObjectNotFoundException {
        adminService.removeCompany(id);
        return true;
    }

这是允许整个网络应用程序通过 CORS 的 restConfig 类(因此,据我了解,@CrossOrigin 并不是真正必要的。):

@SuppressWarnings("deprecation")
@Configuration
public class RestConfig{

    @Bean
    public WebMvcConfigurer corsConfigurer(){
        return new WebMvcConfigurerAdapter() {
            public void addCorsMappings(CorsRegistry registry) {
                registry
                .addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "POST", "OPTIONS", "PUT", "DELETE", "TRACE")
                .allowCredentials(true);
            }
        };
    }

}

这是 Angular 方面 - 正在发送请求:

 public updateCompany(companyToUpdate: Company) {
        this.httpClient.put<Company>(`${this.baseUrl}updatecompany/${companyToUpdate.id}`, companyToUpdate, { withCredentials: true })
            .subscribe(() => alert(`Company ${companyToUpdate.id} has been successfully updated.`), err => alert("We could not update this company.  " + err.error.messages));
    }

    deleteCompany(id: number) {
        if (confirm('Are you sure you want to delete company of id ' + id + '?')) { //user must confirm his intention to delete
            this.httpClient.delete(`${this.baseUrl}removecompany/${id}`, { withCredentials: true })
                .subscribe(res => {
                    alert(`Company ${id} deleted successfully.`);
                    this.getAllCompanies();//update table
                }, err => {
                    alert("Unable to delete. " + err.error.messages);
                    this.getAllCompanies(); //Update dropdown with existing companies.
                }
                );
        } else {
            // Do nothing.  Giving user a chance to regret it.
        }
    }

这是 Angular 端的拦截器——尽管我认为这不是问题,因为请求在发送到服务器之前就被 CORS 阻止了。

@Injectable()
export class HttpConfigInterceptor implements HttpInterceptor {
    constructor(private router: Router) { }
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(catchError(error => {
            // Checking if it is an Authentication Error (401)
            if (error.status === 401) {
                this.router.navigate([`/login`]);
                alert('You are not logged in.  Log in first.');
                return throwError(error);
            }
            // If it is not an authentication error, just throw it
            return throwError(error);
        }));
    }
}

我希望所有请求都能顺利通过,而不仅仅是 GET。 (如果用户没有登录,它应该过滤他并发送 401,然后 Angular 拦截器才会阻止他。)并在发送请求时停止给我 CORS 错误。

【问题讨论】:

  • 浏览器控制台出现什么错误??
  • @gnanajeyam95 这是控制台中的文本:(提供示例浏览器文本我尝试使用删除请求删除公司) removecompany:1 Access to XMLHttpRequest at 'localhost:8080/CouponSystem/sec/admin/removecompany/5' from源“localhost:4200”已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:它没有 HTTP ok 状态。

标签: angular spring-boot session filter cors


【解决方案1】:

最近我在我的 Spring 云 API 网关中遇到了这个问题。 Cors Bean 确实有任何不同。所以我在 application.yml 文件中添加了我的配置。

spring:
   cloud:    
    gateway:
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_UNIQUE
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "*"
            allowed-methods: "*"
            allowed-headers: "*"
            allow-credentials: true

上面我使用default-filters 删除了多个 Access-Control-Allow-Origin 标头。考虑一个场景,如果您的 API 网关和下游服务设置 Access-Control-Allow-Origin,那么您来自服务器的响应标头填充多个响应标头浏览器将不允许。

要删除我们正在使用的多个响应标头

 - DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_UNIQUE

【讨论】:

    【解决方案2】:

    最后,我偶然发现了答案。这与在处理非简单的 HTTP 请求时发送到服务器的预检请求有关(在某些情况下,例如 put 或 post)。
    客户端的预检请求是使用“OPTIONS”方法发送的,该方法显然没有附加会话 ID。这个请求总是被 LoginFilter 满足,它的条件是:

        if (session == null) {
        httpResponse.sendError(401, "You are not logged in.");
        }
    

    我通过将“if”子句扩展为:

        if (session == null&&(!"OPTIONS".equalsIgnoreCase(httpRequest.getMethod())))  {
        httpResponse.sendError(401, "You are not logged in.");
        }
    

    如果这对任何人都有帮助,我还使用 CorsFilter 管理了整个 CORS 问题,该 CorsFilter 还负责处理“选项”方法并返回 200 OK 响应:

    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.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    
    
    @Configuration
    public class CORSFilter implements Filter {
    
        @Value("${cors.origin}")
        private String preDefinedCorsOrigin;
    
        public void destroy() {
    
        }
    
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
                throws IOException, ServletException {
            if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) res;
    
                // Access-Control-Allow-Origin
                response.setHeader("Access-Control-Allow-Origin", preDefinedCorsOrigin);
                response.setHeader("Vary", "Origin");
    
                // Access-Control-Max-Age
                response.setHeader("Access-Control-Max-Age", "3600");
    
                // Access-Control-Allow-Credentials
                response.setHeader("Access-Control-Allow-Credentials", "true");
    
                // Access-Control-Allow-Methods
                response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, PATCH, HEAD, TRACE, OPTIONS, DELETE");
                response.setHeader("Access-Control-Allow-Headers", "Content-type, Accept");
    
                if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
                    response.setStatus(HttpServletResponse.SC_OK);
                }
                chain.doFilter(request, response);
            }
    
        }
    
        public void init(FilterConfig filterConfig) {
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-08-23
      • 1970-01-01
      • 1970-01-01
      • 2011-07-04
      • 2010-10-13
      • 2019-01-20
      • 1970-01-01
      • 2021-06-10
      • 2017-07-14
      相关资源
      最近更新 更多