为什么我的 Spring 网络过滤器会阻塞 Cors?



我已经设置了一个向Spring服务器发送请求的Angular客户端。 不久前我已经克服了CORS阻塞,所有请求都很好。 直到我在弹簧侧添加了一个@webFilter,现在我收到 CORS 错误,允许 GET 请求,但不允许其他请求。
如果我将网络过滤器放在注释中,代码工作正常(但不检查登录(。

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

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

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

如前所述,get 请求(返回 200(通过罚款。

在检查发送的请求时,这些是删除中显示的标头: 请求网址:http://localhost:8080/CouponSystem/sec/admin/removecompany/3 请求方法:选项 状态代码:401 远程地址: [::1]:8080 推荐人政策:降级时禁止推荐人 访问控制允许凭据:真 访问控制-允许-方法:删除 访问控制允许来源:http://localhost:4200 访问控制-最大年龄: 1800 允许:获取、头、发布、放置、删除、跟踪、选项、补丁 内容长度:0 日期:2019 年 9 月 2 日星期一 13:00:00 GMT 变化:来源,访问控制请求方法,访问控制请求标头 显示临时标题 访问控制请求方法:删除 产地:http://localhost:4200 推荐人: http://localhost:4200/admin/removecompany 秒获取模式:无<--我想知道是否需要更改此行。 用户代理:Mozilla/5.0(视窗NT 10.0;赢64;x64( AppleWebKit/537.36 (KHTML, like Gecko( Chrome/76.0.3809.132 Safari/537.36

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

这是网络过滤器:

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

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

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

这是 restConfig 类,它允许整个 Web 应用程序的 CORS 通过(因此据我所知,@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);
}
};
}
}

这是角度端 - 正在发送的请求:

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 错误。

最近,我在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删除了多个访问控制允许源标头。考虑一个场景,如果您的 API 网关和下游服务设置Access-Control-Allow-Origin因此来自填充了多个响应标头的服务器的响应标头浏览器将不允许。

为了删除多个响应标头,我们正在使用它

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

最后,我偶然发现了答案。 这与处理非简单 HTTP 请求(在某些情况下为 put 或 post(时发送到服务器的预检请求有关。
客户端的预检请求是使用"OPTIONS"方法发送的,该方法显然没有附加会话 ID。 此请求始终满足登录过滤器,其条件是:

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 问题,该过滤器还负责"选项"方法并返回 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) {
}
}

最新更新