014-Spring MVC处理CORS跨域

Spring MVC中处理CORS跨域

Spring MVC中处理CORS跨域有如下几种方式

  1. @CrossOrigin(注解)
  2. CorsRegistry(全局配置)
  3. SpringMVC拦截器
  4. 实现Filter接口

编写axios异步请求

当然JQuery或直接AJAX请求也可以

编写test.html文件如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./js/axios.min.js"></script>
</head>
<body>


<button onclick="checkCors()" type="button">CORS请求</button>

<script>

    axios.defaults.withCredentials=false // 不携带cookie
    axios.defaults.crossDomain=true
    axios.defaults.headers.post['Content-Type'] = 'application/json'

    var baseUrl = "http://localhost:8090";
    var token = '';
    var request = axios.create({
        baseURL: baseUrl, // api的base_url
        timeout: 50000, // request timeout
        data: {}
    });


    function checkCors() {

        request({
            url: '/demo6/checkCors',
            method: 'post',
            data : {
            }
        }).then(response => {
            console.log(response.data)
            token = response.data.content;
        });
    }

</script>

</body>
</html>

点击下载axios.min.js文件

编写测试Controller

package com.yyoo.springmvc.controller;

import com.yyoo.springmvc.bean.MyResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("demo6")
public class Demo6Controller {

    @RequestMapping("checkCors")
    public MyResponse checkCors(){
        return MyResponse.success("CORS请求成功!");
    }

}

测试请求

在这里插入图片描述

如图,我们在没有进行任何的跨域处理时,点击CORS请求按钮后会有两个请求发送,而且都有错误,而且它们的状态也不一样。第一个就是“预检”请求,第二个才是实际的请求。关于请求的状态以及相关的http知识不是我们本章的讨论内容。接下来我们来通过以上几种方式来处理跨域问题。想了解跨域的相关知识可查看CORS跨域介绍

@CrossOrigin(注解)解决跨域

package com.yyoo.springmvc.controller;

import com.yyoo.springmvc.bean.MyResponse;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("demo6")
public class Demo6Controller {

    @RequestMapping("checkCors")
    @CrossOrigin
    public MyResponse checkCors(){
        return MyResponse.success("CORS请求成功!");
    }

}

可见@CrossOrigin注解方式十分简单,在对应的方法上加上该注解即可。@CrossOrigin注解也可以作用在类上,让该类下的请求方法都支持跨域。

@CrossOrigin注解也可以设置对应的参数,直接查看源码即可了解。

package org.springframework.web.bind.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;

import org.springframework.core.annotation.AliasFor;
import org.springframework.web.cors.CorsConfiguration;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {

	/**
	 * Alias for {@link #origins}.
	 */
	@AliasFor("origins")
	String[] value() default {};

	/**
	 * A list of origins for which cross-origin requests are allowed. Please,
	 * see {@link CorsConfiguration#setAllowedOrigins(List)} for details.
	 * <p>By default all origins are allowed unless {@link #originPatterns} is
	 * also set in which case {@code originPatterns} is used instead.
	 */
	@AliasFor("value")
	String[] origins() default {};

	/**
	 * Alternative to {@link #origins} that supports more flexible origin
	 * patterns. Please, see {@link CorsConfiguration#setAllowedOriginPatterns(List)}
	 * for details.
	 * <p>By default this is not set.
	 * @since 5.3
	 */
	String[] originPatterns() default {};

	/**
	 * The list of request headers that are permitted in actual requests,
	 * possibly {@code "*"}  to allow all headers.
	 * <p>Allowed headers are listed in the {@code Access-Control-Allow-Headers}
	 * response header of preflight requests.
	 * <p>A header name is not required to be listed if it is one of:
	 * {@code Cache-Control}, {@code Content-Language}, {@code Expires},
	 * {@code Last-Modified}, or {@code Pragma} as per the CORS spec.
	 * <p>By default all requested headers are allowed.
	 */
	String[] allowedHeaders() default {};

	/**
	 * The List of response headers that the user-agent will allow the client
	 * to access on an actual response, other than "simple" headers, i.e.
	 * {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
	 * {@code Expires}, {@code Last-Modified}, or {@code Pragma},
	 * <p>Exposed headers are listed in the {@code Access-Control-Expose-Headers}
	 * response header of actual CORS requests.
	 * <p>The special value {@code "*"} allows all headers to be exposed for
	 * non-credentialed requests.
	 * <p>By default no headers are listed as exposed.
	 */
	String[] exposedHeaders() default {};

	/**
	 * The list of supported HTTP request methods.
	 * <p>By default the supported methods are the same as the ones to which a
	 * controller method is mapped.
	 */
	RequestMethod[] methods() default {};

	/**
	 * Whether the browser should send credentials, such as cookies along with
	 * cross domain requests, to the annotated endpoint. The configured value is
	 * set on the {@code Access-Control-Allow-Credentials} response header of
	 * preflight requests.
	 * <p><strong>NOTE:</strong> Be aware that this option establishes a high
	 * level of trust with the configured domains and also increases the surface
	 * attack of the web application by exposing sensitive user-specific
	 * information such as cookies and CSRF tokens.
	 * <p>By default this is not set in which case the
	 * {@code Access-Control-Allow-Credentials} header is also not set and
	 * credentials are therefore not allowed.
	 */
	String allowCredentials() default "";

	/**
	 * The maximum age (in seconds) of the cache duration for preflight responses.
	 * <p>This property controls the value of the {@code Access-Control-Max-Age}
	 * response header of preflight requests.
	 * <p>Setting this to a reasonable value can reduce the number of preflight
	 * request/response interactions required by the browser.
	 * A negative value means <em>undefined</em>.
	 * <p>By default this is set to {@code 1800} seconds (30 minutes).
	 */
	long maxAge() default -1;

}

CorsRegistry(全局配置)解决跨域

@CrossOrigin注解方式简单,但如果我们需要为我们的应用设置一个全局的CORS配置,@CrossOrigin就不太方便了。这时候我们可以通过CorsRegistry来配置

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/demo6/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "OPTIONS","POST")
                .allowCredentials(false).maxAge(3600);

        // 还能添加更多的 mappings...
    }
}

WebMvcConfigurer:我们添加的大部分配置都用到了它,目前为止我们用他添加过类型转换器、拦截器、以及现在的CORS全局配置。

SpringMVC 拦截器解决跨域

package com.yyoo.springmvc.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.addHeader("Access-Control-Allow-Origin", "*");
        // 允许请求携带cookie
        response.setHeader("Access-Control-Allow-Credentials", "false");
        response.addHeader("Access-Control-Max-Age","86400");
        // 响应类型
        response.addHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
        // 响应头设置
        response.addHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With, X-authentication, X-client, X-Token, X_Requested_With");
        return true;
    }
}

此方式在我们需要携带cookie的时候比较有用,因为携带cookie的时候需要Access-Control-Allow-Credentials为true,而且Access-Control-Allow-Origin不能为*(具体请查看上一篇文章:CORS跨域介绍)。如果我们需要运行跨域的请求Origin有多个怎么办?或者我们后端根本不知道Origin的值,需要每次从请求头中获取比对怎么办?在拦截器中或过滤器中我们就可以使用request对象进行一些编码来实现。

package com.yyoo.springmvc.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 指定允许其他域名访问,Access-Control-Allow-Credentials为true时,该值不能为*
        String origin = request.getHeader("origin");
        if(origin != null && checkOrigin(origin)) {
            response.addHeader("Access-Control-Allow-Origin", origin);
        }
        // 允许请求携带cookie
        response.setHeader("Access-Control-Allow-Credentials", "true");

        response.addHeader("Access-Control-Max-Age","86400");
        // 响应类型
        response.addHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
        // 响应头设置
        response.addHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With, X-authentication, X-client, X-Token, X_Requested_With");

        return true;
    }


    private boolean checkOrigin(String origin){
        List<String> originList = new ArrayList<>();
        originList.add("http://localhost:8090");
        originList.add("http://localhost:9090");
        // ...

        for(String o : originList){
            if(o.contains(origin)){
                return true;
            }
        }
        return false;
    }
}

过滤器解决跨域问题

@Component
public class CorsFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        // String origin = request.getHeader("Origin");
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
        response.setHeader("Access-Control-Max-Age", "86400");
        // 为true时Access-Control-Allow-Origin不能为*
        response.setHeader("Access-Control-Allow-Credentials", "false");
        // 此处为允许请求携带的所以请求头,如果要限制,可自行定义
        response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
        filterChain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}

注:在使用shiro的时候拦截器或者其他方式无法解决跨域的问题,这个时候使用过滤器可以解决。(具体可以参考shiro使用token认证

上一篇:CORS跨域介绍
下一篇:待续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值