为了安全,web默认是不允许跨域访问的。不过实际项目中,会遇到不同模块之间来回跳转的情况。所以,项目内部一般会修改配置或者代码来解决CORS跨域问题。
我的后端使用的是 Jetty 服务器,所以下面就拿jetty来举例。Jetty 提供了一个 CrossOriginFilter
类,可以用来配置和管理跨域请求。
在 Jetty 中配置 CORS
下面有三种配置方式,一种是在web.xml中直接配置,无需修改代码。另外两种是在java代码中配置,这种适合没有web.xml的场景。
方法一. 修改 web.xml
文件
如果项目使用的是 web.xml
文件来配置 servlet,可以通过在 web.xml
中添加以下内容来配置 CORS 过滤器:
<filter>
<filter-name>cross-origin</filter-name>
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
<init-param>
<param-name>allowedOrigins</param-name>
<param-value>*</param-value> <!-- 允许所有域,如果需要限制可以具体指定 -->
</init-param>
<init-param>
<param-name>allowedMethods</param-name>
<param-value>GET,POST,HEAD,OPTIONS</param-value> <!-- 允许的HTTP方法 -->
</init-param>
<init-param>
<param-name>allowedHeaders</param-name>
<param-value>X-Requested-With,Content-Type,Accept,Origin</param-value> <!-- 允许的请求头 -->
</init-param>
<init-param>
<param-name>preflightMaxAge</param-name>
<param-value>1800</param-value> <!-- 预检请求的最大缓存时间,单位为秒 -->
</init-param>
<init-param>
<param-name>allowCredentials</param-name>
<param-value>true</param-value> <!-- 是否允许凭证 -->
</init-param>
</filter>
<filter-mapping>
<filter-name>cross-origin</filter-name>
<url-pattern>/*</url-pattern> <!-- 过滤器适用的URL模式 -->
</filter-mapping>
方法二. 使用 Java 代码配置 CORS 过滤器
如果使用的是 Java 代码配置 Jetty 而不是 web.xml
文件,可以使用以下代码来配置 CrossOriginFilter
:
import org.eclipse.jetty.servlets.CrossOriginFilter;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
// 创建 Jetty 服务器实例
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
// 创建并配置 CORS 过滤器
FilterHolder cors = new FilterHolder(CrossOriginFilter.class);
cors.setInitParameter("allowedOrigins", "*"); // 允许所有来源
cors.setInitParameter("allowedMethods", "GET,POST,HEAD,OPTIONS"); // 允许的HTTP方法
cors.setInitParameter("allowedHeaders", "X-Requested-With,Content-Type,Accept,Origin"); // 允许的请求头
cors.setInitParameter("allowCredentials", "true"); // 是否允许凭证
// 将 CORS 过滤器添加到 Jetty 上下文
context.addFilter(cors, "/*", EnumSet.of(DispatcherType.REQUEST));
server.setHandler(context);
// 启动服务器
server.start();
server.join();
CrossOriginFilter依赖jetty-servlets.jar。
参数解释
-
allowedOrigins: 设置允许的来源(Origin)。可以是具体的 URL,或者使用
*
表示允许所有来源。为了安全,生产环境中建议指定具体的域。 -
allowedMethods: 定义允许的 HTTP 方法(如 GET, POST, OPTIONS 等)。
-
allowedHeaders: 设置允许的请求头。如果需要处理自定义头部,可以在这里添加。
-
allowCredentials: 表示是否允许发送 Cookie 等凭证信息。如果为
true
,则allowedOrigins
不能是*
,需要指定具体的域。 -
preflightMaxAge: 设置浏览器在发送下一个预检请求之前缓存 OPTIONS 请求的时间。
方法三. 自定义 AccessControlFilter
实现
除了配置CrossOriginFilter,还可以自定义 ContainerResponseFilter
的实现类。只要确保自定义类处理了 CORS 请求的核心逻辑即可,下面的代码是定义了一个实现类AccessControlFilter:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
@Provider
public class AccessControlFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
// 设置允许的来源
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*"); // 可以根据需求修改为具体域名
// 设置允许的HTTP方法
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS, HEAD");
// 设置允许的请求头
responseContext.getHeaders().add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept, Origin");
// 是否允许凭证
responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
// 预检请求的缓存时间
responseContext.getHeaders().add("Access-Control-Max-Age", "1209600"); // 14天
}
}
如何使用 AccessControlFilter
-
如果使用的是 Java 配置类,代码如下:
import org.glassfish.jersey.server.ResourceConfig;
import javax.ws.rs.ApplicationPath;
@ApplicationPath("/api")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("你的包名");
register(AccessControlFilter.class);
}
}
-
如果使用
web.xml,
在web.xml
中配置<servlet> <servlet-name>jersey-servlet</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>你的包名</param-value> </init-param> <init-param> <param-name>jersey.config.server.provider.classnames</param-name> <param-value>com.example.AccessControlFilter</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jersey-servlet</servlet-name> <url-pattern>/api/*</url-pattern> </servlet-mapping>
处理 OPTIONS 请求
CORS 预检请求通常使用 OPTIONS 方法。在 AccessControlFilter
中添加对 OPTIONS 请求的处理,以确保预检请求能够成功返回。
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.core.Response;
import java.io.IOException;
@Provider
public class AccessControlFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
// 设置 CORS 响应头
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS, HEAD");
responseContext.getHeaders().add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept, Origin");
responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
// 处理 OPTIONS 预检请求
if (requestContext.getMethod().equalsIgnoreCase("OPTIONS")) {
responseContext.setStatusInfo(Response.Status.OK);
}
}
}
如果不想处理options请求, 可以通过在web服务器侧,配置正向和反向代理,将后端的url都映射成一个apache侧的url。具体实现方法可参考对应的web服务器,这里就不再赘述了。