Java Web应用中支持跨域请求

时间:2024-06-08 10:03:56

转载:https://blog.****.net/lmy86263/article/details/51724221

由于工程合作开发的需要,后台的应用要能支持跨域访问,但是在这个跨域访问“时好时坏”,我们这帮屌丝所知道的就是加上两个jar包,然后声明一下Filter,感觉很简单的有没有!!感觉自己很牛X有没有!!全是幻觉!!要不然怎么会时好时坏!!为了深入了解这个问题,决定写这篇文章总结一下。 
要知道跨域请求就要先了解同源策略,那么什么是同源?什么是不同源?简单来说就是,如果两个资源,包括HTML页面、JavaScript脚本、css样式,对应的协议、域名和端口完全相同,那么这两个资源就是同源的,Same-origin policy解释得很清楚。那么同源策略的意思就是一个源中的资源访问另外一个源中的资源,在在这一点上JavaScript的跨站资源访问表现的更加明显。在HTML5之前Ajax是不允许发起跨站请求的,如果有需求的话,可以使用JSONP等方法,但是缺点就是:

  • 只支持Get不支持Post;
  • 本质上是脚本注入的方式,存在安全隐患;

还有JSONP的优缺点,但是自从HTML5出现之后,提出了CORS(跨站资源共享)这种方式,极大地方便了日常的开发。如果要理解CORS的工作原理,首先要知道跨域访问是怎么被禁止的,之前本屌丝一直以为是前台的跨域访问请求不能发出去,是实现同源策略的浏览器拦截了该请求,但是后来才知道浏览器并没有拦截请求,而是拦截了服务器端返回的响应。 
所以如果要支持跨域访问,需要浏览器和后台服务器程序同时支持,如果这两个条件不能同时满足,则还是不能支持跨域访问。

用于CORS中的Http的首部有如下几个:

  • 响应头

    • Access-Control-Allow-Origin: 允许跨域访问的域,可以是一个域的列表,也可以是通配符”*”;
    • Access-Control-Allow-Methods: 允许使用的请求方法,以逗号隔开;
    • Access-Control-Allow-Headers: 允许自定义的头部,以逗号隔开,大小写不敏感;
    • Access-Control-Expose-Headers: 允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息
    • Access-Control-Allow-Credentials: 是否允许请求带有验证信息,XMLHttpRequest请求的withCredentials标志设置为true时,认证通过,浏览器才将数据给脚本程序。
    • Access-Control-Max-Age: 缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数;
  • 请求头

    • Origin: 普通的HTTP请求也会带有,在CORS中专门作为Origin信息供后端比对,表明来源域,要与响应头中的Access-Control-Allow-Origin相匹配才能进行跨域访问;
    • Access-Control-Request-Method: 将要进行跨域访问的请求方法,要与响应头中的Access-Control-Allow-Methods相匹配才能进行跨域访问;
    • Access-Control-Request-Headers: 自定义的头部,所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中,要与响应头中的Access-Control-Allow-Headers相匹配才能进行跨域访问

从支持跨域访问的范围说,可以有整个服务器、单个应用程序、单个接口。

1、在整个服务器上支持跨域访问

在服务器上可以部署多个应用程序,如果在整个服务器的范围上支持跨域访问,那么在所有应用程序上都不用单独配置了,直接使用服务器的配置即可,这里通过tomcat来进行举例。在Tomcat7之后包括tomcat7才开始支持CORS,之前的版本是不支持的。配置CORS,首先配置Tomcat中的conf\web.xml,在其中添加一个Filter声明,如下:

<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

这样就能应用到在该Tomcat中部署的所有的应用程序的接口上。然后在Tomcat的lib文件夹下加入两个jar包:cors-filter-2.5.jar和java-property-utils-1.9.jar,这两个jar包对应的maven依赖如下:

<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.5</version>
</dependency>

在Tomcat服务器上配置完成后,在自己的应用程序上就不要再配置有关跨域访问的内容了,这样会造成访问相应的接口时不支持跨域访问。 
这种配置方式覆盖面太广,有些部署在该服务器下的应用程序根本不需要支持跨域访问,就会带来一些安全问题,所以其实不推荐使用这种配置方式。

2、在整个后台应用上支持跨域访问

在这个应用程序的范围内支持跨域访问是比较常见的使用方法,而且这种配置有很多实现方式。

2.1、在web.xml中配置

将在上述服务器中的web.xml上配置的filter,配置到自己的工程中。然后加入maven依赖,如下类似:

<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.5</version>
</dependency>

这样就完成了对跨域访问请求的支持,如果并不想对所有的请求都支持跨域访问,则可以在Filter的url-pattern中改变匹配到的url地址。

2.2、通过WebMvcConfigurer ###

在Spring中,使用这个接口可以通过定义回调方法来进行一些Spring MVC中要用到的配置,在里面用来支持CORS的方法是addCorsMappings(CorsRegistry registry),我们并不直接使用这个接口而是使用它的抽象实现类WebMvcConfigurerAdapter,这个类中给我们提供了WebMvcConfigurer接口中方法的空实现,我们可以直接填上自己的业务逻辑就可以直接使用。在这个回调方法中的参数是CorsRegistry,这个类可以帮助我们为相应的url地址提供CORS配置,关于这个方法可以放到Application启动类中,也可以单独放到一个类中。

在Application启动类中使用该方法,只需要在Application启动类中加入如下代码即可。

@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}

独自建立新类使用该方法,则需要配置一些注解,如下:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.springboot.demo" })
public class WebConfig extends WebMvcConfigurerAdapter { @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/*").allowedOrigins("http://localhost:8070");
} }

与在Application启动类中使用相比多了@Configuration@EnableWebMvc注解,这是因为@SpringBootApplication已经包含上述两个注解了,所以不需要重新加入。 
CorsRegistry中调用addmapping后会得到CorsRegistration类,这时候就可以使用链式调用可以对这个CORS配置进行origin、method、header、maxage等的限制,这里就不展开了。

2.3 自定义Filter

所有的方法归根结底就是拦截对接口的访问,所以如果你不想麻烦并且想深入了解CORS到底是怎样工作的,可以通过自定义Filter来实现,其实这也没有什么难的,就是在拦截器上通过对请求和响应加上一些Headers,这里就不说了。

3、在单个接口上支持跨域访问

跨域访问所支持的最小的范围就是在Controller以及对应的方法上,这里使用@CrossOrigin注解来完成相应的配置,首先来看看这个注解的源代码。

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
String[] DEFAULT_ORIGINS = { "*" };
String[] DEFAULT_ALLOWED_HEADERS = { "*" };
boolean DEFAULT_ALLOW_CREDENTIALS = true;
long DEFAULT_MAX_AGE = 1800; String[] value() default {};
@AliasFor("value")
String[] origins() default {};
String[] allowedHeaders() default {};
String[] exposedHeaders() default {};
RequestMethod[] methods() default {};
String allowCredentials() default "";
long maxAge() default -1;
}

从该注解中可以看到,它可以使用在方法上,也可以使用在类上,并且已经完全覆盖了CORS提出的响应头首部。

  • 使用在类上,一般使用在有@Controller或者@RestController注解的类上,这样在该类上全部遵从该注解提供的关于CORS的配置。
  • 使用在方法上,该CORS的配置只是针对该指定的方法;

默认情况下,@CrossOrigin支持所有的origin和所有的headers来进行跨域访问,而且它所限制的方法是由@Requestmapping中的method属性来提供的。 
如果查看源码的话是可以发现的,在CorsRegistry中使用addMapping()配置映射时,返回的CorsRegistration类中对origin、header、Credentials的配置使用的就是@CrossOrigin的默认配置。实例如下:

@CrossOrigin(origins={"http://localhost:8070"}, methods={RequestMethod.GET, RequestMethod.POST})
@RequestMapping("/")
@ResponseBody
public String getInformation(){
restHandleService.getRestInformation();
return "spring boot";
}