初識單點登錄及JWT實現

單點登錄

多系統,單一位置登錄,實現多系統同時登錄的一種技術

(三方登錄:某系統使用其他系統的用戶,實現本系統登錄的方式。如微信登錄、支付寶登錄)

單點登錄一般是用于互相授信的系統,實現單一位置登錄,全系統有效

?

一、Session跨域

  所謂 Session 跨域就是摒棄了系統提供的 Session ,而使用自定義的類似 Session 的機制來保存客戶端數據的一種解決方案。

  如:通過設置 cookie 的 domain 來實現 cookie 的跨域傳遞。在 cookie 中傳遞一個自定義的 session_id。這個 session_id 是客戶端的唯一標記,將這個標記作為key,將客戶需要保存的數據作為value,在服務端進行保存(數據庫保存或nosql保存)。這種機制就是 Session 的跨域解決。

  什么為跨域:客戶端請求的時候,請求的服務器,不是同一個IP、端口、域名、主機名的時候,都稱為跨域。

  什么是域:在應用模型中,一個完整的、有獨立訪問路徑的功能集合成為一個域。

       如:百度稱為一個應用或系統,其下有若干個域,如搜索引擎(www.baidu.com),百度貼吧(tie.baidu.com),百度知道(zhidao.baidu.com)等。

       有時也稱為多級域名。域的劃分:以IP、端口、域名、主機名為標準,實現劃分。

?

二、Spring Session 共享

  spring-session 技術是 spring 提供的用于處理集群會話共享的解決方案。spring-session技術是將用戶 session 數據保存到第三方容器中,如數據庫。

  Spring-session 技術是解決同域名下的多服務器集群 session 共享問題的,不能解決跨域 Session 共享問題

?

三、Nginx Session 共享

  nginx中的 ip_hash 技術能夠將某個 ip 的請求定向到同一臺后端,這樣一來這個ip下的某個客戶端和某個后端就能建立起穩固的session,ip_hash是在upstream配置中定義的

?

四、Token身份認證

  使用基于 Token 的身份驗證方法,在服務端不需要存儲用戶的登錄記錄,大概流程如下:

  1)客戶端使用用戶名、密碼請求登錄

  2)服務端收到請求、去驗證用戶名與密碼

  3)驗證成功后,服務端會簽發y一個 token ,再把這個 token 發送給客戶端

  4)客戶端收到 token 以后可以把它存儲起來,比如放在 cookie 里或者 Local Storage里

  5)客戶端每次向服務器請求資源的時候需要帶著服務器簽發的 token

  6)服務端收到請求,然后去驗證客戶端請求里面帶著的 token,如果驗證成功,就向客戶端返回請求的數據

  使用token的優勢:

  無狀態、可擴展:

    在客戶端存儲的 token 是無狀態的,并且能夠被擴展,基于這種無狀態和不存儲session信息,負載均衡器能夠將用戶信息從一個服務傳到其他服務器上。

  安全性:

    請求中發送token而不再發送cookie能夠防止CSRF(跨域請求偽造)。即使在客戶端使用cookie存儲token。cookie也僅僅是一個存儲機制而不是用于認證。

  不將信息存儲在session中,讓我們少了對session的操作。

?

五、JSON Web Token(JWT)機制

  JWT是一種緊湊且自包含的,用于在多方傳遞 json 對象的技術。傳遞的數據可以使用數字簽名增加其安全性。可以使用HMAC加密算法或RSA公鑰/私鑰加密方式。

  緊湊:數據小,可以通過URL、POST參數,請求頭發送,且數據小代表傳輸速度快。

  自包含:使用 payload 數據塊j記錄用戶必要且不隱私的數據,可以有效的減少數據庫訪問次數,提高代碼性能

  JWT一般用于處理用戶身份校驗或數據信息交換

  JWT的數據結構

    JWT的數據結構:A.B.C  以.(點)來劃分

    A-header  頭信息

    B-payload  (有效荷載?)

    C-Signature  簽名

  header:

  數據結構:{"alg":"加密算法名稱","typ":"JWT"}

  alg可以有 HMAC 或 SHA256 或 RSA 等

  payload:主要分為三部分:已注冊信息、公開數據、私有數據

  singature:

    簽名信息,這是一個由開發者提供的信息。是服務器驗證的傳遞的數據是否有效安全的標準。  

  執行流程

  

?

?簡單實現

1)造數據 JWTUsers模擬數據庫用戶名密碼

package cn.zytao.taosir;import java.util.HashMap;
import java.util.Map;/*** 用于模擬用戶數據的,開發中應訪問數據庫驗證用戶* @author TAOSIR**/
public class JWTUsers {private static final Map<String,String> USERS =new HashMap<>(11);static {for(int i=0;i<10;i++) {USERS.put("admin"+i, "pwd"+i);}}//驗證是否可以登錄public static boolean isLogin(String username,String pwd) {if(null == username || username.trim().length()==0)return false;String obj=USERS.get(username);if(null ==obj||!obj.equals(pwd))return false;return true;}
}

2)JWTSubject

package cn.zytao.taosir;
/*** 作為Subject數據使用,也就是payload中保存的public claims* 其中不應包含任何敏感數據* 開發中建議使用實體類型,或BO,DTO數據對象* @author TAOSIR**/
public class JWTSubject {private String username;public JWTSubject() {super();}public JWTSubject(String username) {super();this.username = username;}public String getUsername() {return username;}public void setUsername(String username) {this.username=username;}
}

3)JWT結果對象

package cn.zytao.taosir;
/*** 作為Subject數據使用,也就是payload中保存的public claims* 其中不應包含任何敏感數據* 開發中建議使用實體類型,或BO,DTO數據對象* @author TAOSIR**/
public class JWTSubject {private String username;public JWTSubject() {super();}public JWTSubject(String username) {super();this.username = username;}public String getUsername() {return username;}public void setUsername(String username) {this.username=username;}
}

4)響應對象

package cn.zytao.taosir;public class JWTResponseData {private Integer code;//返回碼private Object data;//業務數據private String msg;//返回描述private String token;//身份標識public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public String getToken() {return token;}public void setToken(String token) {this.token = token;}
}

5)JWT控制類

package cn.zytao.taosir;
/*** JWT工具類* @author TAOSIR**/import java.util.Date;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;import com.fasterxml.jackson.databind.ObjectMapper;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;public class JWTUtils {private static final String JWT_SECERT = "test_jwt_secert";//服務器的key,密鑰private static final ObjectMapper MAPPER = new ObjectMapper();//用戶java對象和json字符串轉換public static final int JWT_ERRCODE_EXPIRE = 1005;//Token過期public static final int JWT_ERRCODE_FAIL = 1006;//驗證不通過public static SecretKey generalKey() {try {byte[] encodedKey=JWT_SECERT.getBytes("UTF-8");SecretKey key=new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;} catch (Exception e) {e.printStackTrace();return null;}}/*** 簽發JWT,即創建token的方法* @param id    jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊* @param iss    jwt簽發者* @param subject    jwt所面向的用戶,payload中記錄的public,claims,當前環境中就是用戶的登錄名* @param ttlMills    有效期,單位毫秒* @return*/public static String createJWT(String id,String iss,String subject,long ttlMillis) {//加密算法SignatureAlgorithm signatureAlgorithm=SignatureAlgorithm.HS256;long nowMillis = System.currentTimeMillis();Date now=new Date(nowMillis);SecretKey secretKey=generalKey();//創建JWT的構造器用于生成tokenJwtBuilder builder=Jwts.builder().setId(id).setIssuer(iss).setSubject(subject).setIssuedAt(now).signWith(signatureAlgorithm, secretKey);if(ttlMillis >= 0) {long expMillis =nowMillis+ttlMillis;Date exDate = new Date(expMillis);builder.setExpiration(exDate);}return builder.compact();}/*** 驗證JWT* @param jwtStr* @return*/public static JWTResult validateJWT(String jwtStr) {JWTResult checkResult=new JWTResult();Claims claims=null;try {claims=parseJWT(jwtStr);checkResult.setSuccess(true);checkResult.setClaims(claims);} catch (ExpiredJwtException e) {checkResult.setSuccess(false);checkResult.setErrCode(JWT_ERRCODE_EXPIRE);} catch (SignatureException e) {checkResult.setSuccess(true);checkResult.setErrCode(JWT_ERRCODE_FAIL);}return checkResult;}/*** 解析JWT字符串* @param jwt    就是token* @return*/public static Claims parseJWT(String jwt) {SecretKey secretKey=generalKey();//getBody獲取值就是token中記錄的payload數據,就是其中保存的claimsreturn Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}/*** 生成subject信息* @param subObj* @return*/public static String generalSubject(Object subObj) {try {return MAPPER.writeValueAsString(subObj);} catch (Exception e) {e.printStackTrace();return null;}}
}

6)寫個簡單的controller實踐

package cn.zytao.taosir.controller;import java.util.UUID;import javax.servlet.http.HttpServletRequest;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import cn.zytao.taosir.JWTResponseData;
import cn.zytao.taosir.JWTResult;
import cn.zytao.taosir.JWTSubject;
import cn.zytao.taosir.JWTUsers;
import cn.zytao.taosir.JWTUtils;@RestController
public class JWTController {@RequestMapping("testAll")public Object testAll(HttpServletRequest request) {String token=request.getHeader("Authorization");JWTResult result=JWTUtils.validateJWT(token);JWTResponseData responseData=new JWTResponseData();if(result.isSuccess()) {responseData.setCode(200);responseData.setData(result.getClaims().getSubject());String newToken=JWTUtils.createJWT(result.getClaims().getId(), result.getClaims().getIssuer(), result.getClaims().getSubject(), 1*60*1000);responseData.setToken(newToken);return responseData;}else {responseData.setCode(500);responseData.setMsg("用戶未登錄");return responseData;}}@RequestMapping("login")public Object login(String username,String password) {JWTResponseData responseData=null;//認證用戶信息if(JWTUsers.isLogin(username, password)) {JWTSubject subject=new JWTSubject(username);String jwtToken = JWTUtils.createJWT(UUID.randomUUID().toString(),"sxt-test-jwt", JWTUtils.generalSubject(subject), 1*60*1000);responseData=new JWTResponseData();responseData.setCode(200);responseData.setData(null);responseData.setMsg("登錄成功");responseData.setToken(jwtToken);}else {responseData=new JWTResponseData();responseData.setCode(500);responseData.setData(null);responseData.setMsg("登錄失敗");responseData.setToken(null);}return responseData;}
}

7)Postman查看情況

?

轉載于:https://www.cnblogs.com/it-taosir/p/10021811.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/248805.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/248805.shtml
英文地址,請注明出處:http://en.pswp.cn/news/248805.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Vue組件通信原理剖析(三)provide/inject原理分析

首先我們先從一個面試題入手。 面試官問&#xff1a; “Vue中組件通信的常用方式有哪些&#xff1f;” 我答&#xff1a; 1. props 2. 自定義事件 3. eventbus 4. vuex 5. 還有常見的邊界情況$parent、$children、$root、$refs、provide/inject 6. 此外還有一些非props特性$att…

iMX6開發板-uboot-網絡設置和測試

本文章基于迅為IMX6開發板 將iMX6開發板通過網線連接到路由器&#xff0c;同時連接好調試串口&#xff0c;上電立即按 enter&#xff0c;即可進入 uboot。然后輸入命令 pri&#xff0c;查看開發板當前的配置&#xff0c;如下圖所示可以看到 ip 地址、子網掩碼 等信息。 本文檔測…

Django ajax 檢測用戶名是否已被注冊

添加一個 register.html 頁面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <form><p>用戶名<input id"username" type&…

pyqt5控件

背景色設置 self.tab.setStyleSheet("background: rgb(238, 233, 233)") self.but_0.setStyleSheet("background: rgb(0, 255, 255)")樣式&#xff1a; self.but_0.setStyle(QStyleFactory.create("Windows"))字體&#xff1a; self.lineEdit.se…

詳解JDBC連接數據庫

一、概念 1. 為了能讓程序操作數據庫&#xff0c;對數據庫中的表進行操作&#xff0c;每一種數據庫都會提供一套連接和操作該數據庫的驅動&#xff0c;而且每種數據庫的驅動都各不相同&#xff0c;例如mysql數據庫使用mysql驅動&#xff0c;oracle數據庫使用oracle驅動&#xf…

ASP.NET MVC 自定義模型綁定1 - 自動把以英文逗號分隔的 ID 字符串綁定成 Listint...

直接貼代碼了&#xff1a; CommaSeparatedModelBinder.cs using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Web.Mvc;namespace MvcSample.Extensions {public class CommaSeparatedMode…

ZOJ4024 Peak

題意 給出一個數組 判斷這個數組是否形成了一個“山峰” 即中間有個數最大 從第一個數到這個數遞增 從這個數到最后一個數遞減 模擬 從兩端分別以遞增和遞減判斷 看第一個不滿足遞增或遞減的數是否相等并且沒越界就可以了 AC代碼&#xff1a; 1 #include<bits/stdc.h>2 u…

基本數據類型與String之間的轉換

字符串轉基本數據類型 調用基本數據類型對應的包裝類中的方法parseXXX(String)或valueOf(String)即可返回相應基本類型。 基本數據類型轉字符串 一種方法是將基本數據類型與空字符串&#xff08;""&#xff09;連接&#xff08;&#xff09;即可獲得其所對應的字符串…

springmvc跨域問題

1、跨域問題&#xff1a; 按照網上所有的方法試了一遍&#xff0c;都沒跨過去&#xff0c;正在無助之際&#xff0c;使用filter按照下面的方法解決的時候出現了轉機&#xff1a; 添加filter&#xff1a; package com.thc.bpm.filter;import javax.servlet.*; import javax.serv…

柳傳志給年輕人的建議:比起過日子,更要奔日子

改革開放的 40 年&#xff0c;是柳傳志實現人生價值的 40 年。 十一屆三中全會后&#xff0c;伴隨“科學的春天”&#xff0c;迎著改革開放的大潮&#xff0c;柳傳志“下海”了。但他并沒想到&#xff0c;自己選擇的電腦行業&#xff0c;讓他和聯想集團站在了潮頭。 從 1984 年…

成功秀了一波scala spark ML邏輯斯蒂回歸

1、直接上官方代碼&#xff0c;調整過的&#xff0c;方可使用 package com.test import org.apache.spark.{SparkConf, SparkContext} import org.apache.spark.mllib.classification.{LogisticRegressionModel, LogisticRegressionWithLBFGS} import org.apache.spark.mllib.e…

記錄一次查詢log的經歷

一大早發現生產數據庫的基礎資料被刪除。 由于每天都做了差異備份&#xff0c;而且是基礎資料&#xff0c;這樣數據就不會擔心找不回來。 首先通過每天的差異本分文件進行查看數據丟失的大概時間&#xff0c;查到數據丟失是在17晚上備份過后18丟失的。 然后找18號的數據庫執行記…

移動端輪播圖

1. 頁面布局 1.1 頁面框架 <body><div class"box"><div class"tupian"><img src"4.webp" alt""><img src"1.webp" alt""><img src"2.webp" alt""><…

Boost 序列化

原文鏈接&#xff1a; https://blog.csdn.net/qq2399431200/article/details/45621921 1. 編譯器 gcc, boost 1.55 2.1第一個簡單的例子 —— Hello World &#xff0c;將字符串內容歸檔到文本文件中 #include <iostream>#include <fstream>#include <string>…

docker CE 的安裝

一、Docker CE的安裝1.先決條件運行環境&#xff1a;Ubuntu 64位或者其他支持Docker的64位系統運行配置&#xff0c;linux內核版本必須大于 3.10&#xff0c;否則會因為缺少容器運行所需的功能而出錯。 2.在ubuntu下安裝Docker CEUbuntu版本? Cosmic 18.10 ? Bionic 18.04 (…

nodeJS中的異步編程

nodejs 不是單線程 在博客項目中關于異步問題&#xff1a; 1.當用戶添加一條博客時 需要通過post方式向服務器發送數據 后臺獲取用戶以post方式拿到傳送過來的數據 然后存入數據庫&#xff1a; 上面的代碼&#xff1a;創建一個空字符串 當用戶向服務器發送請求時出發data事件將…

day01筆記

linux基本命令的學習&#xff1a; 1.查看主機名hostname 2.修改主機名hostnamectl set-hostname s16ds 3.linux命令提示符 [roots16ds ~]# # 超級用戶的身份提示符 $ 普通用戶的身份提示符4.修改命令提示符 PS1變量控制 [roots16ds ~]# echo $PS1 [\u\h \W]\$PS1[\u\h \w \t]…

angular 路由

1. vscode編輯器快速新建主路由&#xff1a; ng-router注意修改為 根路由為&#xff1a;‘forRoot()’app-route.module.ts;{ path:,redirectTo:/login,pathMatch:full } 當路由為空的時候&#xff0c;會重定向到/login路由&#xff0c;必須加上pathMatch:full 1 import { Rou…

nodeJs 操作數據庫

首先在node中下載mysql包 npm install mysql 連接數據庫 var mysql require(mysql); var con mysql.createConnection({host : localhost,user : root,password : root,database : blog });開啟鏈接 con.connect();執行增刪改查 不同功能創建不同的sql語句即可…

shell字體顏色應用

輸出特效格式控制&#xff1a; \033[0m 關閉所有屬性 \033[1m 設置高亮度 \03[4m 下劃線 \033[5m 閃爍 \033[7m 反顯 \033[8m 消隱 \033[30m -- \033[37m 設置前景色 \033[40m -- \033[47m 設置背景色 光標位置等的格式控制&#xff1a; …