掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
單點登錄(Single Sign-On,簡稱SSO)是一種身份驗證和授權(quán)機制,允許用戶在訪問多個相關(guān)獨立的系統(tǒng)或應(yīng)用程序時只需一次登錄, 而不需要為每個系統(tǒng)都提供單獨的身份驗證憑證。SSO的目的是簡化用戶體驗、提高安全性, 并減少用戶因頻繁登錄而可能面臨密碼疲勞問題。

我們注重客戶提出的每個要求,我們充分考慮每一個細(xì)節(jié),我們積極的做好做網(wǎng)站、成都網(wǎng)站建設(shè)服務(wù),我們努力開拓更好的視野,通過不懈的努力,成都創(chuàng)新互聯(lián)公司贏得了業(yè)內(nèi)的良好聲譽,這一切,也不斷的激勵著我們更好的服務(wù)客戶。 主要業(yè)務(wù):網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)站設(shè)計,小程序開發(fā),網(wǎng)站開發(fā),技術(shù)開發(fā)實力,DIV+CSS,PHP及ASP,ASP.Net,SQL數(shù)據(jù)庫的技術(shù)開發(fā)工程師。
SSO的工作原理涉及以下關(guān)鍵概念:
原理圖如下:
圖片
JWT 是一種基于 JSON 格式的輕量級令牌,其主要原理是通過在服務(wù)端生成一個包含用戶信息的 JSON 對象,然后使用密鑰對該對象進行簽名,生成一個令牌。這個令牌可以被發(fā)送到客戶端,客戶端可以在之后的請求中攜帶該令牌,服務(wù)端使用密鑰驗證令牌的簽名,并解析其中的信息, 從而完成身份驗證。
JWT 由三部分組成:Header(頭部)、Payload(負(fù)載)和 Signature(簽名)。
JWT原理圖如下:
圖片
為了模擬單點登錄(SSO), 將創(chuàng)建兩個簡單的Golang服務(wù): 一個用于認(rèn)證用戶(認(rèn)證中心), 另一個用于資源提供。用戶如果要獲取資源,必須先登錄認(rèn)證中心獲取令牌, 然后再通過令牌訪問資源服務(wù)器, 下面是認(rèn)證中心的服務(wù)端實現(xiàn)代碼:
package main
import (
"fmt"
"net/http"
"time"
"github.com/dgrijalva/jwt-go"
)
var secretKey = []byte("btk.gqv7jtu7VZD1dar")
func main() {
http.HandleFunc("/login", handleLogin)
http.ListenAndServe(":8080", nil)
}
func handleLogin(w http.ResponseWriter, r *http.Request) {
// 在實際應(yīng)用中,這里應(yīng)該有用戶認(rèn)證的邏輯,為了簡化,這里直接使用一個固定用戶
userID := "9527"
tokenString, err := createToken(userID)
if err != nil {
http.Error(w, "創(chuàng)建令牌失敗", http.StatusInternalServerError)
return
}
// 將 JWT 令牌附加到響應(yīng)中
w.Header().Set("Authorization", "Bearer "+tokenString)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "登錄成功. Token: %s", tokenString)
}
func createToken(userID string) (string, error) {
// 創(chuàng)建負(fù)載
payload := jwt.MapClaims{
"user": userID,
"exptime": time.Now().Add(time.Minute * 15).Unix(), // 令牌過期時間為15分鐘
}
// 創(chuàng)建 Token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
// 簽名并獲取完整的 Token 字符串
tokenString, err := token.SignedString(secretKey)
if err != nil {
return "", err
}
return tokenString, nil
}在上面的代碼中, 認(rèn)證中心服務(wù)端在本地監(jiān)聽8080端口, 用來模擬處理用戶的登錄請求, 在收到用戶請求之后, 服務(wù)端根據(jù)用戶ID調(diào)用JWT的方法生成Token, 并將Token設(shè)置到HTTP頭的Authorization字段中返回給客戶端。
接下來實現(xiàn)資源提供的服務(wù)端,參考代碼如下:
package main
import (
"fmt"
"net/http"
"github.com/dgrijalva/jwt-go"
)
var secretKey = []byte("btk.gqv7jtu7VZD1dar")
func main() {
http.HandleFunc("/resource", handleResource)
http.ListenAndServe(":8081", nil)
}
func handleResource(w http.ResponseWriter, r *http.Request) {
// 從請求中獲取 Authorization 頭
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "未找到Authorization字段", http.StatusUnauthorized)
return
}
// 解析JWT令牌
tokenString := authHeader[len("Bearer "):]
token, err := parseToken(tokenString)
if err != nil || !token.Valid {
http.Error(w, "令牌不合法", http.StatusUnauthorized)
return
}
// 獲取負(fù)載信息
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
http.Error(w, "令牌不合法", http.StatusUnauthorized)
return
}
userID, ok := claims["user"].(string)
if !ok {
http.Error(w, "訪問令牌中的用戶不存在", http.StatusUnauthorized)
return
}
// 在實際應(yīng)用中,這里可以根據(jù) userID 獲取用戶信息或提供資源
// 這里只是一個簡單的示例
response := fmt.Sprintf("用戶ID%s獲取資源成功!", userID)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, response)
}
func parseToken(tokenString string) (*jwt.Token, error) {
// 解析 Token 字符串
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return secretKey, nil
})
if err != nil {
return nil, err
}
return token, nil
}在上面的代碼中, 資源服務(wù)端監(jiān)聽本地的8081端口, 當(dāng)接收到用戶請求之后, 首先從請求頭中的Authorization字段獲取Token令牌, 并對令牌進行解析, 如果正確解析出用戶ID, 返回該用戶的資源信息。
將上面兩段代碼分別編譯成兩個獨立Server端,并開啟兩個窗口分別運行。首先請求SSO服務(wù)端, 返回結(jié)果如下:
圖片
從上圖可知,SSO服務(wù)端成功返回了一個Token令牌, 下面先不用Token訪問一下資源服務(wù)器試試:
圖片
從上圖可以看到,沒有令牌無法正常請求到所需資源, 下面使用Apifox新建一個請求, 在里面加上Token, 如圖:
圖片
保存之后, 帶著Token請求一下資源服務(wù)端, 如圖:
圖片
可以看到, 成功返回了資源, 整個流程測試成功。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流