Java Web应用中支持跨域请求

第一篇解决办法:

1,JavaScript由于安全性方面的考虑,不允许页面跨域调用其他页面的对象,那么问题来了,什么是跨域问题?

答:这是由于浏览器同源策略的限制,现在所有支持JavaScript的浏览器都使用了这个策略。那么什么是同源呢?所谓的同源是指三个方面“相同”:
1,域名相同
2,协议相同
3,端口相同

2,下面就举几个例子来帮助更好的理解同源策略。
URL 说明 是否允许通信

 http://www.a.com/a.js
 http://www.a.com/b.js 同一域名 允许

 http://www.a.com/a.js 
 http://www.b.com/a.js  不同域名    不允许

 http://www.a.com:8000/a.js
 http://www.a.com/b.js  同一域名不同端口    不允许

 https://www.a.com/a.js 
 http://www.a.com/b.js  同一域名不同协议    不允许

3,在JAVA中处理跨域问题,通常有以下两种常用的解决方法。
1,第一种解决方法
后台代码在被请求的Servlet中添加Header设置:

response.setHeader("Access-Control-Allow-Origin", "*");
PrintWriter out =null;
try
{
out = response.getWriter();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
out.print("{'status':'ok'}");
out.flush();
out.close();

Access-Control-Allow-Origin这个Header在W3C标准里用来检查该跨域请求是否可以被通过,如果值为*则表明当前页面可以跨域访问。默认的情况下是不允许的。

在前端JS中需要向Servlet发出请求,请求代码如下所示:

    $.ajax({
              url: "your url",
              type:"get or post",
              dataType:"json",
              data:{
                          ....
              },
              success:function(data){
                             ...
             }

第二种解决方法
通过jsonp跨域请求的方式。JSONP和JSON虽然只有一个字母的区别,但是他们完全就是两回事,很多人很容易把他们搞混。JSON是一种数据交换的格式,而JSONP则是一种非官方跨域数据交互协议。
首先来说一下前端JS是怎么发送请求。代码如下所示:

 $.ajax({
           url:"your url",
           type:"get or post",
           async:false,
           dataType : "jsonp",
           //服务端用于接收callback调用的function名的参数
           jsonp:"callbackparam",
           //callback的function名称
           jsonpCallback:"success_jsonpCallback",
           success:function(data){
                    console.log(data);
           },
           error:function(data){
                    console.log(data);
           }
 });

这里的callbackparam和success_jsonpCallback可以理解为发送的data数据的键值对,可以自定义,但是callbackparam需要和后台约定好参数名称,因为后台需要获取到这个参数里面的值(即success_jsonpCallback)。

4,下面,最重要的来了,后台怎么样获取和返回数据呢。代码如下所示:

PrintWriter out =null;
String callback=req.getParameter("callbackparam");
String json=callback+"({'status':'ok'})";
try
{
out = resp.getWriter();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
out.print(json);
out.flush();
out.close();

首先需要获取参数名为callbackparam的值,这里获取到的值就是“success_jsonpCallback”。然后将这个值加上一对小括号。小括号里放入你需要返回的数据内容,比如这里我返回一个JSON对象。当然你也可以返回其他对象,比如只返回一个字符串类型数据也可以。最后前端JS返回的数据就是这样的:

success_jsonpCallback({'status':'ok'})

浏览器会自动解析为json对象,这时候你只需要在success回调函数中直接用data.status就可以了。

第二篇解决办法:

由于工程合作开发的需要,后台的应用要能支持跨域访问,但是在这个跨域访问“时好时坏”,我们这帮屌丝所知道的就是加上两个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。

如:java类filter:

public class CorsFilter implements Filter{

 

@Override

public void init(FilterConfig filterConfig) throws ServletException {

// TODO Auto-generated method stub

 

}

 

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,

ServletException {

// TODO Auto-generated method stub

HttpServletResponse res = (HttpServletResponse) response;

res.setContentType("text/html;charset=UTF-8");

   res.setHeader("Access-Control-Allow-Origin", "*");

   res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");

   res.setHeader("Access-Control-Max-Age", "0");

   res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");

   res.setHeader("Access-Control-Allow-Credentials", "true");

   res.setHeader("XDomainRequestAllowed","1");

   chain.doFilter(request, response);

}

 

@Override

public void destroy() {

// TODO Auto-generated method stub

 

}

 

}

 

业务项目中的web.xml配置如下:

<filter>

 <filter-name>cors</filter-name>

 <filter-class>com.tianlong.common.base.CorsFilter</filter-class>

</filter>

<filter-mapping>

 <filter-name>cors</filter-name>

 <url-pattern>/*</url-pattern>

 

</filter-mapping>


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";
}
第三篇实现办法:

1、nginx配置文件配置:

server {  

    location / {  

        if ($request_method = 'OPTIONS') {  

          add_header 'Access-Control-Allow-Origin' '*';  

          add_header 'Access-Control-Allow-Credentials' 'true';  

          add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';  

          add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';  

         # add_header 'Access-Control-Max-Age' 3600;  

          add_header 'Content-Type' 'text/plain charset=UTF-8';  

          add_header 'Content-Length' 0;  

          return 200;  

        }  

}  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值