🤦♂️这个是我在springboot中使用springSecruity写一个小demo时遇到的问题,记录下来🤦♂️
文章目录
- 跨域请求
- springboot项目中使用springSecruity导致跨域请求@CrossOrigin请求失效解决方法
- springboot 中的跨域方法
跨域请求
什么是跨域请求,为啥会有跨域请求?👍
跨域请求,就是说浏览器在执行脚本文件的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 。
服务器收到请求时,需要分别对 Origin、Access-Control-Request-Method、Access-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*/@Overrideprotected 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 {@Beanpublic 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 {@Beanpublic 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 {@Overridepublic 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方法。