SpringBoot 處理跨域請求

什麼是跨域?

由於安全原因,瀏覽器都遵循著同源原則,攔截了不同域名之間的請求。跨域請求,是指能讓不同域名之間,可以相互發送請求。 當它請求的一個資源是從一個與它本身提供的第一個資源的不同的域名時,一個資源會發起一個跨域HTTP請求(Cross-site HTTP request)。 比如說,域名A ( http://domaina.example ) 的某 Web 應用程序中通過< img>標籤引入了域名B( http://domainb.foo ) 站點的某圖片資源(http://domainb.foo/image.jpg),域名A的那 Web 應用就會導致瀏覽器發起一個跨站 HTTP 請求。 在當今的 Web 開發中,使用跨站 HTTP 請求加載各類資源(包括CSS、圖片、JavaScript 腳本以及其它類資源),已經成為了一種普遍且流行的方式。 正如大家所知,出於安全考慮,瀏覽器會限制腳本中發起的跨站請求。比如,使用 XMLHttpRequest 對象發起 HTTP 請求就必須遵守同源策略。 具體而言,Web 應用程序能且只能使用 XMLHttpRequest 對象向其加載的源域名發起 HTTP 請求,而不能向任何其它域名發起請求。為了能開發出更強大、更豐富、更安全的Web應用程序,開發人員渴望著在不丟失安全的前提下,Web 應用技術能越來越強大、越來越豐富。比如,可以使用 XMLHttpRequest 發起跨站 HTTP 請求。 跨域並非瀏覽器限制了發起跨站請求,而是跨站請求可以正常發起,但是返回結果被瀏覽器攔截了。最好的例子是CSRF跨站攻擊原理,請求是發送到了後端服務器無論是否跨域!注意:有些瀏覽器不允許從HTTPS的域跨域訪問HTTP,比如Chrome和Firefox,這些瀏覽器在請求還未發出的時候就會攔截請求,這是一個特例。

跨域訪問報錯如下:

<code>OPTIONS http://localhost:8080/api/bocom/query/name/123
XMLHttpRequest cannot load http://localhost:8080/api/bocom/query/name/123. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. The response had HTTP status code 403.
/<code>

一、第一種方式:1、在需要跨域的類或方法上方增加註解@CrossOrigin,只針對單個方法或類有效,適用於只有個別方法需要跨域的情況。

<code>@RequestMapping(value = "/users")
@RestController
@CrossOrigin
public class UserController {

@Autowired
private UserService userService;

@RequestMapping(method = RequestMethod.POST)
@CrossOrigin
public User create(@RequestBody @Validated User user) {
return userService.create(user);
}

/<code>

二、第二種方式:1、編寫一個支持跨域請求的 Configuration

<code> 
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
* 處理AJAX請求跨域的問題
* @author jeffrey
* @time 2018-12-13
*/
@Configuration
public class CorsConfig extends WebMvcConfigurerAdapter {

static final String ORIGINS[] = new String[] { "GET", "POST", "PUT", "DELETE" };
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowCredentials(true).allowedMethods(ORIGINS)
.maxAge(3600);
}
}
2、HTTP請求接口

@RestController
public class HelloController {

@Autowired
HelloService helloService;


@GetMapping(value = "/test", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public String query() {
return "hello";
}
}
/<code>

三、 第三種方式(推薦)PS:第一種存在一個問題,當服務器拋出 500 的時候依舊存在跨域問題

<code>@SpringBootApplication
@ComponentScan
@EnableDiscoveryClient
public class ManagementApplication {

public static void main(String[] args) {
SpringApplication.run(ManagementApplication.class, args);
}

private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addExposedHeader(HttpHeaderConStant.X_TOTAL_COUNT);//可去掉
return corsConfiguration;
}

/**
* 跨域過濾器

*
* @return
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4
return new CorsFilter(source);
}
}
/<code>

也可以寫成一個類,加註解@Configuration

<code>package com.beenoisy.springboot.way.common.config;  

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;

@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1、允許任何域名使用
corsConfiguration.addAllowedHeader("*"); // 2、允許任何頭
corsConfiguration.addAllowedMethod("*"); // 3、允許任何方法(post、get等)
return corsConfiguration;
}

@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4
return new CorsFilter(source);
}
}
/<code>

2、index.html

<code>



<title>跨域請求/<title>





<button>獲取其他內容/<button>


/<code>

四、第四種方式,編寫Filter過濾器

<code>package com.cci.market.common.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;

/**
* 處理跨域問題
* @author jeffrey
* @date 2019/08/08
*
*/
@Component
public class OriginFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@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-Methods", "POST, GET, OPTIONS, DELETE,PUT");

response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, res);
}

@Override
public void destroy() {
// TODO Auto-generated method stub

}
}
/<code>

五、Nginx跨域配置Nginx跨域也比較簡單,只需添加以下配置即可。

<code>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';
}
}
/<code>

其中:add_header 'Access-Control-Expose-Headers' 務必加上你請求時所帶的header。例如本例中的“Token”,其實是前端傳給後端過來的。如果記不得也沒有關係,瀏覽器的調試器會有詳細說明。


分享到:


相關文章: