前后端分离跨域问题的总结

最近由于工作需要,需要java的后端api系统,在搭建过程中遇到了一些跨域问题, 在这里做个简单的总结,以便以后可以参考使用。

  1. 跨域的原因
  2. 我遇到的跨域问题
  3. 如何解决跨域问题
  4. spring Mvc 如何解决跨域
  5. springSecurity 如何解决跨域

1. 跨域的原因

    跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。
简单的来说就是浏览器栏上的ip,协议,端口和请求的后端ip,协议,端口不一致问题。

举例来说:

http://127.0.0.1:8080 可以调用http://127.0.0.1:8080/api的接口
http://127.0.0.1:8080 不可以调用https://127.0.0.1:8080/api的接口(协议不同)
http://127.0.0.1:8080 不可以调用http://10.0.10.151:8080/api的接口(IP不同)
http://127.0.0.1:8080 不可以调用http://127.0.0.1:8081/api的接口(端口P不同)`

2.我遇到的跨域问题

后端框架:springBoot + SpringSecurity + session
在搭建过程中发现有一下一些问题:
1.前端提示cors错误
2.前端无法set-cookie
3.前段无法读取到Response headers 中的自定义值

3.如何解决跨域问题

在http协议的response header 中有这样几个属性:

Access-control-Allow-Origin:设置允许访问域
Access-control-Allow-Methods:设置允许访问的方法
Access-control-Allow-Header:设置允许访问的请求头
Access-Control-Expose-Headers:设置允许访问的响应头

所以在reponse headers 中写入以下配置就可以解决上述问题1和问题3.

Access-control-Allow-Origin: *
Access-control-Allow-Methods: POST,GET,OPTIONS,DELETE,PUT
Access-control-Allow-Header: token
Access-Control-Expose-Headers: token

这里的token是自定义的header属性名称,不同的项目属性值不同,没有可以不做设置。

关于上述问题2,在尝试了几种方式后都没有很好的解决,所以就将sessionId信息写入到了自定义的header(token)
中返回给前端

4.spring Mvc 如何解决跨域

如果想在Spring mvc项目中设置上述属性,可以加一个Filter


    package cn.web.filter;

    import javax.servlet.*;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    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;
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "token");
            response.setHeader("Access-Control-Expose-Headers", "token");
            filterChain.doFilter(servletRequest, servletResponse);
        }
        @Override
        public void destroy() {
        }
    }

不要忘记在web.xml中添加filter相关配置

    <filter>
        <filter-name>crossorigin</filter-name>
        <filter-class>cn.web.filter.CorsFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>crossorigin</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

5.springSecurity 如何解决跨域

  • 方法1:

参考spring Mvc的经验,我们也可以加一个filter

    package com.web.filter;

    import lombok.extern.slf4j.Slf4j;

    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    @Slf4j
    //@Component
    public class MyCorsFilter implements Filter {

        // @Override
        protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Expose-Headers", "token");
            //Access-Control-Allow-Origin
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Lamp-App-Token, content-type");
            filterChain.doFilter(servletRequest, servletResponse);
        }
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            Filter.super.init(filterConfig);
        }
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            doFilterInternal((HttpServletRequest)request,(HttpServletResponse)response,chain);
        }
    
        @Override
        public void destroy() {
            Filter.super.destroy();
        }
    }

将Filter 加入到配置中

    package com.cetc.clp.framework.auth;

    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FilterConfig {
    
        @Bean
        public FilterRegistrationBean<MyCorsFilter> someFilterRegistration() {
            FilterRegistrationBean<MyCorsFilter> registration = new FilterRegistrationBean<>();
            registration.setFilter(sessionFilter());
            registration.addUrlPatterns("/*");
            registration.setName("MyCorsFilter");
            registration.setOrder(Integer.MIN_VALUE + 50);
            return registration;
        }
    
        @Bean
        public MyCorsFilter sessionFilter(){
            return new MyCorsFilter();
        }
    }
  • 方法2:

    这里还有一种方法是基于spring security框架.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       ... 
      http.cors().configurationSource(configurationSource());
     ...
    }

    @Bean
	public CorsConfigurationSource configurationSource() {
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

		CorsConfiguration config = new CorsConfiguration();
		//config.setAllowCredentials(true);
		//config.setAllowedOriginPatterns(Collections.singletonList("*"));
		config.addAllowedOrigin("*"); //可以放开上面两行,删除这一行,也可以解决跨域问题
		//设置允许访问的方法
		config.setAllowedMethods(Arrays.asList("POST", "OPTIONS", "GET", "DELETE", "PUT"));
		//设置request header允许设置的属性值
		config.addExposedHeader("token");
		config.setAllowedHeaders(Arrays.asList("token"));
        source.registerCorsConfiguration("/**", config);
		return source;
	}

总结:网上有很多相关代码来解决跨域问题,其实不管怎么变化(不包括nigix解决域名的方法),最后都是在reponse header中写入上面几个参数。所以在遇到跨域问题时候,可以检查下代码中是否有以上几个参数设置。

上一篇:过滤器(Filter)


下一篇:使用FFMPEG类库分离出多媒体文件中的H.264码流