spring boot跨域请求访问配置以及spring security中配置失效的原理解析

1、同源策略

同源策略[same origin policy]是浏览器的一个安全功能,不一样源的客户端脚本在没有明确受权的状况下,不能读写对方资源。 同源策略是浏览器安全的基石。html

什么是源

[origin]就是协议、域名和端口号。例如:http://www.baidu.com:80这个URL。前端

什么是同源

若地址里面的协议、域名和端口号均相同则属于同源。java

是不是同源的判断

例如判断下面的URL是否与 http://www.a.com/test/index.html 同源web

  • http://www.a.com/dir/page.html 同源
  • http://www.child.a.com/test/index.html 不一样源,域名不相同
  • https://www.a.com/test/index.html 不一样源,协议不相同
  • http://www.a.com:8080/test/index.html 不一样源,端口号不相同

哪些操做不受同源策略限制

  1. 页面中的连接,重定向以及表单提交是不会受到同源策略限制的;
  2. 跨域资源的引入是能够的。可是JS不能读写加载的内容。如嵌入到页面中的<script src="..."></script><img><link><iframe>等。

跨域

受前面所讲的浏览器同源策略的影响,不是同源的脚本不能操做其余源下面的对象。想要操做另外一个源下的对象就须要跨域。 在同源策略的限制下,非同源的网站之间不能发送 AJAX 请求。ajax

如何跨域

  • 降域spring

    能够经过设置 document.damain='a.com',浏览器就会认为它们都是同一个源。想要实现以上任意两个页面之间的通讯,两个页面必须都设置documen.damain='a.com'后端

  • JSONP跨域跨域

CORS 跨域浏览器

2、CORS 简介

为了解决浏览器同源问题,W3C 提出了跨源资源共享,即 CORS(Cross-Origin Resource Sharing)。缓存

CORS 作到了以下两点:

  • 不破坏即有规则
  • 服务器实现了 CORS 接口,就能够跨源通讯

基于这两点,CORS 将请求分为两类:简单请求和非简单请求。

一、简单请求

CORS出现前,发送HTTP请求时在头信息中不能包含任何自定义字段,且 HTTP 头信息不超过如下几个字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type 只限于 [application/x-www-form-urlencoded 、multipart/form-datatext/plain ] 类型

一个简单的请求例子:

GET /test HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, sdch, br
Origin: http://www.examples.com
Host: www.examples.com

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

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

除了上面提到的 Access-Control-Allow-Origin ,还有几个字段用于描述 CORS 返回结果 :

  1. Access-Control-Allow-Credentials: 可选,用户是否能够发送、处理 cookie
  2. Access-Control-Expose-Headers:可选,可让用户拿到的字段。有几个字段不管设置与否均可以拿到的,包括:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma 。

二、非简单请求

对于非简单请求的跨源请求,浏览器会在真实请求发出前,增长一次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 相关的字段有:

  1. 请求使用的 HTTP 方法 Access-Control-Request-Method ;
  2. 请求中包含的自定义头字段 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

他们的含义分别是:

  1. Access-Control-Allow-Methods: 真实请求容许的方法
  2. Access-Control-Allow-Headers: 服务器容许使用的字段
  3. Access-Control-Allow-Credentials: 是否容许用户发送、处理 cookie
  4. Access-Control-Max-Age: 预检请求的有效期,单位为秒。有效期内,不会重复发送预检请求

当预检请求经过后,浏览器会发送真实请求到服务器。这就实现了跨源请求。

3、跨域请求

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

springboot提供了跨域的方法:

受权方式
方式1:返回新的CorsFilter
方式2:重写WebMvcConfigurer
方式3:使用注解(@CrossOrigin)
方式4:手工设置响应头(HttpServletResponse )

一、返回新的CorsFilter(全局跨域)

package com.hehe.yyweb.config;

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1.添加CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
          //放行哪些原始域
          config.addAllowedOrigin("*");
          //是否发送Cookie信息
          config.setAllowCredentials(true);
          //放行哪些原始域(请求方式)
          config.addAllowedMethod("*");
          //放行哪些原始域(头部信息
          config.addAllowedHeader("*");
          //暴露哪些头部信息(由于跨域访问默认不能获取所有头部信息)
          config.addExposedHeader("*");

        //2.添加映射路径
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);

        //3.返回新的CorsFilter.
        return new CorsFilter(configSource);
    }
}

2. 重写WebMvcConfigurer(全局跨域)

一、1.5版本为继承WebMvcConfigurerAdapter 类实现抽象方法,

//springboot 1.5方式
@Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedHeaders("*") .allowedMethods("*") .allowedOrigins("*") .allowCredentials(true); } }

2.0之后为实现WebMvcConfigurer 接口重写方法

//springboot 2.0以上的方式
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedHeaders("Content-Type","X-Requested-With","accept,Origin","Access-Control-Request-Method","Access-Control-Request-Headers","token") .allowedMethods("*") .allowedOrigins("*") .allowCredentials(true); } }

3. 编写Filter过滤器或者拦截器(全局跨域)

使用拦截器实现跨域:

import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerInterceptor() { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); response.addHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,token"); 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 { } }); } }

使用servlet提供的过滤器进行跨域配置

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; /** * 请求的基本过滤器 预处理请求头 */ @Component @WebFilter(urlPatterns = {"/*"}, filterName = "tokenAuthorFilter") public class TokenAuthorFilter implements Filter { private static Logger LOG = LoggerFactory.getLogger(TokenAuthorFilter.class); @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse rep = (HttpServletResponse) response; HttpSession session = req.getSession(); LOG.info("sessionId:{}", session.getId()); //LOG.info("Origin:{}", req.getHeader("Origin")); //设置容许跨域的配置 // 这里填写你容许进行跨域的主机ip(正式上线时能够动态配置具体容许的域名和IP)
        rep.setHeader("Access-Control-Allow-Origin", "*"); //rep.setHeader("Access-Control-Allow-Origin", "*");
        rep.setHeader("Access-Control-Expose-Headers", jwtProperties.getHeader()); // 容许的访问方法
        rep.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH"); // Access-Control-Max-Age 用于 CORS 相关配置的缓存
        rep.setHeader("Access-Control-Max-Age", "3600"); rep.setHeader("Access-Control-Allow-Headers", "token, Origin, X-Requested-With, Content-Type, Accept"); //若要返回cookie、携带seesion等信息则将此项设置我true
        rep.setHeader("Access-Control-Allow-Credentials", "true"); // 把获取的Session返回个前端Cookie //rep.addCookie(new Cookie("JSSESIONID", session.getId()));
 chain.doFilter(req, rep); } @Override public void init(FilterConfig arg0) throws ServletException { } }

4.使用注解(局部跨域)

灵活的跨域方式:注解形式

@CrossOrigin

一、@CrossOrigin使用场景要求
  • jdk1.8+
  • Spring4.2+
二、@CrossOrigin源码解析
@Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CrossOrigin { String[] DEFAULT_ORIGINS = { "*" }; String[] DEFAULT_ALLOWED_HEADERS = { "*" }; boolean DEFAULT_ALLOW_CREDENTIALS = true; long DEFAULT_MAX_AGE = 1800; /** * 同origins属性同样 */ @AliasFor("origins") String[] value() default {}; /** * 全部支持域的集合,例如"http://domain1.com"。 * <p>这些值都显示在请求头中的Access-Control-Allow-Origin * "*"表明全部域的请求都支持 * <p>若是没有定义,全部请求的域都支持 * @see #value */ @AliasFor("value") String[] origins() default {}; /** * 容许请求头重的header,默认都支持 */ String[] allowedHeaders() default {}; /** * 响应头中容许访问的header,默认为空 */ String[] exposedHeaders() default {}; /** * 请求支持的方法,例如"{RequestMethod.GET, RequestMethod.POST}"}。 * 默认支持RequestMapping中设置的方法 */ RequestMethod[] methods() default {}; /** * 是否容许cookie随请求发送,使用时必须指定具体的域 */ String allowCredentials() default ""; /** * 预请求的结果的有效期,默认30分钟 */
    long maxAge() default -1;
}

二、@CrossOrigin的使用

@RestController @RequestMapping("/account") public class AccountController { @CrossOrigin @GetMapping("/{id}") public Account retrieve(@PathVariable Long id) { // ...
 } @DeleteMapping("/{id}") public void remove(@PathVariable Long id) { // ...
 } }
package com.example.demo.controller; import com.example.demo.domain.User; import com.example.demo.service.IUserFind; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @Title: UserController * @ProjectName demo * @Description: 请求处理控制器 * @author * @date 2018/7/2022:18 **/ @RestController //实现跨域注解 //origin="*"表明全部域名均可访问 //maxAge飞行前响应的缓存持续时间的最大年龄,简单来讲就是Cookie的有效期 单位为秒 //若maxAge是负数,则表明为临时Cookie,不会被持久化,Cookie信息保存在浏览器内存中,浏览器关闭Cookie就消失
@CrossOrigin(origins = "*",maxAge = 3600) public class UserController { @Resource private IUserFind userFind; @GetMapping("finduser") public User finduser(@RequestParam(value="id") Integer id){ //此处省略相应代码
 } }

4、Nginx跨域配置

其中:add_header 'Access-Control-Expose-Headers' 务必加上你请求时所带的header。例如本例中的“Token”,实际上是前端传给后端过来的。若是记不得也没有关系,浏览器的调试器会有详细说明。

location / { proxy_pass http://localhost:8080;
    if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; 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,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; } 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,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Token'; } }

5、spring security 跨域问题

配置跨域

@Configuration public class GlobalCorsConfiguration { @Bean public CorsFilter corsFilter() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.setAllowCredentials(true); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); // corsConfiguration.addExposedHeader("head1"); //corsConfiguration.addExposedHeader("Location");
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); return new CorsFilter(urlBasedCorsConfigurationSource); } }

或者

@Configuration public class CorsConfig extends WebMvcConfigurerAdapter { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.addExposedHeader("Authorization"); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); return new CorsFilter(source); } @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT") .maxAge(3600); } }

spring security下这些跨域配置后,仍是会引发跨域的问题,跨域请求仍是没法访问,须要在springsecurity配置中加上cors()来开启跨域以及requestMatchers(CorsUtils::isPreFlightRequest).permitAll()来处理跨域请求中的preflight请求。(以下代码红色标记部分)

//开启跨域 cors()
 http.cors().and().csrf().disable().authorizeRequests() //处理跨域请求中的Preflight请求
 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() .antMatchers(HttpMethod.GET,"/hello/hello").permitAll() .antMatchers("/oauth/login").permitAll() .anyRequest().authenticated() .and() .formLogin() //自定义登陆页面
                .loginPage("/oauth/login") .and() .logout()

在springsecurity的配置中设置后,在权限安全框架中才会真正的实现跨域请求。

原文出处:https://www.cnblogs.com/yuarvin/p/10923280.html

相关文章
相关标签/搜索