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