前后端分离跨域问题

后端配置跨域过滤器

/** imports **/

/**
 * 跨域过滤器 (解决浏览器访问跨域问题)
 *
 * @author LPxz
 * @since 2021/01/14
 */
// @Order里边的数字越小代表越先被该Filter过滤,@WebFilter代表这是个Filter类并把这个类注入到容器中
@Order(1)
@WebFilter(filterName = "oneFilter", urlPatterns = "/*")
public class CorsFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletResponse response = (HttpServletResponse) res;
    HttpServletRequest request = (HttpServletRequest) req;

    // 不使用*,自动适配跨域域名,避免携带Cookie时失效
    String origin = request.getHeader("Origin");
    if (!StringUtils.isEmpty(origin)) {
      response.setHeader("Access-Control-Allow-Origin", origin);
    }

    // 自适应所有自定义头
    String headers = request.getHeader("Access-Control-Request-Headers");
    if (!StringUtils.isEmpty(headers)) {
      response.setHeader("Access-Control-Allow-Headers", headers);
      response.setHeader("Access-Control-Expose-Headers", headers);
    }

    // 允许跨域的请求方法类型
    response.setHeader("Access-Control-Allow-Methods", "*");
    // 预检命令(OPTIONS)缓存时间,单位:秒
    response.setHeader("Access-Control-Max-Age", "3600");
    // 明确许可客户端发送Cookie,不允许删除字段即可
    response.setHeader("Access-Control-Allow-Credentials", "true");

    chain.doFilter(request, response);
  }

  @Override
  public void init(FilterConfig filterConfig) {

  }

  @Override
  public void destroy() {
  }

  /*
    注册过滤器:
    @Bean
    public FilterRegistrationBean registerFilter() {
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>();
        bean.addUrlPatterns("/*");
        bean.setFilter(new CorsFilter());
        // 过滤顺序,从小到大依次过滤
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
     */
}

注意还要在启动类中加入注解 @ServletComponentScan

@SpringBootApplication
@ServletComponentScan
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

fetch 方式

在前端修改请求头,然后在后端加上 @CrossOrigin 注解,可直接加在方法上,加在类上对应所有方法。

import React, {Component} from 'react'

class Page1 extends Component{
  constructor(props){
    super(props);
    this.state = {
      mytext: []
    }
  }
  getData(){
    //修改请求头
    let myHeaders = new Headers({
      'Access-Control-Allow-Origin': '*',
      'Content-Type': 'text/plain'
    });
    fetch('http://127.0.0.1:8080/hello',{
      method:'GET',
      headers: myHeaders,
      mode: 'cors',
      //转或称字符串格式
    }).then(res=>res.json()).then(
      data=>{
        console.log(data);
        // eslint-disable-next-line
        data.map((key)=>{
          this.setState({
            mytext: [...this.state.mytext, key]
          })
        })
      }
    )
  }
  //React周期函数,防止死循环,在组建被渲染前调用
  componentWillMount(){
    this.getData();
  }
  render() {
    return (
      <React.Fragment>
        <ul>
          {
            this.state.mytext.map((key, index)=>{
              return <li key={index}>{key.name}</li>
            })
          }
        </ul>
      </React.Fragment>
    )
  }
}
export default Page1;
import com.jarvis.springboot_mybatis.Entity.User;
import com.jarvis.springboot_mybatis.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import java.util.List;

@Controller
@CrossOrigin
public class MybatisController {

  @Autowired
  private UserMapper userMapper;

  @RequestMapping(value = "/hello", method = RequestMethod.GET)
  @ResponseBody
  public List<User> hello(){
    List<User> user = userMapper.queryUserList();
    System.out.println(user);
    return user;
  }
}

XMLHttrRequest 方式

这种方式在前端不用配置,需要在后端设置请求头,请求的时候需要将 localhost 改成 127.0.0.1,要不然因为不识别会报错,setResponse 需要和 Origin 保持一致(F12)。

true 和 false 那是发不发送 cookie,自行解决。

import React, {Component} from 'react'

class Page2 extends Component{
  constructor(props){
    super(props)
    this.state = {
      service: [],
    }
  }
  componentDidMount() {
    //ajax方法去服务器取数据,异步true
    const xhr = new XMLHttpRequest()
    xhr.open("GET", "http://127.0.0.1:8080/Page22", true)
    //根据情况选择是否要随get请求发送用于身份认证的信息
    xhr.withCredentials = false
    xhr.send()  
    xhr.onreadystatechange = () =>{
      /**
         *   0 UNSENT- open()尚未被调用
             1 OPENED- send()尚未被调用
             2 HEADERS_RECEIVED- send()已被调用,并且头和状态可用
             3 LOADING下载;- responseText的保持部分数据
             4 - 的操作完成
         */
      // eslint-disable-next-line
      if (xhr.readyState == XMLHttpRequest.DONE) {
        // eslint-disable-next-line
        if (xhr.status == 200) {
          let gotService = JSON.parse(xhr.responseText)
          this.setState({
            service: gotService
          })
        }
      } else {
        console.log(xhr.readyState + "---加载中")
      }
    }
  }

  render() {
    const serviceShows = this.state.service.map((user, index)=>{
      return (
        <tr key={index}>
          <td>{user.id}</td>
          <td>{user.username}</td>
          <td>{user.name}</td>
        </tr>
      )
    })
    return (
      <table>
        <tbody>
          {serviceShows}
        </tbody>
      </table>
    )
  }
}
export default Page2;
import com.jarvis.springboot_mybatis.Entity.User;
import com.jarvis.springboot_mybatis.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import javax.servlet.http.HttpServletResponse;

import java.util.List;

@Controller
@CrossOrigin
public class MybatisController {
  @Autowired
  private UserMapper userMapper;

  @RequestMapping(value = "/Page22", method = RequestMethod.GET)
  @ResponseBody
  public List<User> helloPage2(HttpServletResponse response){
    List<User> user = userMapper.queryUserList();
    System.out.println(user);
    // response.setHeader("Access-Control-Allow-Credentials","True");
    response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:3000");
    response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
    return user;
  }
}

前后端分离跨域问题
http://lpxz.work/posts/42396/
作者
LPxz
发布于
2022年4月22日
许可协议