• Java编程技巧:跨域


    1、跨域概念

    通过一个地址去访问另外一个地址,两个地址的url中的3个地方只要有任何一个不同,那就会引起跨域问题,它们分别是:访问协议ip地址端口号

    日常工作中,用得比较多的解决跨域方案是Nginx代理或者后端CORS(跨域资源共享)配置

    2、后端CORS(跨域资源共享)配置原理

    CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
    浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

    服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

    3、既然请求跨域了,那么请求到底发出去没有?

    跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了

    4、通过后端CORS(跨域资源共享)配置解决跨域问题代码

    4.1、SpringBoot(FilterRegistrationBean)
    4.1.1、配置文件
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    import javax.servlet.DispatcherType;
    
    /**
     * @author smalljop
     * @description 过滤器配置
     * @create 2019-01-29 16:27
     **/
    @Configuration
    public class FilterConfig {
        /**
         * 跨域过滤器
         *
         * @return
         */
        @Bean
        public FilterRegistrationBean corsFilterRegistration() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setDispatcherTypes(DispatcherType.REQUEST);
            CorsConfiguration config = new CorsConfiguration();
            config.addAllowedOrigin("*");
            config.addAllowedMethod("*");
            config.addAllowedHeader("*");
            UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
            corsConfigurationSource.registerCorsConfiguration("/**", config);
            CorsFilter corsFilter = new CorsFilter(corsConfigurationSource);
            registration.setOrder(Integer.MAX_VALUE - 4);
            registration.setFilter(corsFilter);
            return registration;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    4.1.2、项目

    链接:https://pan.baidu.com/s/1tEHpnsvAlQ6g4N4aiocp-g?pwd=mulu

    提取码:mulu

    4.1.3、结果验证
    1. 启动后端代码
    2. 在浏览器访问test.html
    3. 通过F12查看网络,可以看到请求test1正常执行,并且有返回值
      在这里插入图片描述
    4.2、SpringBoot(WebMvcConfigurer)
    4.2.1、配置文件
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .maxAge(3600);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    4.2.2、项目

    链接:https://pan.baidu.com/s/1iaZuG1BfUZt_FaNyDyXRfQ?pwd=xorq

    提取码:xorq

    4.2.3、结果验证
    1. 启动后端代码
    2. 在浏览器访问test.html
    3. 通过F12查看网络,可以看到请求test1正常执行,并且有返回值
      在这里插入图片描述
    4.3、SpringBoot(@CrossOrigin)
    4.3.1、使用示例
    @Api(value = "测试", tags = {"测试"}) // 必须添加tags,否则UI界面中不会显示当前类的中文说明
    @RestController
    @RequestMapping("/test")
    @CrossOrigin
    public class TestController {
    	……
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    4.3.2、项目

    链接:https://pan.baidu.com/s/1af-rXbnDRzia47NReUQZ_w?pwd=hewt

    提取码:hewt

    4.3.3、结果验证
    4.4、SpringBoot(SpringSecurity + 过滤器)
    4.4.1、配置文件

    ResourcesConfig:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import com.ruoyi.common.config.RuoYiConfig;
    import com.ruoyi.common.constant.Constants;
    import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
    
    /**
     * 通用配置
     * 
     * @author ruoyi
     */
    @Configuration
    public class ResourcesConfig implements WebMvcConfigurer
    {
        @Autowired
        private RepeatSubmitInterceptor repeatSubmitInterceptor;
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry)
        {
            /** 本地文件上传路径 */
            registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
                    .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
    
            /** swagger配置 */
            registry.addResourceHandler("/swagger-ui/**")
                    .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
        }
    
        /**
         * 自定义拦截规则
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry)
        {
            registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
        }
    
        /**
         * 跨域配置
         */
        @Bean
        public CorsFilter corsFilter()
        {
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowCredentials(true);
            // 设置访问源地址
            config.addAllowedOriginPattern("*");
            // 设置访问源请求头
            config.addAllowedHeader("*");
            // 设置访问源请求方法
            config.addAllowedMethod("*");
            // 有效期 1800秒
            config.setMaxAge(1800L);
            // 添加映射路径,拦截一切请求
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", config);
            // 返回新的CorsFilter
            return new CorsFilter(source);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    SecurityConfig:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    import org.springframework.security.web.authentication.logout.LogoutFilter;
    import org.springframework.web.filter.CorsFilter;
    import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
    import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
    import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
    import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
    
    /**
     * spring security配置
     * 
     * @author ruoyi
     */
    @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter
    {
        /**
         * 自定义用户认证逻辑
         */
        @Autowired
        private UserDetailsService userDetailsService;
        
        /**
         * 认证失败处理类
         */
        @Autowired
        private AuthenticationEntryPointImpl unauthorizedHandler;
    
        /**
         * 退出处理类
         */
        @Autowired
        private LogoutSuccessHandlerImpl logoutSuccessHandler;
    
        /**
         * token认证过滤器
         */
        @Autowired
        private JwtAuthenticationTokenFilter authenticationTokenFilter;
        
        /**
         * 跨域过滤器
         */
        @Autowired
        private CorsFilter corsFilter;
    
        /**
         * 允许匿名访问的地址
         */
        @Autowired
        private PermitAllUrlProperties permitAllUrl;
    
        /**
         * 解决 无法直接注入 AuthenticationManager
         *
         * @return
         * @throws Exception
         */
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception
        {
            return super.authenticationManagerBean();
        }
    
        /**
         * anyRequest          |   匹配所有请求路径
         * access              |   SpringEl表达式结果为true时可以访问
         * anonymous           |   匿名可以访问
         * denyAll             |   用户不能访问
         * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
         * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
         * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
         * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
         * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
         * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
         * permitAll           |   用户可以任意访问
         * rememberMe          |   允许通过remember-me登录的用户访问
         * authenticated       |   用户登录后可访问
         */
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception
        {
            // 注解标记允许匿名访问的url
            ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
            permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll());
    
            httpSecurity
                    // CSRF禁用,因为不使用session
                    .csrf().disable()
                    // 认证失败处理类
                    .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                    // 基于token,所以不需要session
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                    // 过滤请求
                    .authorizeRequests()
                    // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                    .antMatchers("/login", "/register", "/captchaImage").anonymous()
                    // 静态资源,可匿名访问
                    .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                    .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
                    // 除上面外的所有请求全部需要鉴权认证
                    .anyRequest().authenticated()
                    .and()
                    .headers().frameOptions().disable();
            // 添加Logout filter
            httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
            // 添加JWT filter
            httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
            // 添加CORS filter
            httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
            httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
        }
    
        /**
         * 强散列哈希加密实现
         */
        @Bean
        public BCryptPasswordEncoder bCryptPasswordEncoder()
        {
            return new BCryptPasswordEncoder();
        }
    
        /**
         * 身份认证接口
         */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception
        {
            auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    4.4.2、项目
    4.5、SpringCloud-Gateway(CorsWebFilter)
    4.5.1、配置文件
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.reactive.CorsWebFilter;
    import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
    
    /**
     * 跨域配置
     * 
     * @author ruoyi
     */
    @Configuration
    public class CorsConfig
    {
        @Bean
        CorsWebFilter corsWebFilter() {
            CorsConfiguration corsConfig = new CorsConfiguration();
    //        List list = new ArrayList();
    //        list.add("*");
    //        corsConfig.setAllowedOrigins(list);
            corsConfig.setAllowCredentials(true);
            corsConfig.setMaxAge(3600L);
            corsConfig.addAllowedMethod("PUT");
            corsConfig.addAllowedMethod("DELETE");
            corsConfig.addAllowedMethod("GET");
            corsConfig.addAllowedMethod("POST");
            corsConfig.addAllowedMethod("OPTIONS");
            corsConfig.addAllowedHeader("*");
            corsConfig.addAllowedOriginPattern("*");
            corsConfig.addExposedHeader("access-control-allow-headers");
            corsConfig.addExposedHeader("access-control-allow-methods");
            corsConfig.addExposedHeader("access-control-allow-origin");
            corsConfig.addExposedHeader("access-control-max-age");
            corsConfig.addExposedHeader("X-Frame-Options");
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", corsConfig);
            return new CorsWebFilter(source);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    4.5.2、项目

    链接:https://pan.baidu.com/s/1r6ivfYfA6Qel7uqmVU4OHg?pwd=nbtj

    提取码:nbtj

    4.5.3、结果验证
    1. 启动后端代码
    2. 在浏览器访问test.html
    3. 通过F12查看网络,可以看到请求test1正常执行,并且有返回值
      在这里插入图片描述
    4.6、SpringCloud-Gateway(WebFilter)
    4.6.1、配置文件
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.web.cors.reactive.CorsUtils;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.server.WebFilter;
    import org.springframework.web.server.WebFilterChain;
    import reactor.core.publisher.Mono;
    
    /**
     * 跨域配置
     * 
     * @author ruoyi
     */
    @Configuration
    public class CorsConfig
    {
        private static final String ALLOWED_HEADERS = "*";
        private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
        private static final String ALLOWED_ORIGIN = "*";
        private static final String ALLOWED_EXPOSE = "*";
        private static final String MAX_AGE = "18000L";
    
        @Bean
        public WebFilter corsFilter()
        {
            return (ServerWebExchange ctx, WebFilterChain chain) -> {
                ServerHttpRequest request = ctx.getRequest();
                if (CorsUtils.isCorsRequest(request))
                {
                    ServerHttpResponse response = ctx.getResponse();
                    HttpHeaders headers = response.getHeaders();
                    headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
                    headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
                    headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
                    headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
                    headers.add("Access-Control-Max-Age", MAX_AGE);
                    headers.add("Access-Control-Allow-Credentials", "true");
                    if (request.getMethod() == HttpMethod.OPTIONS)
                    {
                        response.setStatusCode(HttpStatus.OK);
                        return Mono.empty();
                    }
                }
                return chain.filter(ctx);
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    4.6.2、项目

    链接:https://pan.baidu.com/s/1wn9b-ZBDED91YeTrEtWr-A?pwd=q2jx

    提取码:q2jx

    4.6.3、结果验证
    1. 启动后端代码
    2. 在浏览器访问test.html
    3. 通过F12查看网络,可以看到请求test1正常执行,并且有返回值
      在这里插入图片描述
    4.7、SpringCloud-Gateway(bootstrap.yml)
    4.7.1、配置文件:bootstrap.yml
    spring:
      cloud:
        gateway:
          globalcors:
            corsConfigurations:
              '[/**]':
                allowedOriginPatterns: "*"
                allowed-methods: "*"
                allowed-headers: "*"
                allow-credentials: true
                exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    4.7.2、项目

    链接:https://pan.baidu.com/s/1MyWulmXILdcrdNOUmkCbJg?pwd=7hgz

    提取码:7hgz

    4.7.3、结果验证
    1. 启动后端代码
    2. 在浏览器访问test.html
    3. 通过F12查看网络,可以看到请求test1正常执行,并且有返回值
      在这里插入图片描述
  • 相关阅读:
    第13章: 常用类
    签个到吧codeforces898
    (一)探索随机变量及其分布:概率世界的魔法
    vue的自定义过滤器 - Filter
    java计算机毕业设计好物网站MyBatis+系统+LW文档+源码+调试部署
    带你实现react源码的核心功能
    酸纯化APU系统在阳极氧化酸回收中的应用
    聚观早报 | 智界S7正式亮相;ChatGPT重磅更新
    音频数据分析注意事项
    爬虫 Selector 选择器查找元素
  • 原文地址:https://blog.csdn.net/qq_42449963/article/details/133459367