2020 年 11 月 16 日

IT Skills 波林

Polin WEI – 資訊工作者的技術手札

Spring Boot Vue Axios 實現前後端分離的跨域訪問(CORS)

1 min read
springboot-response-axios

SpringBoot Vue Axios 實現前後端分離的跨域訪問(CORS-Cross-Origin Resource Sharing

 

在 使用 Eclipse 快速建立 Spring Starter Project 與相關的文章裡,可以見識到 Spring 在後端 Java 平台的快速開發;前端 在 Node JS 使用 vue-cli 3 快速開發網頁與佈署到 github-pages ,可以使用 vue-cli 3 與 Vue Router 去建立單一網頁,只需利用 Axios 的  ajax  技術向後端取得資料, 而 Vue Router 不需作跳轉頁面。要達成這樣前後端分離的要求,需要在 Spring boot 配置跨域訪問(CORS-Cross-Origin Resource Sharing

 

  • 建立 CorsFilter

因為跨域需設定 Access-Control-Allow-Credentials 為 true , 所以設定 Access-Control-Allow-Origin 允許來自那個網站發的請求時,不可以設為 ‘*’

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CorsFilter implements Filter {

 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  
  HttpServletResponse response = (HttpServletResponse) servletResponse;
  HttpServletRequest request= (HttpServletRequest) servletRequest;
  // addAllowedOrigin 不能設定為* 因為與 allowCredential 衝突
  response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
  response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
  response.setHeader("Access-Control-Allow-Headers", "*");        
  response.setHeader("Access-Control-Allow-Credentials", "true");
  response.setHeader("Access-Control-Max-Age", "180");
  filterChain.doFilter(servletRequest, servletResponse);
    }

}

 

  • 設定 WebSecurityConfigurerAdapter

code: 38-42 行建立 Bean: corsFilter ,並放在 code: 11 行加入自定義的 CorsFilter,因為還沒有寫認證程式,所以在 code: 35 行中對 "/auth/**" 的請求都允許。認證方式請參閱:Spring Boot 與 Vue + Axios 跨域訪問(CORS)並使用 JWT 解決 302(OPTIONS) 問題

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

 
 @Override
 protected void configure(HttpSecurity httpSecurity) throws Exception {
   httpSecurity
    //adds your custom CorsFilter
   .addFilterBefore(corsFilter(), (Class<? extends Filter>) SessionManagementFilter.class) 
   // CSRF Token
   .csrf()
   .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
   .authorizeRequests()
   .anyRequest().authenticated().and()                  
   .formLogin()
    .loginPage("/login")                    // 認證頁面指向頁頁                 
    .failureHandler(appsAuthenticationFailureHandler)                   
    .defaultSuccessUrl("/auth/home")                    
    .permitAll()
    .successHandler(appsAuthenticationSuccessHandler)
    .and()
   .logout()
    .logoutSuccessHandler(appsLogoutSuccessHandler)
    .and()
   .sessionManagement()
    .maximumSessions(1).sessionRegistry(sessionRegistry());

 }
    
 @Override
 public void configure(WebSecurity web) throws Exception {      
    // for axios CORS
    web.ignoring().antMatchers("/auth/**");
 }
 
 @Bean
 CorsFilter corsFilter() {
     CorsFilter filter = new CorsFilter();
     return filter;
 }
}

 

  • 設定 axios 的初始值

在 code: 13 行設定要向那個網域發出請求

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
 
Vue.use(VueAxios, axios)

if (process.env.NODE_ENV === 'production'){
  const CSRF_TOKEN = document.cookie.match(new RegExp(`XSRF-TOKEN=([^;]+)`)) == null ? "" : document.cookie.match(new RegExp(`XSRF-TOKEN=([^;]+)`))[1];
  axios.defaults.headers = { "X-XSRF-TOKEN": CSRF_TOKEN, "X-REQUEST-TYPE":"axios"};  
  axios.defaults.timeout = 5000;
} else {
  //axios.defaults.withCredentials = true  
  axios.defaults.baseURL = "https://im.your-domain-name.com/"; // 域名
}

 

  • 跨域請求(CORS-Cross-Origin Resource Sharing

在 code: 26 行 axios 向 spring boot 發出 request ,利用 Bootstrap-vue 的 Table 來顯示。 因為 axios 與 Vue 有整合所以可以用 self.$http.post ,當然也可以直接使用 this.axios.post

<template>
  <div class="about">
      <h1>This is an about page for axios request to spring boot</h1>    
    
    <div lass="col-6">
      <b-card class="text-center">
        <b-table striped hover :items="roleList"></b-table>
      </b-card>
    </div
    >
   </div>

</template>

<script>

export default {
  data() {      
      return {              
        roleList: [],
      };
    },
    mounted() {
        // 取得初始資料      
        let self = this;
        self.$http.post('/auth/security/authority/roles/list').then(response=> { self.roleList = response.data.roles;})     
        
    },
}
</script>

 

  • 觀察請求的表頭 ( Request Headers )

可以查到 Host: 是指向另一個網域

axios-request

 

  • 觀察回應的表頭 ( Response Headers )

可以查到 Access-Control-Allow-Origin 是來自 http://localhost:8080

springboot-response-axios

 

參考:

Spring Boot Security CORS

前後端分離 Spring Boot Vue 開發單頁面應用

 

Copyright © All rights reserved. | Newsphere by AF themes.