ssm+vue 使用shiro後 post請求報錯 Request header field Content-Type is not allowed by Access-Control-Allow-H
ssm+vue 使用shiro後 post請求報錯 Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
出現的問題
當我傳送post請求時,控制檯就報錯這個,很明顯這個是跨域的問題。但是問題是我的跨域肯定是沒有問題的,已經使用了這麼多天,使用的都是跨域啊。而且使用get沒問題,登入(post方式)的時候也沒問題。我用fiddler觀察了介面,發現這個介面其實返回了json資訊 “登入超時,請重新登入”。這更讓我百思不得其解,我一度懷疑我的cors跨域是不是真的有問題,還是其他什麼莫名的bug。
經過長時間的排查和查詢資料後,我發現了問題的所在。
由於我這個專案比較特殊,是前後端分離並且跨域的,所以需要注意的點比較多,突然我找到一篇部落格,和我的專案很像,仔細看了下,似乎和我的問題一樣,是這位老哥的部落格https://blog.csdn.net/madonghyu/article/details/80027387
他在部落格裡說:發現當ajax請求為複雜請求時,cookie無法被攜帶傳輸到伺服器的,導致一直無法訪問。
我檢查了一下我的請求,用fiddler發現請求時果然沒有cookie
原因分析
其實,在post跨域請求時,一個請求會發送2次,第一次是驗證請求(options)這個請求時通知後臺,我要傳送跨域請求了,第二次才是真實的操作請求。
解決問題
既然是第一次的options沒通過導致的,那我就判斷是否是options請求,如果是就直接讓它通過。如果不是就走正常的程式
判斷是否是options請求的關鍵程式碼是
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
setHeader(httpRequest,httpResponse);
return true;
}
在這篇部落格裡,他是重寫shiro的UserFilter,實現通過OPTIONS請求,不是必須重寫這個方法,其他方法也行,在xml檔案裡配置就行了。
我是在重寫AuthorizationFilter裡使用的,程式碼如下
package com.wolwo.shiro;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wolwo.base.util.JsonResult;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
public class MyAuthorizationFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
/**
* 在訪問過來的時候檢測是否為OPTIONS請求,如果是就直接返回true
*/
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
setHeader(httpRequest,httpResponse);
return true;
}
//獲得當前物件
Subject subject = getSubject(servletRequest, servletResponse);
//判斷是否已登入
if (null != subject.getPrincipals()) {
return true;
}
//可在此處根據session中存放的使用者許可權,比對路徑,如果擁有該許可權則放行
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
throws IOException {
saveRequest(request);
setHeader((HttpServletRequest) request,(HttpServletResponse) response);
PrintWriter out = response.getWriter();
//物件轉json,傳給前臺接收
ObjectMapper mapper = new ObjectMapper();
JsonResult jsonResult = new JsonResult(JsonResult.NOTLOGIN, "登入超時,請重新登入");
String result = mapper.writeValueAsString(jsonResult);
out.println(result);
out.flush();
out.close();
return false;
}
/**
* 為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());
}
}
這樣請求就ok了