Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
SSO
? SSO英文全稱Single Sign On,單點登錄。SSO是在多個應(yīng)用系統(tǒng)中,用戶只需要登錄一次就可以訪問所有相互信任的應(yīng)用系統(tǒng)。它包括可以將這次主要的登錄映射到其他應(yīng)用中用于同一個用戶的登錄的機(jī)制。它是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一。
體系結(jié)構(gòu)
? 當(dāng)用戶第一次訪問應(yīng)用系統(tǒng)1的時候,因為還沒有登錄,會被引導(dǎo)到認(rèn)證系統(tǒng)中進(jìn)行登錄;根據(jù)用戶提供的登錄信息,認(rèn)證系統(tǒng)進(jìn)行身份校驗,如果通過校驗,應(yīng)該返回給用戶一個認(rèn)證的憑據(jù)--token;用戶再訪問別的應(yīng)用的時候就會將這個token帶上,作為自己認(rèn)證的憑據(jù),應(yīng)用系統(tǒng)接受到請求之后會把token送到認(rèn)證系統(tǒng)進(jìn)行校驗,檢查token的合法性。如果通過校驗,用戶就可以在不用再次登錄的情況下訪問應(yīng)用系統(tǒng)2和應(yīng)用系統(tǒng)3了 。
Token(令牌)
token的意思是“令牌”,是服務(wù)端生成的一串字符串,作為客戶端進(jìn)行請求的一個標(biāo)識。
當(dāng)用戶第一次登錄后,服務(wù)器生成一個token并將此token返回給客戶端,客戶端收到token后把它存儲起來,可以放在cookie或者Local Storage(本地存儲)里。 以后客戶端只需帶上這個token前來請求數(shù)據(jù)即可,無需再次帶上用戶名和密碼。
簡單token的組成;uid(用戶唯一的身份標(biāo)識)、time(當(dāng)前時間的時間戳)、sign(簽名,token的前幾位以哈希算法壓縮成的一定長度的十六進(jìn)制字符串。為防止token泄露)。
設(shè)計token的值可以有以下方式
同域SSO原理分析
實際上,HTTP協(xié)議是無狀態(tài)的,單個系統(tǒng)的會話由服務(wù)端Session進(jìn)行維持,Session保持會話的原理是通過Cookie把sessionId寫入瀏覽器,每次訪問都會自動攜帶全部Cookie,在服務(wù)端讀取其中的sessionId進(jìn)行驗證實現(xiàn)會話保持。同域下單點登錄其實就是手寫token代替sessionId進(jìn)行會話認(rèn)證。
token的生成
服務(wù)端生成token后,將token與user對象存儲在Map結(jié)構(gòu)中,token為Key,user對象為value,response.addCookie()生成新的Cookie,名為token,值為token的值。
token過期移除
將服務(wù)端的token從Map中移除,再刪除瀏覽器端的名為token的Cookie。
認(rèn)證流程
跨域SSO原理分析
當(dāng)有多個系統(tǒng)時,認(rèn)證機(jī)制的流程如下:
分析
當(dāng)系統(tǒng)有多個并且在不同域(domain)時,Cookie只會作用在當(dāng)前域下。
將token寫入所有域的Cookie中才是解決跨域SSO的核心。
如何讀取Cookie?
通過Servlet中的request對象可以讀取到Cookie數(shù)組,然后foreach遍歷讀取,一般只是獲取到nam和value,其他信息寫入到瀏覽器后,瀏覽器不主動再發(fā)回來,讀取并無意義。
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
System.out.println(
cookie.getName() +
cookie.getValue() +
cookie.getMaxAge() +
cookie.getPath() +
cookie.getDomain() +
cookie.getSecure() +
cookie.isHttpOnly()//客戶端js是否可以獲取
);
}
}
復(fù)制代碼
如何寫入Cookie帶瀏覽器?
新建Cookie對象設(shè)置一系列屬性,然后添加到response中去。需要注意的是,當(dāng)設(shè)置path為“/”時,表示所有路徑都會被該Cookie作用到,如果設(shè)置為/path1那么由/path2發(fā)起請求就不會攜帶該Cookie。默認(rèn)不設(shè)置只作用在當(dāng)前路徑下。
Cookie cookie = new Cookie("myCookieName","myCookieValue");
cookie.setHttpOnly(false);//Javascript不能處理
//一個正值表示cookie將在經(jīng)過許多秒之后過期。注意,值是cookie過期的最大時間,而不是cookie當(dāng)前的時間。
//負(fù)值表示cookie沒有持久存儲,在Web瀏覽器退出時將被刪除。零值會導(dǎo)致刪除cookie。
cookie.setMaxAge(-1000);
cookie.setSecure(false);//如果為true,僅支持HTTPS協(xié)議
//cookie對指定目錄中的所有頁面以及該目錄子目錄中的所有頁面都可見。
cookie.setPath("/");
//cookie.setDomain("www.a.com");//默認(rèn)情況下,cookie只返回給發(fā)送cookie的服務(wù)器。
response.addCookie(cookie);
復(fù)制代碼
修改Cookie
修改更新Cookie時,除了要保證Cookie的name是相同的,也要保證Cookie的一系列屬性是相同的,否則瀏覽器會生成新的Cookie。
刪除Cookie
只需要設(shè)置Cookie的MaxAge為負(fù)值,意味著是過去的Cookie,瀏覽器就會清除。
1.利用HTML的script標(biāo)簽跨域?qū)慍ookie
比如當(dāng)前域是www.a.com,下面的script標(biāo)簽是跨域?qū)慶ookie的核心,通過此標(biāo)簽實現(xiàn)了向www.b.com域?qū)懭隿ookie:
<script type="text/javascript" src="http://www.b.com/setCookie?cname=token&cval=123456"></script>
復(fù)制代碼
P3P協(xié)議
? P3P是一種被稱為個人隱私安全平臺項目(the Platform for Privacy Preferences)的標(biāo)準(zhǔn),能夠保護(hù)在線隱私權(quán),使Internet沖浪者可以選擇在瀏覽網(wǎng)頁時,是否被第三方收集并利用自己的個人信息。如果一個站點不遵守P3P標(biāo)準(zhǔn)的話,那么有關(guān)它的Cookies將被自動拒絕,并且P3P還能夠自動識破多種Cookies的嵌入方式。p3p是由全球資訊聯(lián)盟網(wǎng)所開發(fā)的。
舉個例子:
? 我們在訪問A網(wǎng)站時,理論上說,我們只能把Cookie信息保存到A站域名下,而不能寫入到B網(wǎng)站下。如果想要跨域讀寫Cookie,只是通過script標(biāo)簽變相訪問B網(wǎng)站在一些瀏覽器是行不通的,此時B網(wǎng)站的服務(wù)器應(yīng)該告訴瀏覽器允許A網(wǎng)站寫入Cookie,否則瀏覽器將會拒絕執(zhí)行,這就是P3P協(xié)議。
服務(wù)端如何告訴瀏覽器?
? P3P提供了一種簡單的方式 ,來加載用戶隱私策略,只要在http響應(yīng)的頭信息中增加 response.setHeader("P3P","CP=NON DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONa HISa TELa OTPa OUR UNRa IND UNI COM NAV INT DEM CNT PRE LOC);而無需指定隱私策略文件也可以達(dá)到指定隱私策略的目的。 CP=后面的字符串分別代表不同的策略信息。
總結(jié)
因為P3P協(xié)議所以不能保證所有瀏覽器都能通過script標(biāo)簽方式跨域?qū)慍ookie,有的瀏覽器本身就是拒絕跨域的。
顯然這種方式是不能保證跨域?qū)慶ookie的成功性。
2.通過URL參數(shù)實現(xiàn)跨域信息傳遞
我們要在A域?qū)崿F(xiàn)寫入token到B域,需要在A域設(shè)計一個servlet接收請求,代碼:
@WebServlet(name = "tg")
public class Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
//獲取請求的目標(biāo)域
String from = request.getParameter("from");
//生成token,
String token = "123456";
//重定向到目標(biāo)域
response.sendRedirect(from + "?cname=token&cval=" + token);
}
...
}
復(fù)制代碼
由a域發(fā)起請求,請求地址:http://www.a.com/tg?from=http://www.b.com/set_cookie, 請求后該Servlet會獲取from參數(shù)的值并生成token最后讓客戶端重定向到http://www.b.com/set_cookie?cname=token&cval=123456,然后B域的Servlet("set_cookie")獲取Url參數(shù)寫入Cookie到客戶端,代碼:
//將要寫入的cookie項,調(diào)用者通過參數(shù)傳遞
String cookieName = request.getParameter("cname");
String cookieValue = request.getParameter("cval");
//生成cookie
Cookie cookie = new Cookie(cookieName,cookieValue);
cookie.setPath("/");
//一般可以將domain設(shè)置到頂級域
//cookie.setDomain("www.b.com");
response.addCookie(cookie);
復(fù)制代碼
這時候再查看B域下的Cookie就可以發(fā)現(xiàn)(token=123456)已經(jīng)被寫入到瀏覽器。
3.讀取其它域的Cookie
利用script標(biāo)簽
利用script標(biāo)簽執(zhí)行另一個域?qū)崿F(xiàn)的讀取cookie方法,script標(biāo)簽返回結(jié)果將是變量定義形式的JS代碼,每一個變量表示一個cookie項,這些代碼加載后,此頁面后續(xù)JS代碼可以直接在script腳本中讀取已定義的變量值,即各cookie值。
<script type="text/javascript" src="http://www.b.com/reaf_cookies"></script>
復(fù)制代碼
HTML頁面讀取
<script>
alert(token);
</script>
復(fù)制代碼
B域的url為/read_cookies的Servlet是如何實現(xiàn)的?
如圖,首先我們先在request中獲取cookie數(shù)組,然后for循環(huán)遍歷拼接為類似var token='test123';的字符串。最重要的是設(shè)置ContentType為application/javascript,代碼如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
StringBuilder stringBuilder = new StringBuilder();
//一定要設(shè)置響應(yīng)類型,否則可能導(dǎo)致IE不解析js直接進(jìn)行下載操作
response.setContentType("application/javascript");
if (cookies != null) {
for (Cookie cookie : cookies) {
//結(jié)果類似于這樣 var token='123456';
stringBuilder.append("var ")
.append(cookie.getName())
.append("=")
.append("'")
.append(cookie.getValue())
.append("'")
.append(";");
}
response.getWriter().append(stringBuilder.toString());
}
}
復(fù)制代碼
1.Jsonp的方式
跨域Ajax請求在瀏覽器階段就會被阻止,我們可以通過script標(biāo)簽返回想要的json數(shù)據(jù)。如圖:
<script type="text/javascript" src="http://www.b.com/user_info_2"></script>
復(fù)制代碼
后臺Servlet代碼
//要正確設(shè)置響應(yīng)類型,避免IE出現(xiàn)下載
response.setContentType("application/javascript");
String userInfo = "{\"id\":1,\"name\":\"zhangsan\"}";
//返回拼接的javascript語句字符串,語句本身執(zhí)行一個調(diào)用函數(shù)的操作
String ret = "showResult("+userInfo+")";
復(fù)制代碼
在Servlet中設(shè)置返回類型為javascript,并正常獲取json格式的數(shù)據(jù),最關(guān)鍵的是在最后拼接為js語句字符串,語句本身就是執(zhí)行一個調(diào)用函數(shù)的操作:
showResult({"id":1,"name":"zhangsan"})
復(fù)制代碼
而showResult(ret)回調(diào)函數(shù)自然需要我們在之前就定義好:
<script>
function showResult(ret){
console.log(ret)
}
</script>
復(fù)制代碼
優(yōu)化
這種方式,前端的回調(diào)函數(shù)和后端耦合度較高。前端可以在調(diào)用后端方法時帶上回調(diào)函數(shù)名(?callback=xxxxx),后端優(yōu)化后的代碼:
//通過參數(shù)傳遞回調(diào)函數(shù)名,一定程度降低了前后端代碼的耦合度
String callback = request.getParameter("callback");
//返回拼接的javascript語句字符串,語句本身執(zhí)行一個調(diào)用函數(shù)的操作
String ret = callback+"("+userInfo+")";
復(fù)制代碼
再優(yōu)化
HTML頁面加載到我們定義的script標(biāo)簽時就會執(zhí)行我們的回調(diào)方法,更多時候我們想要控制回調(diào)方法的執(zhí)行時機(jī)。這個問題可以通過前端動態(tài)生成節(jié)點來解決,當(dāng)我們執(zhí)行完之后再移除節(jié)點即可:
<script>
var script = document.createElement("script");
script.src = "http://www.b.com/user_info_2?callback=showResult";
document.body.appendChild(script);
script.onload = function () {
document.body.removeChild(script);
}
</script>
復(fù)制代碼
JQuery
我們可以把這些封裝到一個方法里,隨時調(diào)用。這里可以使用Jquery封裝好的API。
$.ajax({
url: "http://localhost:9090/query",
type: "GET",
dataType: "jsonp", //指定服務(wù)器返回的數(shù)據(jù)類型
jsonpCallback: "showData", //指定回調(diào)函數(shù)名稱
success: function (data) {
console.info("調(diào)用success");
}
});
function showData(data){
var result = JSON.stringify(data);
}
復(fù)制代碼
2.CORS簡介
出于安全原因,瀏覽器限制從腳本內(nèi)發(fā)起的跨源HTTP請求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 這意味著使用這些API的Web應(yīng)用程序只能從加載應(yīng)用程序的同一個域請求HTTP資源,除非使用CORS頭文件。
跨域資源共享( CORS)機(jī)制允許 Web 應(yīng)用服務(wù)器進(jìn)行跨域訪問控制,從而使跨域數(shù)據(jù)傳輸?shù)靡园踩M(jìn)行。瀏覽器支持在 API 容器中(例如 XMLHttpRequest或 Fetch)使用 CORS,以降低跨域 HTTP 請求所帶來的風(fēng)險。
GET跨域請求原理
當(dāng)客戶端瀏覽器發(fā)起一個跨域的HTTP請求,瀏覽器經(jīng)過請求響應(yīng),如果沒有看到Access-Control-Allow-Origin的header頭部,會認(rèn)為你的請求是不合法的。換句話說,我們只要在被請求的服務(wù)器上設(shè)置這個頭部,瀏覽器就會允許我們進(jìn)行請求。
解決方法
對于簡單的請求,我們直接在服務(wù)端 設(shè)置就可以了。如圖,只要請求的地址是www.a.com就會被瀏覽器允許跨域。如果想要允許對于多個來源可以用,號進(jìn)行隔開;如果想要允許所有來源,設(shè)置為*就可以,不過建議不要使用,這樣會造成安全隱患。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//簡單請求,直接設(shè)置Access-Control-Allow-Origin就可以了
response.setHeader("Access-Control-Allow-Origin","*");
//要正確設(shè)置響應(yīng)類型,避免IE出現(xiàn)下載
response.setContentType("application/json");
response.getWriter().write("{\"id\":1,\"name\":\"zhangsan\"}");
}
復(fù)制代碼
對于復(fù)雜的請求,比如POST,或者加入了自定義header頭部,上面的方法就不適用了。下面繼續(xù)看。
CORS流程
請求發(fā)起時,瀏覽器先判斷當(dāng)前是否是跨域的AJAX;
如果是,判斷是否是普通類型請求(GET類型,無自定義頭數(shù)據(jù));
普通請求,直接發(fā)起GET到服務(wù)端,在響應(yīng)頭中尋找 Access-Contro-Alow- Origin,如果有且允許,處理響應(yīng)結(jié)果;
不是普通請求(非GET類型,或有自定義頭), 先 PreFlight(即發(fā)起一個 method= OPTIONS)的請求,
要求返回 Access-Control-Allow- Methods和 Access-Control-Allow- Headers, 內(nèi)容體為空
PreFlight正確執(zhí)行后, 再發(fā)起GET請求, 獲得響應(yīng)結(jié)果, 并處理結(jié)果.
實現(xiàn)
歸根到我們的代碼中的實現(xiàn),只需要在servlet中定義options請求的處理方法即可。如圖
protected void doOptions(HttpServletRequest req, HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Methods","GET,POST,OPTIONS,DELETE");
response.setHeader("Access-Control-Allow-Headers","reqid,xxx");
}
復(fù)制代碼
注意:Access-Control-Allow-Origin是必需的。
3.兩種跨域AJax請求對比
兼容性
Jsonp對所有瀏覽器兼容,CORS對現(xiàn)代瀏覽器兼容(IE8之后)。
請求方式
Jsonp只支持GET方式,CORS支持GET,POST等。
調(diào)用方式
Jsonp需要服務(wù)端封裝返回信息,CORS更像原生AJax一樣使用。
作者:風(fēng)平浪靜如碼
鏈接:https://juejin.im/post/5e81e82551882573a1377a08
* JSON 使用 JavaScript 語法來描述數(shù)據(jù)對象,但是 JSON 仍然獨立于語言和平臺。JSON 解析器和 JSON 庫支持許多不同的編程語言。
合格的json對象:
["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 }
{"names": ["張三", "李四"] }
[ { "name": "張三"}, {"name": "李四"} ]
不合格的json對象:
{ name: "張三", 'age': 32 } // 屬性名必須使用雙引號
[32, 64, 128, 0xFFF] // 不能使用十六進(jìn)制值
{ "name": "張三", "age": undefined } // 不能使用undefined
{ "name": "張三",
"birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
"getName": function() {return this.name;} // 不能使用函數(shù)和日期對象
}
JavaScript中關(guān)于JSON對象和字符串轉(zhuǎn)換的兩個方法:
JSON.parse(): 用于將一個 JSON 字符串轉(zhuǎn)換為 JavaScript 對象
JSON.parse('{"name":"run1"}');
JSON.parse('{name:"run1"}') ; // 錯誤
JSON.parse('[18,undefined]') ; // 錯誤
JSON.stringify(): 用于將 JavaScript 值轉(zhuǎn)換為 JSON 字符串。
JSON.stringify({"name":"run1"})
JSON 格式于2001年由 Douglas Crockford 提出,目的就是取代繁瑣笨重的 XML 格式。
JSON 格式有兩個顯著的優(yōu)點:書寫簡單,一目了然;符合 JavaScript 原生語法,可以由解釋引擎直接處理,不用另外添加解析代碼。所以,JSON迅速被接受,已經(jīng)成為各大網(wǎng)站交換數(shù)據(jù)的標(biāo)準(zhǔn)格式,并被寫入ECMAScript 5,成為標(biāo)準(zhǔn)的一部分。
XML和JSON都使用結(jié)構(gòu)化方法來標(biāo)記數(shù)據(jù),下面來做一個簡單的比較。
用XML表示中國部分省市數(shù)據(jù)如下:
<?xml version="1.0" encoding="utf-8"?>
<country>
<name>中國</name>
<province>
<name>黑龍江</name>
<cities>
<city>哈爾濱</city>
<city>大慶</city>
</cities>
</province>
<province>
<name>廣東</name>
<cities>
<city>廣州</city>
<city>深圳</city>
<city>珠海</city>
</cities>
</province>
<province>
<name>臺灣</name>
<cities>
<city>臺北</city>
<city>高雄</city>
</cities>
</province>
<province>
<name>新疆</name>
<cities>
<city>烏魯木齊</city>
</cities>
</province>
</country>
用JSON表示如下
{
"name": "中國",
"province": [{
"name": "黑龍江",
"cities": {
"city": ["哈爾濱", "大慶"]
}
}, {
"name": "廣東",
"cities": {
"city": ["廣州", "深圳", "珠海"]
}
}, {
"name": "臺灣",
"cities": {
"city": ["臺北", "高雄"]
}
}, {
"name": "新疆",
"cities": {
"city": ["烏魯木齊"]
}
}]
}
由上面的兩段代碼可以看出,JSON 簡單的語法格式和清晰的層次結(jié)構(gòu)明顯要比 XML 容易閱讀,并且在數(shù)據(jù)交換方面,由于 JSON 所使用的字符要比 XML 少得多,可以大大得節(jié)約傳輸數(shù)據(jù)所占用的帶寬。
AJAX(Asynchronous Javascript And XML)翻譯成中文就是“異步Javascript和XML”。即使用Javascript語言與服務(wù)器進(jìn)行異步交互,傳輸?shù)臄?shù)據(jù)為XML(當(dāng)然,傳輸?shù)臄?shù)據(jù)不只是XML)。
AJAX除了異步的特點外,還有一個就是:瀏覽器頁面局部刷新;(這一特點給用戶的感受是在不知不覺中完成請求和響應(yīng)過程)
示例:
頁面輸入兩個整數(shù),通過AJAX傳輸?shù)胶蠖擞嬎愠鼋Y(jié)果并返回。
HTML部分代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AJAX局部刷新實例</title>
</head>
<body>
<input type="text" id="i1">+
<input type="text" id="i2">=
<input type="text" id="i3">
<input type="button" value="AJAX提交" id="b1">
<script src="/static/jquery-3.2.1.min.js"></script>
<script>
$("#b1").on("click", function () {
$.ajax({
url:"/ajax_add/",
type:"GET",
data:{"i1":$("#i1").val(),"i2":$("#i2").val()},
success:function (data) {
$("#i3").val(data);
}
})
})
</script>
</body>
</html>
views.py
def ajax_demo1(request):
return render(request, "ajax_demo1.html")
def ajax_add(request):
i1 = int(request.GET.get("i1"))
i2 = int(request.GET.get("i2"))
ret = i1 + i2
return JsonResponse(ret, safe=False)
urls.py
urlpatterns = [
...
url(r'^ajax_add/', views.ajax_add),
url(r'^ajax_demo1/', views.ajax_demo1),
...
]
$.ajax({
url:"", // 控制往哪里提交
type:"POST", // 請求的方法
data:{}, // 請求的參數(shù)
success:function(arg){
// 收到響應(yīng)之后要做的事
}
})
搜索引擎根據(jù)用戶輸入的關(guān)鍵字,自動提示檢索關(guān)鍵字。
還有一個很重要的應(yīng)用場景就是注冊時候的用戶名的查重。
其實這里就使用了AJAX技術(shù)!當(dāng)文件框發(fā)生了輸入變化時,使用AJAX技術(shù)向服務(wù)器發(fā)送一個請求,然后服務(wù)器會把查詢到的結(jié)果響應(yīng)給瀏覽器,最后再把后端返回的結(jié)果展示出來。
當(dāng)輸入用戶名后,把光標(biāo)移動到其他表單項上時,瀏覽器會使用AJAX技術(shù)向服務(wù)器發(fā)出請求,服務(wù)器會查詢名為lemontree7777777的用戶是否存在,最終服務(wù)器返回true表示名為lemontree7777777的用戶已經(jīng)存在了,瀏覽器在得到結(jié)果后顯示“用戶名已被注冊!”。
優(yōu)點
最基本的jQuery發(fā)送AJAX請求示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ajax test</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<button id="ajaxTest">AJAX 測試</button>
<script>
$("#ajaxTest").click(function () {
$.ajax({
url: "/ajax_test/",
type: "POST",
data: {username: "Q1mi", password: 123456},
success: function (data) {
alert(data)
}
})
})
</script>
</body>
</html>
views.py
def ajax_test(request):
user_name = request.POST.get("username")
password = request.POST.get("password")
print(user_name, password)
return HttpResponse("OK")
$.ajax參數(shù)
data參數(shù)中的鍵值對,如果值值不為字符串,需要將其轉(zhuǎn)換成字符串類型。
$("#b1").on("click", function () {
$.ajax({
url:"/ajax_add/",
type:"GET",
data:{"i1":$("#i1").val(),"i2":$("#i2").val(),"hehe": JSON.stringify([1, 2, 3])},
success:function (data) {
$("#i3").val(data);
}
})
})
方式1
通過獲取隱藏的input標(biāo)簽中的csrfmiddlewaretoken值,放置在data中發(fā)送。
$.ajax({
url: "/cookie_ajax/",
type: "POST",
data: {
"username": "rum2",
"password": 123456,
"csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val() // 使用JQuery取出csrfmiddlewaretoken的值,拼接到data中
},
success: function (data) {
console.log(data);
}
})
通過獲取返回的cookie中的字符串 放置在請求頭中發(fā)送。
注意:需要引入一個jquery.cookie.js插件。
$.ajax({
url: "/cookie_ajax/",
type: "POST",
headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 從Cookie取csrf_token,并設(shè)置ajax請求頭
data: {"username": "rum", "password": 123456},
success: function (data) {
console.log(data);
}
})
或者用自己寫一個getCookie方法:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
每一次都這么寫太麻煩了,可以使用$.ajaxSetup()方法為ajax請求統(tǒng)一設(shè)置。
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Django內(nèi)置的serializers
def books_json(request):
book_list = models.Book.objects.all()[0:10]
from django.core import serializers
ret = serializers.serialize("json", book_list)
return HttpResponse(ret)
我們的數(shù)據(jù)中經(jīng)常有日期時間,也就是datetime對象,而json.dumps是無法處理這樣在類型的,那就需要通過自定義處理器來做擴(kuò)展,如下:
class JsonCustomEncoder(json.JSONEncoder):
"""
自定義一個支持序列化時間格式的類
"""
def default(self, o):
if isinstance(o, datetime):
return o.strftime("%Y-%m-%d %H:%M:%S")
elif isinstance(o, date):
return o.strftime("%Y-%m-%d")
else:
return json.JSONEncoder.default(self, o)
def books_json(request):
book_list = models.Book.objects.all().values_list("title", "publish_date")
ret = json.dumps(list(book_list), cls=JsonCustomEncoder)
return HttpResponse(ret)
Bootstrap-sweetalert
https://github.com/lipis/bootstrap-sweetalert
$(".btn-danger").on("click", function () {
swal({
title: "你確定要刪除嗎?",
text: "刪除可就找不回來了哦!",
type: "warning",
showCancelButton: true,
confirmButtonClass: "btn-danger",
confirmButtonText: "刪除",
cancelButtonText: "取消",
closeOnConfirm: false
},
function () {
var deleteId = $(this).parent().parent().attr("data_id");
$.ajax({
url: "/delete_book/",
type: "post",
data: {"id": deleteId},
success: function (data) {
if (data.status === 1) {
swal("刪除成功!", "你可以準(zhǔn)備跑路了!", "success");
} else {
swal("刪除失敗", "你可以再嘗試一下!", "error")
}
}
})
});
})
#科技##軟件開發(fā)##python#
jQuery的Ajax請求實際上是封裝的一個XHLHttpRequest,從字面意思來看是,XML是一種文件格式,Http是一個網(wǎng)絡(luò)請求,Request最開始是用于請求XML文件的,后來不斷擴(kuò)展可以請求多種類型的文件。
XHLHttpRequest,網(wǎng)絡(luò)請求模塊,或者是一個瀏覽器網(wǎng)絡(luò)線程。用于從URL網(wǎng)絡(luò)上獲取數(shù)據(jù),有了對象,就可以實現(xiàn)頁面的無刷新請求數(shù)據(jù),就可以做到一個局部的更新,也可以獲取多種類型的數(shù)據(jù)(ftp、文件)。
它是一個對象,首先我們需要實例化,定義一個:
var xhr = new XMLHttpRequest();
//Ajax請求時的參數(shù)設(shè)置 method 請求方式 url 請求地址 async 是否異步 user 用戶名 password 密碼 ? xhr.open(method,url,async,user,password);
xhr.send(data); 等待接收
一般有如下屬性:
是一個文件,如果響應(yīng)頭內(nèi)容是text/xml,這個屬性將保留響應(yīng)數(shù)據(jù)的XML、DOM文檔。
xhr.onreadystatechange = () => { if (xhr.readyStatus === 4) { /* HTTP 狀態(tài)在 200-300 之間表示請求成功 */ /* HTTP 狀態(tài)為 304 表示請求內(nèi)容未發(fā)生改變,可直接從緩存中讀取 */ if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { console.log('請求成功', xhr.responseText) } } }
function ajax (options) { /* 獲取請求地址 */ let url = options.url; /* 獲取請求方法 */ const method = options.method.toLocaleLowerCase() || 'get'; /* 默認(rèn)異步 */ const async = options.async != false; /* 獲取請求request數(shù)據(jù) */ const data = options.data; /* 實例化XMLHttpRequest */ const xhr = new XMLHttpRequest(); /* 設(shè)置超時時間 */ if (options.timeout && options.timeout > 0) { xhr.timeout = options.timeout } ? return new Promise ( (resolve, reject) => { /* 添加超時回調(diào) */ xhr.ontimeout = () => reject && reject('請求超時'); /* 成功回調(diào) */ xhr.onreadystatechange = () => { if (xhr.readyState == 4) { if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { resolve && resolve(xhr.responseText); } else { reject && reject(); } } } ? /* 失敗回調(diào) */ xhr.onerror = err => reject && reject(err); ? /* 拼接參數(shù) */ let paramArr = []; let encodeData; if (data instanceof Object) { for (let key in data) { paramArr.push( encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) ); } encodeData = paramArr.join('&'); } ? /* get請求 */ if (method === 'get') { const index = url.indexOf('?') if (index === -1) url += '?' else if (index !== url.length -1) url += '&' url += encodeData } ? /* 初始化請求 */ xhr.open(method, url, async); ? /* get直接發(fā)送拼接的URL */ if (method === 'get') xhr.send(null); ? if (method === 'post') { xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8') xhr.send(encodeData) } }) }
總結(jié):關(guān)于網(wǎng)絡(luò)請求,一般常用的有四種:
一個是get,獲取網(wǎng)絡(luò)資源
然后是post,一般用于提交數(shù)據(jù),提交數(shù)據(jù)的參數(shù)是放在請求的body里面的
delete,刪除
put,標(biāo)準(zhǔn)的一個修改
關(guān)于Http的簡單請求和非簡單請求或者是跨域,在跨域時由于瀏覽器的安全機(jī)制,涉及到一個問題關(guān)于在Ajax請求 ,是在請求之后還是請求之前判斷跨域,這時候就需要了解一下簡單請求和復(fù)雜請求。
簡單請求是先把資源請求回來,然后再去判斷是否跨域。如果是一個復(fù)雜請求,瀏覽器會先去發(fā)送一個嗅探或者是欲請求(一般是一個option),先判斷是否跨域,如果不是跨域那就執(zhí)行,如果是跨域那就直接不請求了。一般來講,如果get不修改它的Content-Type,那么基本上都是簡單請求。post如果采用www-form的形式,也是一個簡單請求。
這篇文章主要是分享了Javascript中XMLHttpRequest對象的基本屬性和方法, 從實例化、初始化、發(fā)送和接受四個階段完成了Ajax網(wǎng)絡(luò)請求核心內(nèi)容封裝。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。