最近由于工作需要,需要java的后端api系统,在搭建过程中遇到了一些跨域问题, 在这里做个简单的总结,以便以后可以参考使用。
跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对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不同)`
后端框架:springBoot + SpringSecurity + session 在搭建过程中发现有一下一些问题: 1.前端提示cors错误 2.前端无法set-cookie 3.前段无法读取到Response headers 中的自定义值
在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)
中返回给前端
如果想在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>
参考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中写入上面几个参数。所以在遇到跨域问题时候,可以检查下代码中是否有以上几个参数设置。