1. 程式人生 > >shiro學習分享(三)——解決跨域問題時遇到的坑

shiro學習分享(三)——解決跨域問題時遇到的坑

跨域問題的解決

使用springboot整合了shiro框架,springboot解決跨域的方法也是網上的到處都是的配置CORS解決跨域問題。

出現的問題:

使用了shiro框架,開啟了shiro的登陸驗證過濾器時,即filterChainDefinitionMap.put("/**","user");,代表要登陸過才能進行訪問,但是經過一番測試,發現當ajax請求為複雜請求時,cookie無法被攜帶傳輸到伺服器的,導致一直無法訪問。
在網上找了很久,大部分網友說複雜請求時若要帶cookie則allowedOrigins不能配置為“*”,而要是訪問的地址,雖然可以通過配置過濾器來實現,當是本地的html還是訪問不了,一直報跨域錯誤,而佈置在不同埠的html雖然能夠訪問,當是cookie依舊傳不了。。。
最後發現之所以傳不了cookie是因為shiro的許可權控制,由於複雜請求要傳兩次,第一次驗證請求(OPTIONS

)是沒有帶cookie的所以驗證不通過,導致接下來的真實請求無法繼續,因為上面的請求被拒絕了啊。。。

解決思路:

1.可以自定義shiro的UserFilter來讓OPTIONS請求無條件通過(shiro的自帶的fliter都是可以通過繼承來進行重寫)
樣例如下:

/**
 * 重寫shiro的UserFilter,實現通過OPTIONS請求
 * @author MDY
 */
public class ShiroUserFilter extends UserFilter {

    /**
     * 在訪問過來的時候檢測是否為OPTIONS請求,如果是就直接返回true
     */
@Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { HttpServletResponse httpResponse = (HttpServletResponse) response; HttpServletRequest httpRequest = (HttpServletRequest) request; if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) { setHeader(httpRequest,httpResponse); return
true; } return super.preHandle(request,response); } /** * 該方法會在驗證失敗後呼叫,這裡由於是前後端分離,後臺不控制頁面跳轉 * 因此重寫改成傳輸JSON資料 */ @Override protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException { saveRequest(request); setHeader((HttpServletRequest) request,(HttpServletResponse) response); PrintWriter out = response.getWriter(); out.println(JSONObject.toJSONString(ResultUtil.error(ExceptionEnum.IS_NOT_LOGIN))); out.flush(); out.close(); } /** * 為response設定header,實現跨域 */ private void setHeader(HttpServletRequest request,HttpServletResponse response){ //跨域的header設定 response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin")); response.setHeader("Access-Control-Allow-Methods", request.getMethod()); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers")); //防止亂碼,適用於傳輸JSON資料 response.setHeader("Content-Type","application/json;charset=UTF-8"); response.setStatus(HttpStatus.OK.value()); } }

2.由於博主的專案是前後端分離的,所以shiro關於url的許可權控制可有可無,所以博主幹脆把filterChainDefinitionMap.put("/**","user");去掉,這樣子所有人都能訪問,但是如果你使用了shiro的其它url許可權控制,如身份控制之類的以及shiro的許可權控制註解,,如果你沒有登陸則會丟擲UnauthenticatedException異常,這時可以使用全域性異常控制捕獲該異常,將出錯資訊傳回前端,雖然這樣子解決不是很優雅。。。(關於全域性異常處理網上也有很多教程,也可以看這篇)

最後附上在網上找到的springboot解決跨域問題的程式碼

/**
 * 解決跨域問題springboot所需配置
 */
@Configuration
public class CORSConfiguration {
    @Bean
    public WebMvcConfigurer CORSConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOrigins("*")
                        .allowedMethods("*")
                        .allowedHeaders("*")
                        //設定是否允許跨域傳cookie
                        .allowCredentials(true)
                        //設定快取時間,減少重複響應
                        .maxAge(3600);
            }
        };
    }
}

PS:博主發現sprinboot配置之後前端不用特殊的配置就能進行跨域,不知是不是瀏覽器的問題還是,但為了保險起見,ajax請求時還是加上crossDomain:true,xhrFields: { withCredentials: true },比較好。如果要帶上cookie跨域,則必須加上上面兩句。
請求樣例:

$.ajax({  
    async:true,  
    type:"post",  
    url:"", 
    data:JSON.stringify(params),  
    contentType: "application/json; charset=utf-8",

    crossDomain:true,
    xhrFields: {  withCredentials: true  },

    dataType:"json",  
    success:function(data){  
    console.log(params);
           console.log(data);
    },  
    error:function(data){  
        console.log(data)  
    }  

})