不使用反向代理解决跨域问题,含前后端代码及详细注解(axios+springboot)

78 阅读2分钟

一. 前端请求代码

import axios from "axios";

const method = 'post';

axios.request({
    headers:{
        /**
		 * 支持 text/plain、multipart/form-data、application/x-www-form-urlencoded
         * 根据不同场景使用,非文件流使用text/plain,文件流使用multipart/form-data
		 */
        "Content-Type": "text/plain;charset=UTF-8",
        /**
         * 传输cookie时设置为true,否则设置为false
         * 令牌等数据建议使用cookie传输
         */
        "withCredentials": true
    },
    /**
     * 支持 get、post、head
     * get请求参数放在params,post请求参数放在data
     */
    method,
    params: method != 'post' ? { a: 1 } : null,
    data: method == 'post' ? JSON.stringify({ a: 1 }) : null,
    //适用json传输数据
    responseType: 'json'
}).then(res => {
    if(res.status == 200) {
        console.log('success', res);
    } else {
        console.log('error', res);
    }
}).catch(err => {
    console.log('fail', err);
})

二. 前端令牌处理

import Cookies from 'js-cookie'

const tokenValue = ''; //使用你的代码获取令牌

Cookies.set('Authorization', tokenValue, { 
    // cookie 全局访问
    path: '/' 
})

三. 后端跨域处理

  1. 实现跨域处理的拦截器
package cors.test.interceptors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

// 实现跨域处理的拦截器
public class CORSInterceptor extends HandlerInterceptorAdapter {
    //日志记录
    private static final Logger LOGGER = LoggerFactory.getLogger(CORSInterceptor.class);

    private static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
    private static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";

    private static final String NULL_ORIGIN = "null";

    //测试地址处理,加上自己的内网ip
    private Set<String> exactMatches = new HashSet<>(Arrays.asList(
            "127.0.0.1", "localhost"
    ));
    
    //网站域名处理,加上前端域名,一般加上根域名即可,如果有需要更严格限制只添加对应子域名
    private Set<String> containingMatches = new HashSet<>(Arrays.asList(
            "test.com"
    ));

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取来源地址
        String origin = request.getHeader(HttpHeaders.ORIGIN);
        if(NULL_ORIGIN.equals(origin)) {
            origin = null;
        }
        //如果来源为空(非引用页面)或者命中允许的域名则设置允许跨域
        if (StringUtils.isEmpty(origin) || match(origin)) {
            setResponseHeaders(response, origin);
            return true;
        }
        //记录非法访问
        LOGGER.warn("illegality request origin:" + origin);
        return false;
    }

    private void setResponseHeaders(HttpServletResponse response, String origin) {
        if (StringUtils.isEmpty(origin)) {
            return;
        }
        //设置允许跨越
        response.setHeader(RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, origin);
        response.setHeader(RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.TRUE.toString());
    }

    private boolean match(String origin) {
        try {
            URI originUri = new URI(origin);
            //对于ip忽略端口号
            if (exactMatches.contains(originUri.getHost())) {
                return true;
            }
            //对于域名只匹配包含关系
            for (String containingMatch : containingMatches) {
                if (originUri.getHost().endsWith(containingMatch)) {
                    return true;
                }
            }
        } catch (URISyntaxException e) {
            LOGGER.warn("origin parse error:" + origin, e);
        }
        return false;
    }
}
  1. 注册拦截器
package cors.test.web;

import cors.test.interceptors.CORSInterceptor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// 添加web配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加cors拦截器,最好放在拦截器首位保证前端能收到反馈信息
        registry.addInterceptor(new CORSInterceptor());
    }
}

三. 后端请求处理样例

package cors.test.web.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSON;

@RestController
@RequestMapping("/cors/test")
public class CORSTestController {

    @PostMapping("/post")
    public CommonResponse post(@RequestBody RequestData request) {
        return CommonResponse.success(JSON.toJSONString(request));
    }

    @GetMapping("/get")
    public CommonResponse get(RequestData request) {
        return CommonResponse.success(JSON.toJSONString(request));
    }
    
    //文件上传
    @RequestMapping("/upload")
    public CommonResponse upload(@RequestParam("file") MultipartFile file) throws IOException {
        //文件流
        //file.getInputStream()
        return CommonResponse.success(file.getOriginalFilename());
    }
    
    public static class RequestData {
        private String a;

        //TODO 省略get/set方法
    }
    
    public static class CommonResponse {
        protected static final String SUCCESS = "SUCCESS";
        private String code;
        private String msg;

        protected CommonResponse(String code, String msg) {
            this.code = code;
            this.msg = msg;
        }

        public static CommonResponse success(String msg) {
            return new CommonResponse(SUCCESS, msg);
        }

        //TODO 省略get/set方法
    }
}