springSecruity--->和springboot结合的跨域问题

🤦‍♂️这个是我在springboot中使用springSecruity写一个小demo时遇到的问题,记录下来🤦‍♂️

跨域请求

什么是跨域请求,为啥会有跨域请求?👍

跨域请求,就是说浏览器在执行脚本文件的ajax请求时,脚本文件所在的服务地址和请求的服务地址不一样。说白了就是ip、网络协议、端口都一样的时候,就是同一个域,否则就是跨域。这是由于Netscape提出一个著名的安全策略——同源策略造成的,这是浏览器对JavaScript施加的安全限制。是防止外网的脚本恶意攻击服务器的一种措施。

俺们先来看看请求又有哪几种吧:

1.简单请求:

简单请求是指符合跨域请求规范中定义的一种简单请求类型。简单请求必须满足以下条件:
请求方法限于 GET、POST 或 HEAD;
只能使用以下几种 Content-Type:application/x-www-form-urlencoded、multipart/form-data、text/plain;
请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器;
请求中没有使用 ReadableStream 对象;
请求中的任意 ReadableStream 对象均没有被启动;
请求的 method 属性不是 GET、HEAD 或 POST 以外的字符串;
请求的带有信函头之一。
简单请求不会触发预检请求(OPTIONS 请求),而是直接发送实际的请求。这样可以减少跨域请求的复杂性和延迟。

对于简单请求
CORS的策略是请求时在请求头中增加一个Origin字段,服务器收到请求后,根据该字段判断是否允许该请求访问。

1.如果允许,则在 HTTP 头信息中添加 Access-Control-Allow-Origin 字段,并返回正确的结果 ;
2.如果不 允许,则不在 HTTP 头信息中添加 Access-Control-Allow-Origin 字段 。

2.非简单请求

对于非简单请求的跨源请求,浏览器会在真实请求发出前,增加一次OPTION请求,称为预检请求(preflight request)。预检请求将真实请求的信息,包括请求方法、自定义头字段、源信息添加到 HTTP 头信息字段中,询问服务器是否允许这样的操作。

例如一个DELETE请求:

OPTIONS /test HTTP/1.1
Origin: http://www.examples.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: X-Custom-Header
Host: www.examples.com

与 CORS 相关的字段有:

请求使用的 HTTP 方法 Access-Control-Request-Method ;
请求中包含的自定义头字段 Access-Control-Request-Headers 。
服务器收到请求时,需要分别对 OriginAccess-Control-Request-MethodAccess-Control-Request-Headers 进行验证,验证通过后,会在返回 HTTP头信息中添加 :

Access-Control-Allow-Origin: http://www.examples.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
他们的含义分别是:

Access-Control-Allow-Methods: 真实请求允许的方法
Access-Control-Allow-Headers: 服务器允许使用的字段
Access-Control-Allow-Credentials: 是否允许用户发送、处理 cookie
Access-Control-Max-Age: 预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求
当预检请求通过后,浏览器会发送真实请求到服务器。这就实现了跨源请求。

总之:跨域请求会经历的几个步骤也就这几步
1.访问另一个域的资源。
2.有可能会发起一次预检请求(非简单请求,或超过了 Max-Age)。
3.发起实际请求。

springboot项目中使用springSecruity导致跨域请求@CrossOrigin请求失效解决方法

第一步

想访问的controller类该加上@CrossOrigin还加上,不要删除

第二步

配置springSecruity配置类,添加跨域配置
下面代码中通过调用http.cors().and().csrf().disable()方法来启用跨域配置并禁用CSRF保护。

package com.example.sec_demo1.config;

/**
 * @ClassName Sccc
 * @Description TODO
 * @Author zyhh
 * @date 2024/2/29 14:27
 * @version: 1.0
 */

import org.springframework.context.annotation.Configuration;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsUtils;

/**
 * SpringSecurity配置类
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//这个是开启方法级别权限
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 授权
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 开启跨域访问
        http.cors(); //.disable();
        // 开启模拟请求,比如API POST测试工具的测试,不开启时,API POST为报403错误
        http.csrf().disable();

        // iframe 跳转错误处理 Refused to display 'url' in a frame because it set 'X-Frame-Options' to 'deny'
        http.headers().frameOptions().disable();
        // 当出现跨域的OPTIONS请求时,发现被拦截,加入下面设置可实现对OPTIONS请求的放行。
        http.authorizeRequests().
                requestMatchers(CorsUtils::isPreFlightRequest).
                permitAll();
    }
}


第三步

配置一个CorsConfigurationSource的Bean来定义跨域配置。
通过CorsConfigurationSource的Bean定义了允许的来源、方法和头部,并将其应用到所有路径上。

@Configuration
public class CorsConfig {

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("*");
        configuration.addAllowedMethod("*");
        configuration.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

=============== over ===================================

再补充一点哈哈

springboot 中的跨域方法

有三种
1.针对单个 API的
2.针对整个应用的
3.在一些情况下是等效的,而在另一些情况下却又出现不同

针对单个API的
其实就是使用@CrossOrigin。可以加类上,也可以加单个方法上。

针对整个应用的
实现 WebMvcConfigurerz中的addCorsMappings 方法

/**
 * SpringMVC 跨域配置
 */
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true); /*是否允许请求带有验证信息*/
        corsConfiguration.addAllowedOrigin("*");/*允许访问的客户端域名*/
        corsConfiguration.addAllowedHeader("*");/*允许服务端访问的客户端请求头*/
        corsConfiguration.addAllowedMethod("*"); /*允许访问的方法名,GET POST等*/
        corsConfiguration.addExposedHeader("token");/*暴露哪些头部信息 不能用*因为跨域访问默认不能获取全部头部信息*/
        corsConfiguration.addExposedHeader("TOKEN");
        corsConfiguration.addExposedHeader("Authorization");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

在一些情况下是等效的,而在另一些情况下却又出现不同
注入 CorsFilter
注入 CorsFilter 不止这一种方式,我们还可以通过注入一个 FilterRegistrationBean 来实现,这里就不给例子了。

@WebFilter("/*")
public class CORSFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException, ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        // 告诉浏览器允许所有的域访问
        // 注意 * 不能满足带有cookie的访问,Origin 必须是全匹配
        // resp.addHeader("Access-Control-Allow-Origin", "*");
        // 解决办法通过获取Origin请求头来动态设置
        String origin = request.getHeader("Origin");
        if (StringUtils.hasText(origin)) {
            resp.addHeader("Access-Control-Allow-Origin", origin);
        }
        // 允许带有cookie访问
        resp.addHeader("Access-Control-Allow-Credentials", "true");

        // 告诉浏览器允许跨域访问的方法
        resp.addHeader("Access-Control-Allow-Methods", "*");

        // 告诉浏览器允许带有Content-Type,header1,header2头的请求访问
        // resp.addHeader("Access-Control-Allow-Headers", "Content-Type,header1,header2");
        // 设置支持所有的自定义请求头
        String headers = request.getHeader("Access-Control-Request-Headers");
        if (StringUtils.hasText(headers)) {
            resp.addHeader("Access-Control-Allow-Headers", headers);
        }

        // 告诉浏览器缓存OPTIONS预检请求1小时,避免非简单请求每次发送预检请求,提升性能
        resp.addHeader("Access-Control-Max-Age", "3600");

        chain.doFilter(request, resp);
    }
}

以上是springboot才需要的配置,如果是springboot+springSecruity。这些是没办法使用的。得使用

CorsConfigurationSource配置类和 继承WebSecurityConfigurerAdapter类并实现configure方法。

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值