最重要的兩點:
- ajax請求跨域的時候,預設不會攜帶cookie。
- 請求分為普通請求(HttpRequest)和Ajax請求(XMLHttpRequest)
先屢一下跨域CAS認證的流程:
前端發起ajax請求,請求首先被跨域Filter過濾,加上Access-Control-Allow-Origin , 然後再被CASFilter過濾,此時沒有登入的使用者會被重定向到cas.host.cn/login進行登入,最最最關鍵的地方就在這裡。
通常是這樣的:前端發起HttpRequest,後端接受請求並執行res.sendRedirect(),前端接受相應併發起HttpRequest,請求重定向後的頁面,其中HttpRequest不存在跨域問題。
但是現在是這樣的:前端發起XMLHttpRequest, 後端接受請求並執行res.sendRedirect(),前端接受相應併發起XMLHttpRequest,請求重定向後的頁面,但是,由於cas.host.cn/login沒有配置跨域(安全上也不允許),所以產生了跨域!
解決思路:
思路就是把XMLHttpRequest替換為HttpRequest
方案:
@Path("admin/auth")
public class AuthController {
private static Properties prop = new Properties();
static {
InputStream in = CORSFilter.class.getClassLoader().getResourceAsStream("dev.properties");
try {
prop.load(in);
} catch (IOException e) {
e.printStackTrace();
}
}
@Get("validate")
public String validate(Invocation inv) throws JSONException {
HttpServletRequest request = inv.getRequest();
Assertion assertion = (Assertion) request.getSession().getAttribute("_const_cas_assertion_");
JSONObject result = new JSONObject();
if (assertion == null || assertion.getPrincipal().getName() == null) {
result.put("success",false);
result.put("data", prop.getProperty("cas.server.url") + "/login?service=" + prop.getProperty("server.url") + "/thoth-admin/admin/auth/redirect");
} else {
result.put("success", true);
}
return "@json:" + result.toString();
}
@Get("redirect")
public void redirect(Invocation inv) throws IOException {
inv.getResponse().sendRedirect(prop.getProperty("server.url")+"/thoth-admin/admin");
}
}
使用者每次發起ajax請求之前,先請求validate介面,檢驗當前使用者的cookie是否失效,如果沒有,就返回{success: true},前端則繼續傳送接下來的ajax請求。
如果失效了,就返回一個字串,內容是將要跳轉的cas驗證地址,前端則手動通過location.href = “這個地址”來進行HttpRequest。cas登入成功後,會再次重定向,重定向的地址是service=後面的地址。
另外,需要注意的一點是跨域Filter優先順序必須要高於CASFIlter,否則請求會先被CASFilter過濾,沒有登入過得使用者會被302跳轉到cas.mioffice.cn/login, 但是事實是你連跳轉的機會都沒有就會被瀏覽器告知你跨域了。
因為你的請求還沒有被跨域Filter過濾,此時響應頭裡還沒有Access-Control-Allow-Origin。