前后端分离之彻底解决跨域问题(AJAX)

随着互联网的发展,日常开发前后端分离越来越常见,但是问题也随之出现,比如跨域。
前两天项目就遇到了跨域问题,今天就为大家带了一个解决前后端跨域问题的解决办法。也算是给自己做个笔记。
首先跨域解决问题有很多,今天我们主要是基于java中的filter过滤器进行实现
项目环境基于:Springboot

在开始之前我们有必要了解一下java中的三种拦截器:
1:Filter(过滤器)
2:Listener(监听器)
3:Interceptor(拦截器)
如果想具体详解上面三种过滤器,那么请点击我
在这里我也简单概述一下:
过滤器(Filter):主要用来过滤url请求地址的,是系统级别的,可以过滤所有的web请求
监听器(Listener):会在程序启动而启动,充当一个观察者,会在指定事项执行之前或者执行之后进行特殊的处理,同样是系统级别的
拦截器(Interceptor):拦截器不是系统级别的,也就是没有上面两种强大,但是定位更加准确,并且是以动态代理的方式调用指定方法,可以控制active(service)层的方法的运行与否,类似于AOP技术
上面三种拦截器功能都比较类似,具体使用场景还是需要根据情况而定。

另外在开始前我们先说一下OPTIONS请求:
简单来说OPTIONS请求就是在POST请求进行调用接口的时候浏览器自动发出的请求
有兴趣的小伙伴可以点击:HTTP协议的八种请求类型
还可以点击这里:HTTP预请求OPTIONS
之所以需要提前讲一下,是因为在POST请求的时候浏览器会自动发送一次OPTIONS请求给浏览器,如果可以访问,浏览器才会发送真正的请求,所以我们会在Filter过滤器允许访问,但是这样就会造成服务器压力过重,因为每一次请求都会存在两次同样的请求(一次为OPTIONS请求),所以我们就使用过滤器又对OPTIONS请求进行拦截。

接下来步入正题,用java中的Filter实现对跨域的解决,使用Interceptor进行用户loginToken校验

  • 1:创建一个Filter过滤器
/**
 * @author west
 * @date 2018-5-17
 * 跨域过滤器
 *
 */
@Component
public class CorsFilter implements Filter {

    //过滤器的主要代码
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //类型转换
        HttpServletRequest req = WebUtils.toHttp(request);
        HttpServletResponse resp = WebUtils.toHttp(response);
        //调用跨域支持
        WebUtils.option(req, resp);
        //调用下一个过滤器
        chain.doFilter(request, response);
    }

    //容器生成执行的代码
    public void init(FilterConfig filterConfig) {}

    //容器销毁执行的代码
    public void destroy() {}

  • 2:创建我们的跨域的实现代码
/**
 * @Author:West
 * @Date: create in 2018/5/28
 */
public class WebUtils extends org.apache.shiro.web.util.WebUtils {


    //推送消息
    public static void pushMessage(ServletRequest request, ServletResponse response, String message){
        PrintWriter writer = null;
        OutputStreamWriter osw = null;
        response.setContentType("text/html;charset=UTF-8");
        try {
            osw = new OutputStreamWriter(WebUtils.toHttp(response).getOutputStream(),
                    "UTF-8");
            writer = new PrintWriter(osw, true);
            writer.write(message);
            writer.flush();
            writer.close();
            osw.close();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != writer) {
                writer.close();
            }
            if (null != osw) {
                try {
                    osw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 解决跨域问题
     * @param req 请求
     * @param resp 相应
     * @return 如果是option返回true
     */
    public static boolean option(HttpServletRequest req, HttpServletResponse resp) {
        //设置一些允许访问的浏览器的header格式
        resp.setHeader("Access-Control-Allow-Origin",req.getHeader("Origin"));
        resp.setHeader("Access-Control-Allow-Credentials", "true");
        resp.setHeader("Access-Control-Allow-Methods", "POST, OPTION, GET, PUT, DELETE");
        resp.setHeader("Access-Control-Max-Age", "3600");
        resp.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type,loginToken");
        //得到请求的格式
        String method = req.getMethod();
        //如果是OPTIONS格式
        if ("OPTIONS".equals(method)){
            resp.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE");
            resp.setStatus(HttpServletResponse.SC_OK);
            WebUtils.pushMessage(req,resp,"服务器响应");
            return true;

        }
        return false;
    }

    public static void setHeaderForDownload(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
        response.setContentType("application/octet-stream; charset=UTF-8");
        response.setHeader("Access-Control-Expose-Headers","Content-disposition");
        response.setHeader("Content-disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "ISO8859-1"));
    }
}

因为我们的项目是使用loginToken取代session的所以在这里我们需要对请求判断,所以使用拦截器,fileter当然也可以

  • 3:创建我们对loginToken的拦截器
public class Interceptor extends HandlerInterceptorAdapter {

    private static final BaseApiService baseApiService = new BaseApiService();

    //前置拦截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        System.out.println("=====================拦截器开始执行=====================");
        String method = request.getMethod();
        //不区分大消息
        if(method.equalsIgnoreCase("options")){
            System.out.println("该请求是potions不予处理");
            return false;
        }
        String loginToken = request.getHeader("loginToken");
        //没有登录
        if (StringUtils.isEmpty(loginToken)) {
            System.out.println("您还没有登录");
            return noLogin(baseApiService.setResultError("您还没有登录,不能访问"));
        }
        String str = null;
        try {
            str = JWTTokenUtil.parseJWT(loginToken);
        } catch (Exception e) {
            System.out.println("错误的loginToken格式");
            return noLogin(baseApiService.setResultError("错误的loginToken格式"));

        }
        Long outTime = JSON.parseObject(str).getLong("outTime");
        if (outTime <= System.currentTimeMillis()) {
            System.out.println("身份过期,请重新登录");
            return noLogin(baseApiService.setResultError("身份过期,请重新登录"));
        }
        System.out.println("身份验证成功");
        System.out.println("=====================拦截器执行完毕=====================");
        return true;
    }

	//没有正确的loginToken的统一处理
	//ResponseBase 自定义的同意返回格式
    public boolean noLogin(ResponseBase responseBase) {
        System.out.println("=====================拦截器执行完毕=====================");
        if (responseBase != null) {
            throw new CustomException(responseBase);
        }
        return false;
    }

    //当身份验证失败的时候就跳转到登录页面
    public boolean redirect(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("重定向的地址为:" + "127.0.0.1/user/goLogin");
        //重定向
        response.sendRedirect(request.getContextPath() + "/user/goLogin");
        return false;
    }
}
  • 4:配置我们的拦截器
@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurationSupport {

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        // addPathPatterns 用于添加拦截规则
        // excludePathPatterns 用户排除拦截
        //拦截所有/api/开头的请求,但是排除静态文件和用户登录等请求
        registry.addInterceptor(new Interceptor()).addPathPatterns("/api/**").excludePathPatterns("/static/images/**","/user/login","/user/goLogin");
        super.addInterceptors(registry);

    }
}

另外可以参考:https://blog.csdn.net/w_t_y_y/article/details/81333595(三种解决跨域方案)
好了经过上面四步简单的操作就实现了对跨域问题的解决和用户是否登录进行了处理和控制
有什么不足的地方欢迎大家指出,谢谢阅读

注意:其中的ResponseBase是自定义的接口统一返回格式,BaseApiService是对统一返回格式的实现接口

发布了25 篇原创文章 · 获赞 9 · 访问量 3067

猜你喜欢

转载自blog.csdn.net/qq_40053836/article/details/96580606
今日推荐