在一個先後端分離開發的項目中,使用SpringSecurity
作安全框架,用JWT
來實現權限管理提高RESTful Api的安全性。首先遇到的就是跨域問題,可是在攜帶jwt請求過程當中出現了服務端獲取不到jwt狀況。html
在開發過程當中遇到CORS
(跨域資源共享) 的問題,簡單的在服務器端設置了容許跨域訪問,可是在攜帶jwt請求過程當中出現
由於jwt是放在request header中,忽略了在跨域處理是加上容許本身定於的header字段前端
@Component public class CorsFilter implements Filter { Logger logger= LoggerFactory.getLogger(CorsFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request= (HttpServletRequest) servletRequest; HttpServletResponse response= (HttpServletResponse) servletResponse; response.setHeader("Access-Control-Allow-Origin",request.getHeader("origin")); response.setHeader("Access-Control-Allow-Origin","*"); //容許跨域訪問的域 response.setHeader("Access-Control-Allow-Methods","POST,GET,OPTIONS,DELETE,PUT"); //容許使用的請求方法 response.setHeader("Access-Control-Expose-Headers","*"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Cache-Control,Pragma,Content-Type,Authorization"); //容許使用的請求方法 response.setHeader("Access-Control-Allow-Credentials","true");//是否容許請求帶有驗證信息 filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } }
在網上搜索提到要對OPTIONS請求進行處理返回200,可是測試並無起到效果
這裏的OPTIONS請求實際上就是preflight請求java
可是問題依然沒有解決,出現以下
google了以後才知道preflight 請求的相關信息web
在咱們調用後臺接口的時候,常常會發現請求了兩次,其實第一次發送的就是preflight request(預檢請求)。spring
咱們都知道瀏覽器的同源策略,就是出於安全考慮,瀏覽器會限制從腳本發起的跨域HTTP請求,像XMLHttpRequest和Fetch都遵循同源策略。
瀏覽器限制跨域請求通常有兩種方式:數據庫瀏覽器限制發起跨域請求 跨域請求能夠正常發起,可是返回的結果被瀏覽器攔截了
通常瀏覽器都是第二種方式限制跨域請求,那就是說請求已到達服務器,並有可能對數據庫裏的數據進行了操做,可是返回的結果被瀏覽器攔截了,那麼咱們就獲取不到返回結果,這是一次失敗的請求,可是可能對數據庫裏的數據產生了影響。json爲了防止這種狀況的發生,規範要求,對這種可能對服務器數據產生反作用的HTTP請求方法,瀏覽器必須先使用OPTIONS方法發起一個預檢請求,從而獲知服務器是否容許該跨域請求:若是容許,就發送帶數據的真實請求;若是不容許,則阻止發送帶數據的真實請求。後端
瀏覽器將CORS請求分紅兩類:簡單請求和非簡單請求。api
請求方法是如下三種方法之一跨域
HEAD
GET
POST
HTTP的頭信息不超出如下幾種字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是不一樣時知足上面兩個條件,就屬於非簡單請求。
而瀏覽器對這兩種請求的處理是不同的。
非簡單請求是那種對服務器有特殊要求的請求,好比請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。
非簡單請求的CORS請求,會在正式通訊以前,增長一次HTTP查詢請求,稱爲"預檢"請求(preflight)
與cors相關更詳細的看參考底部連接
在咱們後臺用了Spring Security做爲安全框架,而且沒有對Preflight這個請求作出相應的處理,那麼這個請求會致使權限管控失敗。
處理起來也很簡單,只須要在spring security配置類configure方法中增長放行preflight請求
@Override protected void configure(HttpSecurity http) throws Exception { http // 因爲使用的是JWT,咱們這裏不須要csrf .csrf().disable() // 基於token,因此不須要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() // 全部 / 的全部請求 都放行 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() //對preflight放行 .antMatchers("/*").permitAll() .antMatchers("/u").denyAll() .antMatchers("/article/**").permitAll() .antMatchers("/video/**").permitAll() .antMatchers("/api/**").permitAll() .antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/**","/swagger-ui.html", "/webjars/**") .permitAll() .antMatchers("/manage/**").hasRole("ADMIN") // 須要相應的角色才能訪問 // 除上面外的全部請求所有須要鑑權認證 .anyRequest().authenticated(); // 禁用緩存 http.headers().cacheControl(); // 添加JWT filter http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); //添加未受權處理 http.exceptionHandling().authenticationEntryPoint(getAuthenticationEntryPoint()); //權限不足處理 http.exceptionHandling().accessDeniedHandler(getAccessDeniedHandler()); }
最終問題獲得解決!