您好,登錄后才能下訂單哦!
本篇內容介紹了“JavaScript怎么實現前后端分離”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
現在把自己當成是前端,要開發一個前后分離的簡單頁面,用于展示學生信息列表
第一步
編寫一個用于展示表格的靜態頁面
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> <table id="tab" border="1"> <tr> <th>編號</th> <th>名字</th> <th>年齡</th> <th>性別</th> </tr> </table> <button onclick="req()">請求數據</button> <img id="img" /> </body> </html>
不啟動tomcat直接在編輯器中打開即可訪問,此時他就是一個靜態網頁,而我們的編輯器就是一個HTTP服務器,可以響應靜態網頁
第二步
引入jquery使得ajax編寫更方便
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
第三步
編寫ajax,向服務器發送請求
第四步
將數據展示到頁面上
<!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>編號</th> <th>名字</th> <th>年齡</th> <th>性別</th> </tr> </table> <button onclick="req()">請求數據</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>
現在身份切換回后端開發用于獲取表格數據的接口
創建web項目
創建Servlet
引入fastjson
創建一個bean類
創建一堆bean放入列表中
將列表轉為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); } }
啟動服務,測試訪問,會發現頁面上沒有顯示服務器返回的結果….
打開瀏覽器檢查頁面會發現沒有輸出服務器返回的消息而是,出現了一個錯誤信息,這就是前后端分離最常見的跨越問題
跨越問題之所以產生是因為瀏覽器都遵循了同源策略
同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能會受到影響。可以說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。
同源策略是瀏覽器的行為,是為了保護本地數據不被JavaScript代碼獲取回來的數據污染,瀏覽器會先發送OPTION請求進行預檢查,判斷服務器是否允許跨域,如果允許才發送真正的請求,否則拋出異常。
同源策略是瀏覽器的核心安全機制,其不允許在頁面中解析執行來自其他服務器數據
當一個請求url的協議、域名、端口三者之間任意一個與當前頁面url不同即為跨域
無法讀取非同源網頁的 Cookie、LocalStorage 和 IndexedDB
無法向非同源地址發送 AJAX 請求
瀏覽器在解析執行一個網頁時,如果頁面中的js代碼請求了另一個非同源的資源,則會產生跨越問題
而瀏覽器直接跳轉另一個非同源的地址時不會有跨域問題
既然禁止跨域問題是瀏覽器的行為,那么只需要設置瀏覽器允許解析跨域請求的數據即可,但是這個設置必須放在服務器端,由服務器端來判斷對方是否可信任
在響應頭中添加一個字段,告訴瀏覽器,某個服務器是可信的
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); } }
其值可以是某個或多個指定的域名,也可以是*表示信任所有地址
其他相關設置
//指定允許其他域名訪問 'Access-Control-Allow-Origin:http://XXX.XXX.XXX'//一般用法(*,指定域,動態設置),注意*不允許攜帶認證頭和cookies //預檢查間隔時間 'Access-Control-Max-Age: 1800' //允許的請求類型 'Access-Control-Allow-Methods:GET,POST,PUT,POST' //列出必須攜帶的字段 'Access-Control-Allow-Headers:x-requested-with,content-type'
解決了跨越問題后再來完善上面的案例
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 { //允許來自任何主機的跨越訪問 response.setHeader("Access-Control-Allow-Origin","*"); //設置響應類型為json數據 response.setContentType("application/json;charset=utf-8"); //學生信息 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>編號</th> <th>名字</th> <th>年齡</th> <th>性別</th> </tr> </table> <button onclick="req()">請求數據</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>
一個簡單的前后端分離項目就搞定了
默認情況下cookie是不允許跨域傳輸的.可以通過以下方式來解決
瀏覽器端設置允許cookie跨域
第二步
服務器端在響應中添加字段,說明允許cookie跨域
該值只能是true,為false無效,默認為false
#'Access-Control-Allow-Credentials:true'
在傳統的項目中我們利用,session+cookie來保持用戶的登錄狀態,但這在前后端分離項目中出現了問題;
sessionid是使用cookie存儲在客戶端的,而cookie遵守同源策略,只在同源的請求中有效,這就導致了問題出現:
前后端分離后靜態資源完全可能(而且經常....)部署到另一個域下,導致cookie失效,例如這樣:
在www.baidu.com中設置的cookie是不會自動發送到cloud.baodu.comde
雖然我們可以在cookie中指定domain來解決,但是cookie必須針對性的設置作用域
這對于有多個不同域要共享cookie時,可操作性差,難以維護
上述問題出現在前后端分離的web項目中,對于前后端分離的原生CS結構項目而言,很多客戶端默認是不處理session和cookie的,需要進行相應的設置
在分布式或集群的項目中,共享session和cookie也是一大問題,必須引入第三方來完成session的存儲和共享(也可通過中間層做cookie轉發如Nginx.Node.js),這也是傳統單體服務無法支持分布式和集群的問題所在
正因為有這些問題,導致session+cookie的方式在某些項目中使用起來變得很麻煩,這時候就需要一種新的狀態維持的方式;
JWT全稱(json WEB token),是基于json數據結構的數據驗證方式,其本質是對json數據進行加密后產生的字符串
JWT的亮點:
安全
穩定
易用
支持 JSON
回顧,之所以使用session和cookie是因為HTTP的無狀態性質,導致服務器無法識別多次請求是否來自同一個用戶
JWT可以對用戶信息進行加密生成一個字符串,下發到客戶端,客戶端在后續請求中攜帶該字符串,服務器解析后取出用戶信息,從而完成用戶身份的識別,如下圖:
session共享和JWT在集群中的不同
JWT是一個很長的字符串,分為成三個部分,中間用點.隔開注意; JWT 內部是沒有換行的,這里只是為了便于展示,將它寫成了幾行。
Header(頭部)
Payload(負載)
Signature(簽名)
Header 部分是一個 JSON 對象,描述 JWT 的元數據,例如簽名算法等,像下面這樣:
{ "alg": "HS256", "typ": "JWT" }
alg屬性表示簽名的算法,默認是 HMAC SHA256;
typ屬性表示這個令牌(token)的類型統一寫為JWT
最后使用base64URL算法轉換為字符串;
Payload 部分也是一個 JSON 對象,用來存放真正需要傳遞的數據,JWT 規定了7個保留字段,如下:
iss (issuer):簽發人 exp (expiration time):過期時間 sub (subject):主題 aud (audience):受眾 nbf (Not Before):生效時間 iat (Issued At):簽發時間 jti (JWT ID):編號
服務器需要在Payload中添加用于識別用戶身份的數據,也是鍵值對形式,注意不可使用保留字段,像下面這樣
{ "sub": "test JWT", "name": "jerry", "isadmin": true }
Payload同樣使用base64URL算法轉換為字符串;
強調:Payload數據默是不加密的,攻擊者可以通過相同的方式解析獲取
若要將用戶的關鍵數據放入其中則必須對其進行額外的加密
部分是對前兩部分的簽名,防止數據篡改。
簽名時需要指定一個密鑰(secret)。密鑰只有服務器才知道,不能泄露給用戶。然后使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的方式產生簽名。
signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
最后把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點".分隔返回給用戶;
滿足REST Full的無狀態要求(為了提高系統的擴展性,REST要求所有信息由請求端來提供,如此才使得JWT成為了分布式,集群構架的首選方式)
在分布式,集群系統中使身份驗證變得非常簡單
可用于其他數據交換
合理的使用可減少數據庫查詢次數
對于同樣的數據JWT整體大小超過同樣數據的cookie,這會增加網絡負擔
服務器每次解析JWT都需要再次執行對應的算法,這將增加系統負擔
在傳統單體服務,和WEBApp形式的前后端分離項目中使用JWT反而不如Session+cookie
JWT的payload部分是不加密的,如果要放入關鍵數據則必須對其進行加密,或是將最后的JWT整體加密
JWT本身用于認證,一旦泄露,則任何人都可以使用該令牌,獲得其包含的所有權限,為了提高安全性.JWT的有效期不應太長,對于一些非常權限,建議在請求時再次驗證
懂得原理了后我們完全可以自己來實現,但是沒有必要,下面是目前用的較多的一個開源庫
使用JWT的步驟總體分為三步
生成JWT
驗證JWT
提取數據
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 用戶數據 * @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()//創建token .withIssuer("com.kkb")//指定簽發人 .withExpiresAt(new Date(expireTime));//有效時間 //添加自定義數據 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中的數據 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); //驗證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{ //提取數據 DecodedJWT decode = JWT.decode(token); System.out.println("數據:"+decode.getClaim("user").asString()); System.out.println("數據:"+decode.getClaim("isAdmin").asString()); System.out.println("簽發:"+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 無效"); } } }
“JavaScript怎么實現前后端分離”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。