問題來源
web前后端工程的分離门驾,給傳統(tǒng)的web一體式開發(fā)或前后端工程師揉在一塊(前后端代碼在一個工程下,尤其前端開發(fā)需要在本地啟動后臺服務(wù)娃善,后臺也會被前端代碼分散注意力)的尷尬情況提出了解決方案论衍,同時也引來了一些不可避免的問題,而跨域問題就是其中之一会放。
設(shè)想一下饲齐,前端工程師正在開發(fā)登錄頁面的UI,基于nodejs(或基于nodejs的快速開發(fā)工具咧最,如vue-cli)捂人,可以很容易的在本地啟動一個服務(wù)端口用于頁面的開發(fā)。此時矢沿,前端代碼需要調(diào)用后臺的登錄接口做測試滥搭,于是將參考API文檔,拿到了登錄接口的相關(guān)信息(url=/login捣鲸,params={})準備調(diào)用后臺接口測試瑟匆,這時問題來了,如何遠程調(diào)用后臺接口栽惶,肯定不可能只配置一下axios(http工具)的baseUrl就完事愁溜,還要找到后臺的同學,請他將后臺web服務(wù)設(shè)置允許跨域請求外厂,不然你就會在瀏覽器控制臺收到類似下面的警告:
OPTIONS http://localhost:8088/login 404
Access to XMLHttpRequest at 'http://localhost:8088/login' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
解決方案
1冕象、讓前端工程師安裝nginx,設(shè)置http請求轉(zhuǎn)發(fā)汁蝶,這樣可以繞過瀏覽器跨域警告(不推薦渐扮,因為每一個前端開發(fā)都需要安裝nginx论悴,還需要學習配置nginx,透明度不夠)
2墓律、后端服務(wù)開啟跨域請求設(shè)置(推薦膀估,對于前端來說基本是透明的)
spring security如何解決跨域問題
環(huán)境說明
- java8
- spring boot 2.2.0.RELEASE
官方說明
通過官方文檔可以知道,配置是在spring security中耻讽,但是最終還是會交給spring mvc去處理
spring官方說明
繞坑察纯!
最開始以為這是很常見的問題,應(yīng)該很好解決齐饮,于是隨便baidu了一下捐寥,的確有一大堆相關(guān)的案例解決方案,基本思路和代碼大概有三種祖驱,這里就不詳細說了握恳,請自行百度相關(guān)問題。
百度出來的結(jié)果都大概看了下捺僻,也都試過乡洼,沒有一個可行,不知道是不是我的版本太新的原因匕坯。最后只好查看官方文檔(看英文有點慢束昵,需要加強啊)葛峻,意外的是锹雏,這次官方給出的說明很簡單也很直接(show you the best practice code),但是也沒有解決問題术奖。于是再次結(jié)合百度出來的結(jié)果礁遵,然后分析瀏覽器給出的錯誤警告內(nèi)容,將官方代碼稍作了調(diào)整后采记,跨域配置才終于生效佣耐。
code
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ActiveProfile activeProfile;
@Override
protected void configure(HttpSecurity http) throws Exception {
//你的其他配置
......
//dev config
if (activeProfile.isDev()) {
//允許跨域請求
// by default uses a Bean by the name of corsConfigurationSource(官方說明,使下面配置的bean生效)
http.cors(Customizer.withDefaults());
}
}
@Profile("dev")
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");//修改為添加而不是設(shè)置唧龄,* 最好改為實際的需要兼砖,我這是非生產(chǎn)配置,所以粗暴了一點
configuration.addAllowedMethod("*");//修改為添加而不是設(shè)置
configuration.addAllowedHeader("*");//這里很重要既棺,起碼需要允許 Access-Control-Allow-Origin
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
還沒有結(jié)束
上述配置沒有問題讽挟,到目前為止,服務(wù)器端已經(jīng)完全支持跨域請求了丸冕,但是的確還沒有結(jié)束耽梅。應(yīng)為筆者在測試過程中又遇到了坑,下面來具體談一談晨仑。
提示:如果你的前端用到了axios,一定注意axios雖然默認的content-type='application/x-www-form-urlencoded'
,但是body中的參數(shù)是以json字符串的格式提交的洪己,請使用 URLSearchParams 封裝妥凳,如下:
const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);
回到正題,基于上面的配置答捕,現(xiàn)在登錄已經(jīng)沒有了問題逝钥,但是在跨域訪問api時會發(fā)現(xiàn)接口返回302,接口被重定向的原因經(jīng)過查看spring security的dubg日志發(fā)現(xiàn)是因為該接口是匿名訪問拱镐,也就是說你未登錄艘款。但是剛才也的確登錄成功了,而且瀏覽器cookie當中還有我們的sessionid沃琅,再仔細觀察發(fā)現(xiàn)api接口發(fā)送未帶cookie哗咆,這才是接口被解決的真正原因而并非真的沒有登錄。查看官方對于跨域的說明后益眉,發(fā)現(xiàn)跨域訪問默認是不會攜帶cookie的晌柬,除非你設(shè)置參數(shù)告訴瀏覽器我要帶cookie去跨域訪問資源,代碼如下:
//全局配置郭脂,告訴瀏覽器無論如何都要攜帶cookie去請求資源
axios.defaults.withCredentials=true
或者
//也可以在單獨的請求顯示指明 withCredentials=true
self.axios({
method: 'post',
url: '/api/getSomething',
data: params,
withCredentials: true//
}).then(function (rep) {
//your code......
});
如果你使用的是webpack或者基于webpack或nodejs構(gòu)建的開發(fā)工具(如:vue-cli)
你可以通過配置代理年碘,這樣的好處是你可以省略對 withCredentials 參數(shù)的配置,例:
//vue.config.js
module.exports = {
devServer: {
proxy: 'http://localhost:4000'
}
}
關(guān)于該配置的詳細說明參考:
http-proxy-middleware