Security框架:如何使用CorsFilter解決前端跨域請(qǐng)求問(wèn)題
項(xiàng)目情況
最近做的pmdb項(xiàng)目是前后端分離的, 由于測(cè)試的時(shí)候是前端與后端聯(lián)調(diào),所以出現(xiàn)了跨域請(qǐng)求的問(wèn)題。
瀏覽器默認(rèn)會(huì)向后端發(fā)送一個(gè)Options方式的請(qǐng)求,根據(jù)后端的響應(yīng)來(lái)判斷后端支持哪些請(qǐng)求方式,支持才會(huì)真正的發(fā)送請(qǐng)求。
CORS介紹
CORS(Cross-Origin Resource Sharing 跨源資源共享),當(dāng)一個(gè)請(qǐng)求url的協(xié)議、域名、端口三者之間任意一與當(dāng)前頁(yè)面地址不同即為跨域。
在日常的項(xiàng)目開(kāi)發(fā)時(shí)會(huì)不可避免的需要進(jìn)行跨域操作,而在實(shí)際進(jìn)行跨域請(qǐng)求時(shí),經(jīng)常會(huì)遇到類(lèi)似 No 'Access-Control-Allow-Origin' header is present on the requested resource.這樣的報(bào)錯(cuò)。
這樣的錯(cuò)誤,一般是由于CORS跨域驗(yàn)證機(jī)制設(shè)置不正確導(dǎo)致的。
解決方案
注釋?zhuān)罕卷?xiàng)目使用的是SprintBoot+Security+JWT+Swagger
第一步
新建CorsFilter,在過(guò)濾器中設(shè)置相關(guān)請(qǐng)求頭
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; // //測(cè)試環(huán)境用【*】匹配,上生產(chǎn)環(huán)境后需要切換為實(shí)際的前端請(qǐng)求地址 // 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 { // } }
注釋?zhuān)哼@里的Access-Control-Expose-Headers的請(qǐng)求頭是為了使前端能夠獲得到后端在response中自定義的header,不設(shè)置的話,前端只能看到幾個(gè)默認(rèn)顯示的header。我這里是在使用response導(dǎo)出Excel的時(shí)候?qū)⑽募拖螺d狀態(tài)信息以自定義請(qǐng)求頭的形式放在了response的header里。
第二步
在Security的配置文件中初始化CorsFilter的Bean
@Bean public CorsFilter corsFilter() throws Exception { return new CorsFilter(); }
第三步
在Security的配置文件中添加Filter配置,和映射配置
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll() // 除上面外的所有請(qǐng)求全部需要鑒權(quán)認(rèn)證。 .and() 相當(dāng)于標(biāo)示一個(gè)標(biāo)簽的結(jié)束,之前相當(dāng)于都是一個(gè)標(biāo)簽項(xiàng)下的內(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() // 允許對(duì)于網(wǎng)站靜態(tài)資源的無(wú)授權(quán)訪問(wèn) .antMatchers( HttpMethod.GET, "/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js", "/webjars/springfox-swagger-ui/images/**","/swagger-resources/configuration/*","/swagger-resources",//swagger請(qǐng)求 "/v2/api-docs" ).permitAll() // 對(duì)于獲取token的rest api要允許匿名訪問(wèn) .antMatchers("/pmdbservice/auth/**","/pmdbservice/keywords/export3").permitAll() .antMatchers(HttpMethod.OPTIONS,"/**").permitAll() // 除上面外的所有請(qǐng)求全部需要鑒權(quán)認(rèn)證。 .and() 相當(dāng)于標(biāo)示一個(gè)標(biāo)簽的結(jié)束,之前相當(dāng)于都是一個(gè)標(biāo)簽項(xiàng)下的內(nèi)容 .anyRequest().authenticated().and() .addFilterBefore(corsFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); // 禁用緩存 httpSecurity.headers().cacheControl(); } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
聊聊Java 成員變量賦值和構(gòu)造方法誰(shuí)先執(zhí)行的問(wèn)題
這篇文章主要介紹了聊聊Java 成員變量賦值和構(gòu)造方法誰(shuí)先執(zhí)行的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10關(guān)于request.getRequestDispatcher().forward()的妙用及DispatcherType
這篇文章主要介紹了關(guān)于request.getRequestDispatcher().forward()的妙用及DispatcherType對(duì)Filter配置的影響,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Canal搭建?idea設(shè)置及采集數(shù)據(jù)到kafka的操作方法
這篇文章主要介紹了Canal搭建idea設(shè)置及采集數(shù)據(jù)到kafka的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05Java swing讀取txt文件實(shí)現(xiàn)學(xué)生考試系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java swing讀取txt文件實(shí)現(xiàn)學(xué)生考試系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06java web中使用cookie記住用戶(hù)的賬號(hào)和密碼
這篇文章主要介紹了java web中使用cookie記住用戶(hù)的賬號(hào)和密碼的相關(guān)資料,需要的朋友可以參考下2017-01-01Java實(shí)現(xiàn)動(dòng)態(tài)獲取文件的絕對(duì)路徑
我們知道在?Java?中讀取一些配置文件信息,是在開(kāi)發(fā)中十分常用的要求。這篇文章就來(lái)和大家聊聊Java如何實(shí)現(xiàn)動(dòng)態(tài)獲取文件的絕對(duì)路徑,感興趣的可以了解一下2023-02-02