本篇內(nèi)容介紹了“JavaScript怎么實(shí)現(xiàn)前后端分離”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)為客戶提供專業(yè)的成都網(wǎng)站制作、做網(wǎng)站、程序、域名、空間一條龍服務(wù),提供基于WEB的系統(tǒng)開(kāi)發(fā). 服務(wù)項(xiàng)目涵蓋了網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站程序開(kāi)發(fā)、WEB系統(tǒng)開(kāi)發(fā)、微信二次開(kāi)發(fā)、手機(jī)網(wǎng)站制作等網(wǎng)站方面業(yè)務(wù)。
現(xiàn)在把自己當(dāng)成是前端,要開(kāi)發(fā)一個(gè)前后分離的簡(jiǎn)單頁(yè)面,用于展示學(xué)生信息列表
第一步
編寫一個(gè)用于展示表格的靜態(tài)頁(yè)面
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> <table id="tab" border="1"> <tr> <th>編號(hào)</th> <th>名字</th> <th>年齡</th> <th>性別</th> </tr> </table> <button onclick="req()">請(qǐng)求數(shù)據(jù)</button> <img id="img" /> </body> </html>
不啟動(dòng)tomcat直接在編輯器中打開(kāi)即可訪問(wèn),此時(shí)他就是一個(gè)靜態(tài)網(wǎng)頁(yè),而我們的編輯器就是一個(gè)HTTP服務(wù)器,可以響應(yīng)靜態(tài)網(wǎng)頁(yè)
第二步
引入jquery使得ajax編寫更方便
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
第三步
編寫ajax,向服務(wù)器發(fā)送請(qǐng)求
第四步
將數(shù)據(jù)展示到頁(yè)面上
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> </head> <body> <table id="tab" border="1"> <tr> <th>編號(hào)</th> <th>名字</th> <th>年齡</th> <th>性別</th> </tr> </table> <button onclick="req()">請(qǐng)求數(shù)據(jù)</button> <img id="img" /> </body> <script> function req(){ document.getElementById("img").src = "img/timg.gif"; $.ajax({ url:"http://localhost:8080/MyServer/getData", success:function(data){ console.log(data); document.body.insertAdjacentHTML("beforeend","<h2>%</h2>".replace("%",data)); document.getElementById("img").src = ""; }, error:function(err){ console.log(err); document.getElementById("img").src = ""; } }); } </script> </html>
現(xiàn)在身份切換回后端開(kāi)發(fā)用于獲取表格數(shù)據(jù)的接口
創(chuàng)建web項(xiàng)目
創(chuàng)建Servlet
引入fastjson
創(chuàng)建一個(gè)bean類
創(chuàng)建一堆bean放入列表中
將列表轉(zhuǎn)為json字符串 返回給前端
Servlet代碼
package com.kkb; import java.io.IOException; public class AServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { String s = "{\"name\":\"jack\"}"; response.getWriter().println(s); } }
啟動(dòng)服務(wù),測(cè)試訪問(wèn),會(huì)發(fā)現(xiàn)頁(yè)面上沒(méi)有顯示服務(wù)器返回的結(jié)果….
打開(kāi)瀏覽器檢查頁(yè)面會(huì)發(fā)現(xiàn)沒(méi)有輸出服務(wù)器返回的消息而是,出現(xiàn)了一個(gè)錯(cuò)誤信息,這就是前后端分離最常見(jiàn)的跨越問(wèn)題
跨越問(wèn)題之所以產(chǎn)生是因?yàn)闉g覽器都遵循了同源策略
同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能會(huì)受到影響。可以說(shuō)Web是構(gòu)建在同源策略基礎(chǔ)之上的,瀏覽器只是針對(duì)同源策略的一種實(shí)現(xiàn)。
同源策略是瀏覽器的行為,是為了保護(hù)本地?cái)?shù)據(jù)不被JavaScript代碼獲取回來(lái)的數(shù)據(jù)污染,瀏覽器會(huì)先發(fā)送OPTION請(qǐng)求進(jìn)行預(yù)檢查,判斷服務(wù)器是否允許跨域,如果允許才發(fā)送真正的請(qǐng)求,否則拋出異常。
同源策略是瀏覽器的核心安全機(jī)制,其不允許在頁(yè)面中解析執(zhí)行來(lái)自其他服務(wù)器數(shù)據(jù)
當(dāng)一個(gè)請(qǐng)求url的協(xié)議、域名、端口三者之間任意一個(gè)與當(dāng)前頁(yè)面url不同即為跨域
無(wú)法讀取非同源網(wǎng)頁(yè)的 Cookie、LocalStorage 和 IndexedDB
無(wú)法向非同源地址發(fā)送 AJAX 請(qǐng)求
瀏覽器在解析執(zhí)行一個(gè)網(wǎng)頁(yè)時(shí),如果頁(yè)面中的js代碼請(qǐng)求了另一個(gè)非同源的資源,則會(huì)產(chǎn)生跨越問(wèn)題
而瀏覽器直接跳轉(zhuǎn)另一個(gè)非同源的地址時(shí)不會(huì)有跨域問(wèn)題
既然禁止跨域問(wèn)題是瀏覽器的行為,那么只需要設(shè)置瀏覽器允許解析跨域請(qǐng)求的數(shù)據(jù)即可,但是這個(gè)設(shè)置必須放在服務(wù)器端,由服務(wù)器端來(lái)判斷對(duì)方是否可信任
在響應(yīng)頭中添加一個(gè)字段,告訴瀏覽器,某個(gè)服務(wù)器是可信的
package com.kkb; import java.io.IOException; public class AServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { response.setHeader("Access-Control-Allow-Origin","*"); String s = "{\"name\":\"jack\"}"; response.getWriter().println(s); } }
其值可以是某個(gè)或多個(gè)指定的域名,也可以是*表示信任所有地址
其他相關(guān)設(shè)置
//指定允許其他域名訪問(wèn) 'Access-Control-Allow-Origin:http://XXX.XXX.XXX'//一般用法(*,指定域,動(dòng)態(tài)設(shè)置),注意*不允許攜帶認(rèn)證頭和cookies //預(yù)檢查間隔時(shí)間 'Access-Control-Max-Age: 1800' //允許的請(qǐng)求類型 'Access-Control-Allow-Methods:GET,POST,PUT,POST' //列出必須攜帶的字段 'Access-Control-Allow-Headers:x-requested-with,content-type'
解決了跨越問(wèn)題后再來(lái)完善上面的案例
package com.kkb; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import java.io.IOException; import java.util.ArrayList; public class AServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { //允許來(lái)自任何主機(jī)的跨越訪問(wèn) response.setHeader("Access-Control-Allow-Origin","*"); //設(shè)置響應(yīng)類型為json數(shù)據(jù) response.setContentType("application/json;charset=utf-8"); //學(xué)生信息 ArrayList<Student> students = new ArrayList<>(); Student stu1 = new Student("s1","jack",20,"man"); Student stu2 = new Student("s2","tom",22,"girl"); Student stu3 = new Student("s3","jerry",10,"woman"); Student stu4 = new Student("s4","scot",24,"boy"); students.add(stu1); students.add(stu2); students.add(stu3); students.add(stu4); response.getWriter().println(JSON.toJSONString(JSON.toJSONString(students))); } }
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> </head> <body> <table id="tab" border="1"> <tr> <th>編號(hào)</th> <th>名字</th> <th>年齡</th> <th>性別</th> </tr> </table> <button onclick="req()">請(qǐng)求數(shù)據(jù)</button> <img id="img" /> </body> <script> function req(){ document.getElementById("img").src = "img/timg.gif"; $.ajax({ url:"http://localhost:8080/MyServer/getData", success:function(data){ data = JSON.parse(data) console.log(data) for (var i = 0; i < data.length; i++) { a = data[i]; var row = "<tr><td>id</td><td>name</td><td>age</td><td>gender</td></tr>" row = row.replace("id",a.id); row = row.replace("name",a.name); row = row.replace("age",a.age); row = row.replace("gender",a.gender); document.getElementById("tab").insertAdjacentHTML("beforeend",row); } document.getElementById("img").src = ""; }, error:function(err){ console.log(err); document.getElementById("img").src = ""; } }); } </script> </html>
一個(gè)簡(jiǎn)單的前后端分離項(xiàng)目就搞定了
默認(rèn)情況下cookie是不允許跨域傳輸?shù)?可以通過(guò)以下方式來(lái)解決
瀏覽器端設(shè)置允許cookie跨域
第二步
服務(wù)器端在響應(yīng)中添加字段,說(shuō)明允許cookie跨域
該值只能是true,為false無(wú)效,默認(rèn)為false
#'Access-Control-Allow-Credentials:true'
在傳統(tǒng)的項(xiàng)目中我們利用,session+cookie來(lái)保持用戶的登錄狀態(tài),但這在前后端分離項(xiàng)目中出現(xiàn)了問(wèn)題;
sessionid是使用cookie存儲(chǔ)在客戶端的,而cookie遵守同源策略,只在同源的請(qǐng)求中有效,這就導(dǎo)致了問(wèn)題出現(xiàn):
前后端分離后靜態(tài)資源完全可能(而且經(jīng)常....)部署到另一個(gè)域下,導(dǎo)致cookie失效,例如這樣:
在www.baidu.com中設(shè)置的cookie是不會(huì)自動(dòng)發(fā)送到cloud.baodu.comde
雖然我們可以在cookie中指定domain來(lái)解決,但是cookie必須針對(duì)性的設(shè)置作用域
這對(duì)于有多個(gè)不同域要共享cookie時(shí),可操作性差,難以維護(hù)
上述問(wèn)題出現(xiàn)在前后端分離的web項(xiàng)目中,對(duì)于前后端分離的原生CS結(jié)構(gòu)項(xiàng)目而言,很多客戶端默認(rèn)是不處理session和cookie的,需要進(jìn)行相應(yīng)的設(shè)置
在分布式或集群的項(xiàng)目中,共享session和cookie也是一大問(wèn)題,必須引入第三方來(lái)完成session的存儲(chǔ)和共享(也可通過(guò)中間層做cookie轉(zhuǎn)發(fā)如Nginx.Node.js),這也是傳統(tǒng)單體服務(wù)無(wú)法支持分布式和集群的問(wèn)題所在
正因?yàn)橛羞@些問(wèn)題,導(dǎo)致session+cookie的方式在某些項(xiàng)目中使用起來(lái)變得很麻煩,這時(shí)候就需要一種新的狀態(tài)維持的方式;
JWT全稱(json WEB token),是基于json數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)驗(yàn)證方式,其本質(zhì)是對(duì)json數(shù)據(jù)進(jìn)行加密后產(chǎn)生的字符串
JWT的亮點(diǎn):
安全
穩(wěn)定
易用
支持 JSON
回顧,之所以使用session和cookie是因?yàn)镠TTP的無(wú)狀態(tài)性質(zhì),導(dǎo)致服務(wù)器無(wú)法識(shí)別多次請(qǐng)求是否來(lái)自同一個(gè)用戶
JWT可以對(duì)用戶信息進(jìn)行加密生成一個(gè)字符串,下發(fā)到客戶端,客戶端在后續(xù)請(qǐng)求中攜帶該字符串,服務(wù)器解析后取出用戶信息,從而完成用戶身份的識(shí)別,如下圖:
session共享和JWT在集群中的不同
JWT是一個(gè)很長(zhǎng)的字符串,分為成三個(gè)部分,中間用點(diǎn).隔開(kāi)注意; JWT 內(nèi)部是沒(méi)有換行的,這里只是為了便于展示,將它寫成了幾行。
Header(頭部)
Payload(負(fù)載)
Signature(簽名)
Header 部分是一個(gè) JSON 對(duì)象,描述 JWT 的元數(shù)據(jù),例如簽名算法等,像下面這樣:
{ "alg": "HS256", "typ": "JWT" }
alg屬性表示簽名的算法,默認(rèn)是 HMAC SHA256;
typ屬性表示這個(gè)令牌(token)的類型統(tǒng)一寫為JWT
最后使用base64URL算法轉(zhuǎn)換為字符串;
Payload 部分也是一個(gè) JSON 對(duì)象,用來(lái)存放真正需要傳遞的數(shù)據(jù),JWT 規(guī)定了7個(gè)保留字段,如下:
iss (issuer):簽發(fā)人 exp (expiration time):過(guò)期時(shí)間 sub (subject):主題 aud (audience):受眾 nbf (Not Before):生效時(shí)間 iat (Issued At):簽發(fā)時(shí)間 jti (JWT ID):編號(hào)
服務(wù)器需要在Payload中添加用于識(shí)別用戶身份的數(shù)據(jù),也是鍵值對(duì)形式,注意不可使用保留字段,像下面這樣
{ "sub": "test JWT", "name": "jerry", "isadmin": true }
Payload同樣使用base64URL算法轉(zhuǎn)換為字符串;
強(qiáng)調(diào):Payload數(shù)據(jù)默是不加密的,攻擊者可以通過(guò)相同的方式解析獲取
若要將用戶的關(guān)鍵數(shù)據(jù)放入其中則必須對(duì)其進(jìn)行額外的加密
部分是對(duì)前兩部分的簽名,防止數(shù)據(jù)篡改。
簽名時(shí)需要指定一個(gè)密鑰(secret)。密鑰只有服務(wù)器才知道,不能泄露給用戶。然后使用 Header 里面指定的簽名算法(默認(rèn)是 HMAC SHA256),按照下面的方式產(chǎn)生簽名。
signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
最后把 Header、Payload、Signature 三個(gè)部分拼成一個(gè)字符串,每個(gè)部分之間用"點(diǎn)".分隔返回給用戶;
滿足REST Full的無(wú)狀態(tài)要求(為了提高系統(tǒng)的擴(kuò)展性,REST要求所有信息由請(qǐng)求端來(lái)提供,如此才使得JWT成為了分布式,集群構(gòu)架的首選方式)
在分布式,集群系統(tǒng)中使身份驗(yàn)證變得非常簡(jiǎn)單
可用于其他數(shù)據(jù)交換
合理的使用可減少數(shù)據(jù)庫(kù)查詢次數(shù)
對(duì)于同樣的數(shù)據(jù)JWT整體大小超過(guò)同樣數(shù)據(jù)的cookie,這會(huì)增加網(wǎng)絡(luò)負(fù)擔(dān)
服務(wù)器每次解析JWT都需要再次執(zhí)行對(duì)應(yīng)的算法,這將增加系統(tǒng)負(fù)擔(dān)
在傳統(tǒng)單體服務(wù),和WEBApp形式的前后端分離項(xiàng)目中使用JWT反而不如Session+cookie
JWT的payload部分是不加密的,如果要放入關(guān)鍵數(shù)據(jù)則必須對(duì)其進(jìn)行加密,或是將最后的JWT整體加密
JWT本身用于認(rèn)證,一旦泄露,則任何人都可以使用該令牌,獲得其包含的所有權(quán)限,為了提高安全性.JWT的有效期不應(yīng)太長(zhǎng),對(duì)于一些非常權(quán)限,建議在請(qǐng)求時(shí)再次驗(yàn)證
懂得原理了后我們完全可以自己來(lái)實(shí)現(xiàn),但是沒(méi)有必要,下面是目前用的較多的一個(gè)開(kāi)源庫(kù)
使用JWT的步驟總體分為三步
生成JWT
驗(yàn)證JWT
提取數(shù)據(jù)
package com.kkb; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JWTTool { //自定義密鑰 public static final String secretKey = "askjdksadjkasdakjshdjkasdkAakjshdjasdjs"; /*** * * @param secretKey 密鑰 * @param data 用戶數(shù)據(jù) * @param expireTime 有效期 * @return */ public String getJWTWithHMAC256(String secretKey, HashMap<String, String> data,long expireTime){ //指定JWT所使用的簽名算法 Algorithm algorithm = Algorithm.HMAC256(secretKey); JWTCreator.Builder token = JWT.create()//創(chuàng)建token .withIssuer("com.kkb")//指定簽發(fā)人 .withExpiresAt(new Date(expireTime));//有效時(shí)間 //添加自定義數(shù)據(jù) if (data != null){ for (Map.Entry<String, String> a :data.entrySet()) { token.withClaim(a.getKey(),a.getValue()); } } return token.sign(algorithm); } public boolean verifyTokenWithHMAC256(String token,String secretKey){ try{ Algorithm algorithm = Algorithm.HMAC256(secretKey); JWTVerifier verifier = JWT.require(algorithm) .withIssuer("com.kkb") .build(); verifier.verify(token); return true; }catch (JWTVerificationException e){ e.printStackTrace(); return false; } } public static void main(String[] args) { JWTTool jwtTool = new JWTTool(); //要添加到token中的數(shù)據(jù) HashMap<String,String> data = new HashMap<>(); data.put("user","jerry"); data.put("isAdmin","true"); //有效期 long expiresTime = new Date().getTime() + 1000 * 60 * 60; //生成token String token = jwtTool.getJWTWithHMAC256(secretKey,data,expiresTime); System.out.println(token); //驗(yàn)證token //If the token has an invalid signature or the Claim requirement is not met if (jwtTool.verifyTokenWithHMAC256(token,secretKey)){ System.out.println("token 有效"); try{ //提取數(shù)據(jù) DecodedJWT decode = JWT.decode(token); System.out.println("數(shù)據(jù):"+decode.getClaim("user").asString()); System.out.println("數(shù)據(jù):"+decode.getClaim("isAdmin").asString()); System.out.println("簽發(fā):"+decode.getIssuer()); System.out.println("有效期"+decode.getExpiresAt()); }catch (JWTDecodeException e){ //If the token has an invalid syntax or the header or payload are not JSONs, System.out.println("解析token失敗"); } }else { System.out.println("token 無(wú)效"); } } }
“JavaScript怎么實(shí)現(xiàn)前后端分離”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
網(wǎng)頁(yè)題目:JavaScript怎么實(shí)現(xiàn)前后端分離
轉(zhuǎn)載來(lái)于:http://chinadenli.net/article14/gogige.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、App設(shè)計(jì)、全網(wǎng)營(yíng)銷推廣、ChatGPT、自適應(yīng)網(wǎng)站、網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)