1. 程式人生 > 其它 >學習記錄370@ ssm中 加了攔截器,跨域請求配置就不起作用的解決辦法

學習記錄370@ ssm中 加了攔截器,跨域請求配置就不起作用的解決辦法

注意ssm採用全註解的方式配置的
在這裡插入圖片描述

原攔截器和跨域配置

攔截器類

package com.qianfeng.controller.handler;

import com.qianfeng.common.BusinessException;
import com.qianfeng.common.Constant;
import com.qianfeng.common.ResultEnum;
import com.qianfeng.dao.UserDao;
import com.qianfeng.pojo.vo.UserVo;
import com.qianfeng.utils.CurrentUserUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.
http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.lang.reflect.Method; import java.util.List; @Component//攔截器類,交給spring管理 public class LoginInterceptor implements HandlerInterceptor { @Autowired UserDao userDao; @Override public
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); //判斷之前登入存放的session是否存在 Object attribute = session.getAttribute(Constant.User.LOGIN_KEY); if (attribute == null) {//未登入 throw new BusinessException(ResultEnum.NO_LOGIN); } UserVo userVo = (UserVo) attribute; //將id設定進threadlocal,後面記錄日誌的AOP要用,也就是操作人 CurrentUserUtil.setId(userVo.getId()); HandlerMethod handler1 = (HandlerMethod) handler; Method method = handler1.getMethod(); if (method.isAnnotationPresent(NeedPerm.class)) { // 許可權判斷,沒有許可權就拋給前端一個特定的code,前端自己處理 List<String> permUrls = userDao.findPermsByUserId(userVo.getId()); if (!permUrls.contains(request.getRequestURI())) { throw new BusinessException(ResultEnum.NO_PERMISION); } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { CurrentUserUtil.remove(); } }

配置攔截器和跨域允許

package com.qianfeng.config;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.qianfeng.controller.handler.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.*;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;

//@Configuration//也可以交予spring管理
//核心@EnableWebMvc,implements WebMvcConfigurer
@EnableWebMvc
@ComponentScan({"com.qianfeng.controller"})
public class SpringmvcConfig implements WebMvcConfigurer {

    //    新增攔截器
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
//        註冊攔截器,新增攔截的路徑,新增放行的路徑
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**")
                .excludePathPatterns("/login", "/swagger-ui.html", "/webjars/**", "/swagger/**", "/swagger-resources/**");
    }
    
    @Override//解決跨域  ip  請求方式
    public void addCorsMappings(CorsRegistry registry) {
        // 設定允許跨域的路徑
        registry.addMapping("/**")
                // 設定允許跨域請求的域名
                .allowedOrigins("*")
                // 是否允許證書 cookie
                .allowCredentials(true)
                // 設定允許的方法
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                // 設定允許的header屬性
                .allowedHeaders("*")
                // 跨域允許時間
                .maxAge(3600);
    }
}

以上方式有了攔截器後,跨域請求配置就失效了,需要從新進行跨域的配置,可以採用在過濾器中配置跨域,這個在啟動類中配置

package com.qianfeng.init;

import com.qianfeng.config.MainConfig;
import com.qianfeng.config.SpringmvcConfig;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

//啟動伺服器就載入這個類,核心是servletContext,applicationContext
//ServletContext是最大的域物件,隨著伺服器的開啟而開啟,銷燬而銷燬
//用來載入配置檔案,初始化,然後都在spring的管理之下了
public class MyWebApplicationInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        //幫載入父子容器
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        //載入sm配置檔案
        applicationContext.register(MainConfig.class);
        //載入springmvc配置檔案
//        這裡也可以交給spring管理,但是最好讓spring管理service和mybatis即可,springmvc管理controller層
        applicationContext.register(SpringmvcConfig.class);

        //加一個編碼過濾 中文亂碼,之前的過濾器是在web.xml中
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        //放入tomcat容器
        FilterRegistration.Dynamic characterEncodingFilter1 = servletContext.addFilter("characterEncodingFilter", characterEncodingFilter);
        //filter攔截的路徑是/*
        characterEncodingFilter1.addMappingForUrlPatterns(null, true, "/*");

//        以下五行程式碼配置跨域過濾器,
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig());
        CorsFilter corsFilter = new CorsFilter(source);
        FilterRegistration.Dynamic corsFilter1 = servletContext.addFilter("corsFilter", corsFilter);
        corsFilter1.addMappingForUrlPatterns(null, true, "/*");

        //springmvc能啟動 中央控制 dispatchServlet
        ServletRegistration.Dynamic dispatchServlet = servletContext.addServlet("dispatchServlet", new DispatcherServlet(applicationContext));
        dispatchServlet.addMapping("/");
        dispatchServlet.setLoadOnStartup(1);
    }

    //    跨域過濾器方法
    private CorsConfiguration corsConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        /* * 請求常用的三種配置,*代表允許所有,當時你也可以自定義屬性(比如header只能帶什麼,只能是post方式等等)
         */
        corsConfiguration.addAllowedOrigin("*");//域名
        corsConfiguration.addAllowedHeader("*");//所有的請求頭
        corsConfiguration.addAllowedMethod("*");//post get  put delete patch
        corsConfiguration.setAllowCredentials(true);//允許cookie放過
        corsConfiguration.setMaxAge(3600L);
        return corsConfiguration;
    }
}

原因

參考:
DispatchServlet.doDispatch()方法是SpringMVC的核心入口方法,經過分析發現所有的攔截器的preHandle()方法的執行都在實際handler的方法(比如某個API對應的業務方法)之前,其中任意攔截器返回false都會跳過後續所有處理過程。而SpringMVC對預檢請求的處理則在PreFlightHandler.handleRequest()中處理,在整個處理鏈條中出於後置位。由於預檢請求中不帶資料,因此先被許可權攔截器攔截。

原來CROS複雜請求時會首先發送一個OPTIONS請求做嗅探,來測試伺服器是否支援本次請求,請求成功後才會傳送真實的請求;而OPTIONS請求不會攜帶資料,導致這個請求被攔截了,直接返回了狀態碼,響應頭中沒攜帶解決跨域問題的頭部資訊,出現了跨域問題。