Security框架:如何使用CorsFilter解決前端跨域請求問題
項目情況
最近做的pmdb項目是前后端分離的, 由于測試的時候是前端與后端聯(lián)調(diào),所以出現(xiàn)了跨域請求的問題。
瀏覽器默認(rèn)會向后端發(fā)送一個Options方式的請求,根據(jù)后端的響應(yīng)來判斷后端支持哪些請求方式,支持才會真正的發(fā)送請求。
CORS介紹
CORS(Cross-Origin Resource Sharing 跨源資源共享),當(dāng)一個請求url的協(xié)議、域名、端口三者之間任意一與當(dāng)前頁面地址不同即為跨域。
在日常的項目開發(fā)時會不可避免的需要進(jìn)行跨域操作,而在實際進(jìn)行跨域請求時,經(jīng)常會遇到類似 No 'Access-Control-Allow-Origin' header is present on the requested resource.這樣的報錯。

這樣的錯誤,一般是由于CORS跨域驗證機(jī)制設(shè)置不正確導(dǎo)致的。
解決方案
注釋:本項目使用的是SprintBoot+Security+JWT+Swagger
第一步
新建CorsFilter,在過濾器中設(shè)置相關(guān)請求頭
package com.handlecar.basf_pmdb_service.filter;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CorsFilter extends OncePerRequestFilter {
//public class CorsFilter implements Filter {
// static final String ORIGIN = "Origin";
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// String origin = request.getHeader(ORIGIN);
response.setHeader("Access-Control-Allow-Origin", "*");//* or origin as u prefer
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "PUT, POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
// response.setHeader("Access-Control-Allow-Headers", "content-type, authorization");
response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, Authorization");
response.setHeader("XDomainRequestAllowed","1");
//使前端能夠獲取到
response.setHeader("Access-Control-Expose-Headers","download-status,download-filename,download-message");
if (request.getMethod().equals("OPTIONS"))
// response.setStatus(HttpServletResponse.SC_OK);
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
else
filterChain.doFilter(request, response);
}
// @Override
// public void doFilter(ServletRequest req, ServletResponse res,
// FilterChain chain) throws IOException, ServletException {
//
// HttpServletResponse response = (HttpServletResponse) res;
// //測試環(huán)境用【*】匹配,上生產(chǎn)環(huán)境后需要切換為實際的前端請求地址
// response.setHeader("Access-Control-Allow-Origin", "*");
// response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
//
// response.setHeader("Access-Control-Max-Age", "0");
//
// response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, auth");
//
// response.setHeader("Access-Control-Allow-Credentials", "true");
//
// response.setHeader("XDomainRequestAllowed","1");
// chain.doFilter(req, res);
// }
//
// @Override
// public void destroy() {
// }
//
// @Override
// public void init(FilterConfig arg0) throws ServletException {
// }
}
注釋:這里的Access-Control-Expose-Headers的請求頭是為了使前端能夠獲得到后端在response中自定義的header,不設(shè)置的話,前端只能看到幾個默認(rèn)顯示的header。我這里是在使用response導(dǎo)出Excel的時候?qū)⑽募拖螺d狀態(tài)信息以自定義請求頭的形式放在了response的header里。
第二步
在Security的配置文件中初始化CorsFilter的Bean
@Bean
public CorsFilter corsFilter() throws Exception {
return new CorsFilter();
}
第三步
在Security的配置文件中添加Filter配置,和映射配置
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
// 除上面外的所有請求全部需要鑒權(quán)認(rèn)證。 .and() 相當(dāng)于標(biāo)示一個標(biāo)簽的結(jié)束,之前相當(dāng)于都是一個標(biāo)簽項下的內(nèi)容
.anyRequest().authenticated().and()
.addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class)
附:該配置文件
package com.handlecar.basf_pmdb_service.conf;
import com.handlecar.basf_pmdb_service.filter.CorsFilter;
import com.handlecar.basf_pmdb_service.filter.JwtAuthenticationTokenFilter;
import com.handlecar.basf_pmdb_service.security.JwtTokenUtil;
import com.handlecar.basf_pmdb_service.security.CustomAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
//import com.allcom.security.JwtTokenUtil;
@Configuration
//@EnableWebSecurity is used to enable Spring Security's web security support and provide the Spring MVC integration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomAuthenticationProvider customAuthenticationProvider;
@Autowired
public WebSecurityConfig(CustomAuthenticationProvider customAuthenticationProvider) {
this.customAuthenticationProvider = customAuthenticationProvider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthenticationProvider);
}
@Bean
public JwtTokenUtil jwtTokenUtil(){
return new JwtTokenUtil();
}
@Bean
public CorsFilter corsFilter() throws Exception {
return new CorsFilter();
}
@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() {
return new JwtAuthenticationTokenFilter();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// 由于使用的是JWT,我們這里不需要csrf,不用擔(dān)心csrf攻擊
.csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
//.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 允許對于網(wǎng)站靜態(tài)資源的無授權(quán)訪問
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/webjars/springfox-swagger-ui/images/**","/swagger-resources/configuration/*","/swagger-resources",//swagger請求
"/v2/api-docs"
).permitAll()
// 對于獲取token的rest api要允許匿名訪問
.antMatchers("/pmdbservice/auth/**","/pmdbservice/keywords/export3").permitAll()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
// 除上面外的所有請求全部需要鑒權(quán)認(rèn)證。 .and() 相當(dāng)于標(biāo)示一個標(biāo)簽的結(jié)束,之前相當(dāng)于都是一個標(biāo)簽項下的內(nèi)容
.anyRequest().authenticated().and()
.addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// 禁用緩存
httpSecurity.headers().cacheControl();
}
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
聊聊Java 成員變量賦值和構(gòu)造方法誰先執(zhí)行的問題
這篇文章主要介紹了聊聊Java 成員變量賦值和構(gòu)造方法誰先執(zhí)行的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
關(guān)于request.getRequestDispatcher().forward()的妙用及DispatcherType
這篇文章主要介紹了關(guān)于request.getRequestDispatcher().forward()的妙用及DispatcherType對Filter配置的影響,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
Canal搭建?idea設(shè)置及采集數(shù)據(jù)到kafka的操作方法
這篇文章主要介紹了Canal搭建idea設(shè)置及采集數(shù)據(jù)到kafka的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
Java swing讀取txt文件實現(xiàn)學(xué)生考試系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java swing讀取txt文件實現(xiàn)學(xué)生考試系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-06-06

