• 跨域无效问题解决(java后端方案)


    通用后端跨域方法

    1、@CrossOrigin 注解

    在Spring Boot 中给我们提供了一个注解 @CrossOrigin 来实现跨域,这个注解可以实现方法级别的细粒度的跨域控制。我们可以在类或者方添加该注解,如果在类上添加该注解,该类下的所有接口都可以通过跨域访问,如果在方法上添加注解,那么仅仅只限于加注解的方法可以访问。

    @Slf4j
    @RestController
    @RequestMapping(value = AppPath.SERVICE_LOCATION_URL + "/appointment")
    @Api(value = "AppointmentController",tags = "预约列表接口")
    @CrossOrigin
    public class AppointmentController {
    
        @Autowired
        private LiveAppointmentService appointmentService;
        
        @RequestMapping
        @ApiOperation(value = "预约列表分页查询", response = CsLiveAppointmentDTO.class)
        public JsonResult<PageInfo> getAppointmentList(AppointmentListDTO dto){
            log.info("getAppointmentList vo:{}", JSONUtil.toJsonStr(dto));
            PageInfo<CsLiveAppointmentDTO> appointmentList = appointmentService.getAppointmentList(dto);
            return JsonResult.success(appointmentList);
        }
    }

    **@CrossOrigin 注解不生效问题**

    在Spring框架4.2版本后,Spring给出了注解的方式解决问题。

    即在Controller控制器中,在Controller注解上方添加@CrossOrigin注解。

    但是使用这种方式后也有可能仍然出现跨域问题,解决方案就是:

    **在@RequestMapping注解中没有指定Get、Post方式,或者使用@GetMapping或者@Post Mapping**

    **在@CrossOrigin(methods = {RequestMethod.POST})指定方法**

    @Slf4j
    @RestController
    @RequestMapping(value = AppPath.SERVICE_LOCATION_URL + "/appointment")
    @Api(value = "AppointmentController",tags = "预约列表接口")
    @CrossOrigin
    public class AppointmentController {
    
        @Autowired
        private LiveAppointmentService appointmentService;
    
        @ApiOperation(value = "预约列表分页查询", response = CsLiveAppointmentDTO.class)
        //@GetMapping("getList")
        @RequestMapping(method = RequestMethod.GET)
        public JsonResult<PageInfo> getAppointmentList(AppointmentListDTO dto){
            log.info("getAppointmentList vo:{}", JSONUtil.toJsonStr(dto));
            PageInfo<CsLiveAppointmentDTO> appointmentList = appointmentService.getAppointmentList(dto);
            return JsonResult.success(appointmentList);
        }
    }

    2、springboot2.0 实现WebMvcConfigurer 实现跨域

    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
     
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("POST","GET","OPTIONS")
                    .allowedHeaders("*")
                    .allowCredentials(false).maxAge(3600);
        }
     
    }

    3、过滤器实现跨域

    @WebFilter(filterName = "CorsFilter")
    @Configuration
    @Order(Ordered.HIGHEST_PRECEDENCE)
    @Slf4j
    public class CorsFilter implements Filter {
    
        @Value("${allow.headers:X-Requested-With,Authorization,Content-Type}")
        private String allowHeaders;
    
        @Value("${allow.origin:https://xxx.com}")
        private String allowOrigin;
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletResponse response = (HttpServletResponse) res;
    //        response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Origin", "http://xxx:9091");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH,OPTIONS, DELETE, PUT");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", allowHeaders);
    //        response.setHeader("Access-Control-Allow-Headers", "*");
            log.info("CorsFilter res {},{}", response.getHeader("Access-Control-Allow-Origin"), response.containsHeader("Access-Control-Allow-Origin"));
            chain.doFilter(req, res);
        }
    }

    **跨域不生效问题**

    (1)、@Order(Ordered.HIGHEST_PRECEDENCE)如果有登录拦截,要将跨域filter等级提升为最高优先级

    (2)、 response.setHeader("Access-Control-Allow-Headers", "*");

    Access-Control-Allow-Headers: * 在部分客户端上有兼容问题,MDN中介绍 Access-Control-Allow-Headers: * 有两重意思。

    一个是在服务端设置Access-Control-Allow-Credentials: true的时候这个 * 只会被客户端当做字符串 * (我们不希望的,会出错的)。

    另一个是没有这个设置则会被当做通配符(我们希望的,不会出错的)。

    猜测是客户端对于 * 的实现上有兼容性问题,所以建议不要这样设置,用到什么设置什么最好,例如:Access-Control-Allow-Headers: Content-Type,X-Requested-With,Authorization。

    (3)、 response.setHeader("Access-Control-Allow-Origin", "*")

    //指定允许其他域名访问

    'Access-Control-Allow-Origin:http://172.80.0.206'//一般用法(*,指定域,动态设置),3是因为*不允许携带认证头和cookies

    //是否允许后续请求携带认证信息(cookies),该值只能是true,否则不返回

    (4)、 response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH,OPTIONS, DELETE, PUT");OPTIONS 在**预检请求**复杂请求中也会使用到

    (5)、 如果有spring security结合使用需要添加该过滤器

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
     
        @Override
        protected void configure(HttpSecurity security) throws Exception {
            security.csrf().disable();
            security.headers().frameOptions().disable();
            //加入过滤器
            security.addFilterBefore(new CORSFilter(),                              UsernamePasswordAuthenticationFilter.class);
        }
    }

    4、定制化参数实现跨域

            前面要么是*,实际需求是根据业务参数定制化

    @WebFilter(filterName = "corsFilter", urlPatterns = "/*",
            initParams = {@WebInitParam(name = "allowOrigin", value = "*"),
                    @WebInitParam(name = "allowMethods", value = "GET,POST,PUT,DELETE,OPTIONS"),
                    @WebInitParam(name = "allowCredentials", value = "true"),
                    @WebInitParam(name = "allowHeaders", value = "Content-Type,X-Token")})
    public class CorsFilter implements Filter {
     
        private String allowOrigin;
        private String allowMethods;
        private String allowCredentials;
        private String allowHeaders;
        private String exposeHeaders;
     
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            allowOrigin = filterConfig.getInitParameter("allowOrigin");
            allowMethods = filterConfig.getInitParameter("allowMethods");
            allowCredentials = filterConfig.getInitParameter("allowCredentials");
            allowHeaders = filterConfig.getInitParameter("allowHeaders");
            exposeHeaders = filterConfig.getInitParameter("exposeHeaders");
        }
     
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            if (!StringUtils.isEmpty(allowOrigin)) {
                if(allowOrigin.equals("*")){
                    // 设置哪个源可以访问
                    response.setHeader("Access-Control-Allow-Origin", allowOrigin);
                }else{
                    List<String> allowOriginList = Arrays.asList(allowOrigin.split(","));
                    if (allowOriginList != null && allowOriginList.size() > 0) {
                        String currentOrigin = request.getHeader("Origin");
                        if (allowOriginList.contains(currentOrigin)) {
                            response.setHeader("Access-Control-Allow-Origin", currentOrigin);
                        }
                    }
                }
            }
            if (!StringUtils.isEmpty(allowMethods)) {
                //设置哪个方法可以访问
                response.setHeader("Access-Control-Allow-Methods", allowMethods);
            }
            if (!StringUtils.isEmpty(allowCredentials)) {
                // 允许携带cookie
                response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
            }
            if (!StringUtils.isEmpty(allowHeaders)) {
                // 允许携带哪个头
                response.setHeader("Access-Control-Allow-Headers", allowHeaders);
            }
            if (!StringUtils.isEmpty(exposeHeaders)) {
                // 允许携带哪个头
                response.setHeader("Access-Control-Expose-Headers", exposeHeaders);
            }
            filterChain.doFilter(servletRequest, servletResponse);
        }
     
        @Override
        public void destroy() {
     
        }
    }

    5、 使用SpringCloud网关GateWay实现跨域

    原理和前面类似

    @Configuration
    public class CorsConfig {
        @Bean
        public CorsWebFilter corsFilter() {
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowCredentials(Boolean.TRUE);//允许Cookie跨域
            config.addAllowedMethod("*");
            config.addAllowedOrigin("*");
            config.addAllowedHeader("*");//不要设置成*,参考前面
    
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
            source.registerCorsConfiguration("/**", config);
    
            return new CorsWebFilter(source);
        }
    }

    注:在下层服务不需要在做任何跨域配置,例如注解@CrossOrigin,否则会由于配置冲突导致依然出现跨域问题

    6、nginx配置代理解决跨域问题

    server {
            listen       8000;
            server_name  localhost;
            # / 表示匹配路径为/的url
            location / {
               proxy_pass http://需要跨域的域名:5500;
            }
     
            # /user 表示访问以/user 开头 的地址 如/username,/user/find等
            location /user {
               proxy_pass http://需要跨域的域名:3000;
            }
     
        }

    7、nginx配置响应头允许跨域

    #
    # Wide-open CORS config for nginx
    #
    location / {
        
        #### 对OPTIONS请求,会设置很多的请求头,并返回204
         if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            #
            # Custom headers and headers various browsers *should* be OK with but aren't
            #
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            #
            # Tell client that this pre-flight info is valid for 20 days
            #
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
         }
         if ($request_method = 'POST') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
         }
         if ($request_method = 'GET') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
         }
    }

    参考来源如下:

    > https://blog.csdn.net/weixin_40910372/article/details/100068498

    > https://blog.csdn.net/letterTiger/article/details/105939465

    > https://blog.csdn.net/liao0801_123/article/details/103459241

    > https://blog.csdn.net/f641385712/article/details/101170214

    > https://blog.csdn.net/qq_36155003/article/details/114540029

  • 相关阅读:
    Cookie工具类
    验证工具类
    压缩工具类
    一次外企QQ面试
    利用Referer请求头阻止"盗链"
    servlet中ServletConfig的使用
    jquery插件制作
    jQuery选择器总结(转)
    js文件加载执行顺序
    mysql有关问题之:the security settings could not be applied to
  • 原文地址:https://www.cnblogs.com/flgb/p/16158997.html
Copyright © 2020-2023  润新知