精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

快手二面:有了Cookie和Session 為什么還要JWT ?你說一下JWT的原理?

開發 前端
JWT(JSON Web Tokens)作為一種輕量級且靈活的身份驗證和授權機制,在現代web服務及移動應用中得到了廣泛應用。它允許服務器端通過加密簽名的方式向客戶端發放安全的、自包含的令牌,這些令牌可以攜帶必要的用戶身份信息和權限聲明,而且由于其無需持久化存儲的特性,非常適合于微服務架構下的無狀態通信場景。

引言

在業務系統開發中,用戶身份驗證是保障系統服務安全性的基石。無論是社交網絡、電商平臺還是企業級應用,都需要確保用戶的訪問權限與行為與其真實身份相符。為了達到這一目的,我們通常采用了一系列技術手段來管理用戶會話并驗證其身份,其中最常見的是Cookie和Session機制。但是,在近年來隨著微服務以及API驅動架構的發展,JSON Web Tokens(JWT)作為一種輕量級的身份驗證方案得到了廣泛的關注和應用。

那么,JWT究竟是什么?它的原理是如何運作的?又為何在已有Cookie和Session的基礎上,仍需要引入JWT作為身份驗證的一種新方法呢?

Cookie和Session

Cookie是什么?

Cookie是Web開發中一種關鍵的客戶端存儲技術,它是由Web服務器在用戶訪問時生成并發送至用戶瀏覽器的微型數據包,以文本文件的形式儲存在用戶的設備上。瀏覽器根據HTTP協議的規定,在之后對同一服務器的所有請求中自動附帶這些Cookie信息。

服務器通過在HTTP響應頭中設置Set-Cookie字段,來指示瀏覽器保存具有特定名稱、值和其他配置參數(例如有效期、安全選項、路徑限制、域名限制等)的Cookie。這樣,每一個Cookie實質上就是一對鍵值對,幫助服務器識別和區分不同的用戶及其在網站上的交互歷史。

Cookie的一個核心應用是在用戶身份驗證和會話管理方面,它可存儲用戶的會話ID,使得用戶在不同頁面間切換或重新打開網站時,服務器能夠基于這個ID重建用戶的會話狀態,實現無縫登錄和個性化體驗。此外,Cookie還廣泛應用于記錄用戶喜好、統計分析用戶行為、廣告定向投放等方面,以提升用戶體驗和服務質量。但是,由于Cookie可能涉及用戶隱私,現代瀏覽器和相關法規越來越重視對Cookie使用的透明度和用戶控制權。

Seeion是什么?

Session作為一種服務器端機制,旨在管理和維護用戶在其交互期間的狀態信息,這些信息被安全地存儲在服務器內存或者持久化存儲(如數據庫)中,與僅在客戶端存儲的Cookie形成了鮮明對比。每當用戶開始與服務器建立聯系時,服務器會初始化一個新的Session對象,并將其狀態數據保存在服務器內部資源中。

一旦Session建立,服務器會產生一個獨一無二的Session標識符——Session ID,該ID隨后會通過HTTP響應中的Cookie(通常命名規則包括但不限于JSESSIONID)下發至客戶端瀏覽器進行存儲。此后,用戶的每一次請求都將攜帶著這個Session ID,服務器通過解析請求中的Session ID,能夠在自身的Session存儲區域內檢索到相對應的Session對象,從而實時獲取并更新用戶會話的具體狀態。

Session機制廣泛應用在諸如認證授權場景中,用來持久化登錄用戶的憑證和權限等敏感信息,確保用戶在瀏覽網站的不同頁面時仍能維持登錄狀態和個性化體驗。此外,Session還能用于暫存用戶會話過程中的臨時數據,比如購物車的內容、網頁表單填寫的中間狀態等,直至會話自然終止(如超時)、用戶主動登出或清理Session時,這些數據才會失效。

但是雖然Cookie和Session在會話管理中一直使用,但它們存在一些缺點如下:

  • Cookie數量和大小限制:瀏覽器對Cookie的數量和大小有限制,可能導致用戶數據存儲受限。
  • 服務器資源占用:Session需要在服務器端存儲大量用戶會話信息,隨著并發用戶數的增長,可能會造成服務器內存資源緊張。
  • 跨域問題:Cookie默認遵循同源策略,不便于跨域共享會話狀態。
  • 分布式系統中的Session同步:在分布式環境或集群部署的情況下,Session數據需要在各服務器之間同步,增加了系統的復雜性。

為解決上述會話管理中的缺點,以及對無狀態服務、更好的擴展性和安全性要求,JWT這樣的新型身份驗證技術就這樣誕生了。JWT通過將用戶信息加密打包成Token,讓客戶端自行攜帶認證信息,從而實現了服務器的無狀態化。

JWT是什么?

JSON Web Tokens (JWT) 是一種開放標準(RFC 7519),定義了一種緊湊、自包含的方式來安全地在各方之間傳輸信息。JWT主要由三個部分組成,即Header(頭部)、Payload(載荷)和Signature(簽名),這三部分之間通過.分隔。

JWT的組成JWT的組成

JWT的內容采用Base64編碼,可以直接嵌入到HTTP請求頭或者URL查詢參數中,因其具有可讀性、自包含和防篡改的特點而廣泛應用在身份驗證和授權場景中。

Header

描述了所使用的JWT類型(通常為JWT)以及簽名算法(如HS256、RS256等)。

? typ: 表明這是一個JWT(固定為 "JWT")。

? alg: 指定用于簽署JWT的算法,如 "HS256"(HMAC SHA-256)或 "RS256"(RSA SHA-256)等。

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

包含了實際要傳遞的數據,可以是任意的JSON對象,包含一組稱為聲明(claims)的數據。一般為用戶身份信息(如用戶ID、角色、權限等)和其他自定義聲明,還包含一個exp(Expiration Time)字段來設置JWT的有效期以及其他元數據。聲明分為三種類型:

  1. Registered Claims(注冊聲明) 這些是預先定義好的標準聲明,雖然不是必須的,但在JWT規范中建議使用。它們提供了一套通用的信息,有助于JWT的標準化處理。常見的注冊聲明包括:

? iss(issuer):簽發JWT的實體。

? sub(subject):JWT所面向的用戶或主題。

? aud(audience):預期接收JWT的受眾。

? exp(expiration time):JWT過期時間,在此時間之后JWT應被視為無效。

? nbf(not before):JWT生效時間之前,不應被接受處理的時間點。

? iat(issued at):JWT的創建時間。

? jti(JWT ID):JWT的唯一標識符,可用于防止重放攻擊。

2. Public Claims(公共聲明) 公共聲明是預留用于行業共識的標準聲明,雖然目前并未正式注冊到IANA JSON Web Token Registry,但可以在IANA JSON Web Token Registry 查看已注冊的聲明集。如果沒有官方注冊,但多個項目間需要共享相同的聲明,可以選擇將聲明名稱以特定前綴(如 urn:<namespace>:<name>)進行命名,避免沖突。

3. Private Claims(私有聲明) 私有聲明是用于應用內部約定的自定義聲明,我們可以根據自己業務需求自由定義。例如,可以包含用戶ID (userId)、用戶角色 (role)、郵箱地址 (email) 等任何想要在JWT中傳遞的信息。

示例:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022,
  "jti": "1pmysetgujcoden",
  "exp": 1516242622,
  "scope": [
    "read",
    "write"
  ]
}

Payload部分默認是不加密的,一定不要將隱私信息存放在 Payload 當中?。?!

Signature

Signature部分用于確保JWT在傳輸過程中沒有被篡改,它是通過對前兩部分(Header和Payload編碼后的字符串)使用Header中指定的加密算法以及一個共享或私有的密鑰進行簽名計算得到的。簽名確保了只有知道該密鑰的實體才能創建有效的JWT,并且任何人都可以驗證JWT的完整性和來源的真實性。

生成簽名的計算公式如下:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出簽名以后,把Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,這個字符串就是JWT 。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwianRpIjoiMXBteXNldGd1amNvZGVuIiwiZXhwIjoxNTE2MjQyNjIyLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.eW91cmUtYXV0aG9yaXphdGlvbi1zaWduYXR1cmU=

對于這段字符串,我們在JWT官網中,使用它的解碼器進行解碼,就可以得到Header、Payload、Signature這三部分。

圖片圖片

如何基于JWT進行身份驗證?

JWT生成

服務器首先構建JWT的Header和Payload,并分別進行Base64編碼。使用Header中指定的簽名算法(比如HMAC SHA-256或RSA)對編碼后的Header和Payload進行簽名,生成Signature。將Header、Payload和Signature連接在一起,形成完整的JWT字符串。

JWT發放

用戶通過用戶名/密碼登錄后,服務器驗證用戶身份無誤,便生成JWT并將其返回給客戶端。客戶端可以將JWT存儲在瀏覽器的LocalStorage、SessionStorage中,或者作為Bearer Token放在Authorization請求頭中。

這里需要注意,將JWT存儲在瀏覽器的localStorage中,相較于存儲在Cookie中,可以降低CSRF(跨站請求偽造)的風險。因為CSRF攻擊依賴于瀏覽器自動附帶在請求中的Cookie,而localStorage中的數據不會自動包含在普通的HTTP請求頭部。

至于攜帶JWT的方式,建議將JWT放在HTTP Header的Authorization字段中,采用Bearer Token的形式:

Authorization: Bearer <JWT>

這種做法允許在無狀態的RESTful API中方便地進行身份驗證,服務器可以根據請求頭中的JWT來驗證請求發起者的身份和權限。同時,這種方式也便于實現JWT的刷新與撤銷。

JWT使用

在后續的請求中,客戶端將JWT隨HTTP請求一起發送給服務器。服務器接收到JWT后,對其進行解碼和驗證Signature,如果Signature正確,則說明JWT未被篡改,并且仍處于有效期內。根據Payload中的信息,服務器可以確定用戶身份以及相關權限,從而做出相應處理。

JWT過期與刷新

JWT有一定的生命周期,過了指定的exp字段時間后,服務器不再接受該JWT。對于長時間交互的場景,可以設計Refresh Token機制,當JWT即將過期時,客戶端使用Refresh Token去服務器申請新的JWT,以延長用戶會話的有效期。

圖片圖片

JWT安全性

JWT如何防止被篡改?

JWT通過簽名(Signature)來防止被篡改。數字簽名的機制確保了JWT的內容一旦被篡改,服務端就能檢測出來,從而拒絕非法的請求。

當客戶端帶著JWT向服務器發送請求時,服務器會先將接收到的JWT按.分割成Header、Payload和Signature三部分。服務器使用與生成簽名時相同的密鑰和算法,根據接收到的Header和Payload重新計算簽名。如果重新計算出的簽名與接收到的Signature一致,那么可以認為JWT在傳輸過程中未被篡改;如果不一致,則說明JWT已被篡改。

當然,JWT通過這種方式防止被篡改的一個大前提就是,秘鑰(Secret Key)是安全的,如果秘鑰泄漏,那就可以更改Header、Payload,然后利用這個秘鑰生成一個Signature就可以了,那么就不安全了。所以,我們一定要確保服務端使用的密鑰是安全的,并且嚴格保密,不對外泄露。

如何加強JWT的安全性?

作為使用者,防止JWT被篡改的主要措施并不直接體現在客戶端的操作上,而是依賴于服務端的安全設計和實施。我們可以通過以下方面考慮,加強JWT的安全性:

1. 服務端安全策略:首先我們必須確保服務端使用的密鑰是安全的,并且嚴格保密,不對外泄露。密鑰用于簽署和驗證JWT,一旦密鑰泄露,可能導致JWT被偽造或篡改。并且還要采用安全強度足夠高的簽名算法,例如HS256、RS256等,這些算法能有效地確保JWT的完整性。

2. 正確的簽名流程:確保所有簽發的JWT都經過服務器端簽名,并且每次驗證JWT時都要使用相同的密鑰和算法進行驗證。

3. 設置合理的Token有效期:設置JWT的有效期(exp claim)限制,避免JWT長時間有效,減少惡意攻擊者篡改JWT后繼續使用的機會。

4. 嚴謹的身份驗證邏輯:在服務端驗證JWT時,除了驗證簽名之外,還應對Payload中的其他重要聲明(如用戶ID、權限、過期時間等)進行校驗。

5. 客戶端存儲方式:盡管JWT被篡改的風險主要集中在網絡傳輸階段,但在客戶端存儲JWT時,推薦使用localStorage而非Cookie(尤其是考慮到防止CSRF攻擊),同時也可以考慮對敏感信息加密存儲。

JWT相較于Cookie和Session的優缺點

JWT的優點

JWT是無狀態的,這樣服務器不需要存儲會話狀態信息,減輕了服務器端的存儲負擔,在分布式環境下使用JWT可以在集群內的任何節點驗證,無需保持會話狀態同步,JWT還可以輕易地在不同域名的服務之間傳遞,適用于微服務架構和跨域應用。因為它包含了所有必要的用戶信息,所以在服務間跳轉時無需再次驗證用戶身份,每個請求都包含了認證所需要的所有信息,減少了服務器查詢數據庫或存儲服務以驗證用戶狀態的次數,從而提高了效率。

JWT的缺點

JWT中的數據默認情況下是Base64編碼的,雖然可以加密但并非強制要求,這使得在不加密的情況下,JWT不適合存儲敏感信息。相比而言,Session存儲在服務器端,安全性更高。如果JWT被盜取,黑客可以在有效期內持續使用,直到token過期或被撤銷。另外JWT的體積相對較大,特別是當包含更多信息時。每次HTTP請求都需要攜帶JWT,可能會增加請求頭的大小,尤其是在移動網絡環境下,可能會對帶寬和流量有較大影響。并且由于JWT自帶有效期,當用戶登出時,由于JWT無法立即失效,所以只要JWT發放出去,除非在服務器端維護黑名單或使用可撤銷的Token機制(如JWT ID Token配合OpenID Connect協議),否則無法立即廢棄或更改Token中的權限信息。

JWT應用場景

? 身份驗證:JWT通常用于用戶登錄認證,服務器在驗證用戶憑據成功后,生成一個帶有用戶特定信息和過期時間的JWT,客戶端收到后,在后續請求中將其作為憑證發送給服務器,服務器僅需驗證JWT的簽名即可確認用戶身份,無需持久化存儲會話信息。

? 授權:JWT可以攜帶權限聲明,服務器通過解析JWT中的聲明,決定用戶是否有權訪問特定資源或執行某些操作。

? 單點登錄(SSO):由于JWT可以跨域使用,因此在多個子系統間輕松實現SSO功能,用戶在一個系統登錄后,其JWT可在多個系統間傳遞,實現無縫登錄體驗。

在高并發、分布式、跨域及移動端場景下,JWT常因其實現的簡潔性和高效性而得到青睞,而在注重數據安全性、復雜會話管理和易控制的場景下,Session可能會是更好的選擇。同時,很多現代應用采用了混合策略,結合兩者的優勢來優化用戶體驗和系統安全性。

如何使用JWT?

在實際開發中,我們可以使用Spring Boot框架結合JWT來實現用戶身份驗證。我們來實現一個簡單的示例。

我們引入JWT的依賴包:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.12.4</version>
</dependency>

創建JWT工具類

創建一個JWT工具類,用于生成和驗證JWT令牌。這個類通常會包含生成JWT、解析JWT以及提取其中載荷信息的方法。

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;

/**
 * @version 1.0
 * @description: <p></p >
 * @author: 碼農Academy
 * @create: 2024/4/18 14:39
 */
@Component
public class JwtTokenUtil {

    @Value("${security.jwt.token.secret-key}")
    private String secretKey;

    /**過期時間*/
    private static final long VALIDITY_IN_MILLISECONDS = 3600000; // 1h

    /**
     * 生成JWT
     * @param authentication 當前登陸用戶信息
     * @return JWT
     */
    public String generateToken(Authentication authentication) {
        User user = (User) authentication.getPrincipal();
        Date now = new Date();
        // 設置過期時間
        Date expiryDate = new Date(now.getTime() + VALIDITY_IN_MILLISECONDS);
        //指定加密算法
        SecureDigestAlgorithm<SecretKey, SecretKey> algorithm = Jwts.SIG.HS256;
        //密鑰實例
        SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes());
        return Jwts.builder()
                .subject(user.getUsername())
                .signWith(key, algorithm) //設置簽名使用的簽名算法和簽名使用的秘鑰
                .expiration(expiryDate) //設置過期時間
                .claim("username", user.getUsername()) //設置自定義負載信息
                .compact();
    }


    /**
     * 驗證JWT
     * @param authToken
     * @return
     */
    public boolean validateToken(String authToken) {
        try {
            Jws<Claims> claimsJws = parseJWT(authToken);
            claimsJws.getPayload();
            return true;
        } catch (MalformedJwtException | ExpiredJwtException | UnsupportedJwtException | IllegalArgumentException e) {
            return false;
        }
    }

    /**
     * 解析jwt
     * @param authToken 登陸token
     * @return
     */
    public Jws<Claims> parseJWT(String authToken){
        //密鑰實例
        SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes());
        return Jwts.parser()
                .verifyWith(key)  //設置簽名的密鑰
                .build()
                .parseSignedClaims(authToken); //設置要解析的jwt
    }

    /**
     * 從JWT中獲取用戶名
     * @param token
     * @return
     */
    public String getUsernameFromToken(String token) {
        Jws<Claims> claimsJws = parseJWT(token);
        return (String) claimsJws.getPayload().get("username", String.class);
    }
}

實現JWT過濾器

創建一個自定義的JWT過濾器,它負責檢查每個請求的Authorization header中的Bearer Token,并對其進行驗證。驗證成功后,創建一個Authentication對象,并將其置于SecurityContext中。

import com.springboot.base.jwt.util.JwtTokenUtil;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @version 1.0
 * @description: <p></p >
 * @author: 碼農Academy
 * @create: 2024/4/18 16:15
 */
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final UserDetailsService userDetailsService;
    private final JwtTokenUtil jwtTokenUtil;

    private static final String BEARER_PREFIX = "Bearer ";

     public JwtAuthenticationFilter(UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil) {
          this.userDetailsService = userDetailsService;
          this.jwtTokenUtil = jwtTokenUtil;
     }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String jwtToken = getTokenFromRequest(request);
        if (jwtToken != null && jwtTokenUtil.validateToken(jwtToken)) {
            String username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            // 用于權限校驗
            List<GrantedAuthority> authorities = userDetails.getAuthorities().stream()
                    .map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))
                    .collect(Collectors.toList());

            AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                    userDetails, null, authorities
            );
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            SecurityContextHolder.getContext().setAuthentication(authentication);
        }else {
            // 如果出現異常,可能是無效的Token,此處可記錄日志或做其他處理
            System.out.println("沒有登陸。。。。。。");
        }

        filterChain.doFilter(request, response);
    }

    private String getTokenFromRequest(HttpServletRequest request) {
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) {
            return authHeader.replace(BEARER_PREFIX, "");
        }
        return null;
    }
}

配置Spring Security

配置Spring Security以支持JWT身份驗證。這通常涉及編寫自定義的JWT過濾器,用于攔截請求并在請求頭中提取JWT令牌進行驗證。過濾器會在驗證通過后將用戶信息填充到Spring Security的上下文中。

import com.springboot.base.jwt.filter.JwtAuthenticationFilter;
import com.springboot.base.jwt.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @version 1.0
 * @description: <p></p >
 * @author: 碼農Academy
 * @create: 2024/4/18 16:11
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests((requests) -> requests
                        .antMatchers("/user/login").permitAll() // 登錄接口公開
                        .anyRequest().authenticated() // 其他接口需要驗證
                )
                .addFilterBefore(new JwtAuthenticationFilter(userDetailsService, jwtTokenUtil), UsernamePasswordAuthenticationFilter.class) // 添加JWT過濾器
                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 關閉Session管理
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

在實現Spring Security的UserDetailsService的接口,用于登陸時查詢用戶信息,構造Spring Security的User信息。

import com.springboot.base.jwt.login.RoleEntity;
import com.springboot.base.jwt.login.UserEntity;
import com.springboot.base.jwt.repository.UserRepository;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @version 1.0
 * @description: <p></p >
 * @author: 碼農Academy
 * @create: 2024/4/18 16:22
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;

    public UserDetailsServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        // 根據用戶名查詢用戶實體
        UserEntity userEntity = userRepository.findByUserName(username);

        // 如果用戶不存在,則拋出異常
        if (userEntity == null) {
            throw new UsernameNotFoundException("User not found with username: " + username);
        }

        // 將用戶實體轉換為Spring Security可識別的UserDetails對象
        User user = new User(userEntity.getUserName(),
                userEntity.getPassword(), // 注意:此處假設password字段是已經經過加密處理過的
                userEntity.getEnabled(),
                true, true, true,
                getAuthorities(userEntity.getRoleList()) // 獲取用戶的角色并轉換為GrantedAuthority列表
        );
        System.out.println(user.getPassword());
        return user;
    }

    private Collection<? extends GrantedAuthority> getAuthorities(List<RoleEntity> roles) {
        return roles.stream()
                .map(role -> new SimpleGrantedAuthority(role.getRoleCode()))
                .collect(Collectors.toList());
    }
}

上述使用Spring Security,如果有疑問的東西可以去查一下Spring Security的工作原理。

登陸接口

我們在定義一個登陸接口,登陸成功后返回jwt生成的token。

import com.springboot.base.jwt.request.LoginRequest;
import com.springboot.base.jwt.response.JwtResponse;
import com.springboot.base.jwt.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @version 1.0
 * @description: <p></p >
 * @author: 碼農Academy
 * @create: 2024/4/18 17:31
 */
@RestController
@RequestMapping("/user")
public class LoginController {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private AuthenticationManager authenticationManager;

    @PostMapping("/login")
    public ResponseEntity<?> createAuthenticationToken(@RequestBody LoginRequest loginRequest) throws Exception {
        // 創建基于用戶名密碼的Authentication請求對象
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                loginRequest.getUserName(),
                loginRequest.getPassword()
        );

        try {
            // 使用AuthenticationManager進行身份驗證
            Authentication authentication = authenticationManager.authenticate(authToken);

            // 如果身份驗證成功,SecurityContextHolder會存儲該Authentication對象
            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 生成JWT Token或其他形式的授權憑證
            String token = jwtTokenUtil.generateToken(authentication);

            // 返回帶有Token的響應
            return ResponseEntity.ok(new JwtResponse(token));

        } catch (BadCredentialsException ex) {
            // 處理用戶名/密碼不正確等錯誤情況
            System.out.println("密碼不正確");
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
        } catch (DisabledException ex) {
            // 處理賬戶被禁用等情況
            System.out.println("處理賬戶被禁用");
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Account is disabled");
        } catch (AuthenticationException e) {
            System.out.println("Authentication failed");
            // 其他身份驗證異常
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Authentication failed");
        }
    }

}
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.Data;

/**
 * @version 1.0
 * @description: <p></p >
 * @author: 碼農Academy
 * @create: 2024/4/18 18:59
 */
@Data
public class JwtResponse {

    private String token;
    private String type = "Bearer";

    public JwtResponse(String token) {
        this.token = token;
    }

    @JsonCreator
    public static JwtResponse of(String token) {
        return new JwtResponse(token);
    }
}

我們模擬一下登陸發起請求:

登陸成功,返回token。

給一個錯誤密碼,登陸失敗:

我們將token設置到Authorization中,發起其他業務請求:

接口請求成功。

我們改變一下token值,在發起請求:

后臺打印token校驗失敗,即未登錄。

結論

JWT(JSON Web Tokens)作為一種輕量級且靈活的身份驗證和授權機制,在現代web服務及移動應用中得到了廣泛應用。它允許服務器端通過加密簽名的方式向客戶端發放安全的、自包含的令牌,這些令牌可以攜帶必要的用戶身份信息和權限聲明,而且由于其無需持久化存儲的特性,非常適合于微服務架構下的無狀態通信場景。

總結起來,采用JWT進行身份驗證具有以下優點:

? 安全性: 通過密鑰簽名保證令牌的安全性,防止篡改。

? 高效性: 狀態less設計減少了服務器端存儲負擔,提升了系統的可擴展性和響應速度。

? 跨域友好: JWT可輕松應用于多個域名或子系統間的認證需求。

? 自包含性: 令牌自身攜帶了足夠的用戶信息,減輕了服務器端查詢數據庫的頻率。

? 有效期可控: 可以在JWT中設置過期時間,從而控制用戶的登錄會話持續時間。

不過需要注意的是,JWT并非適用于所有場景,尤其是在敏感數據的處理上,因為一旦令牌被截獲,黑客們在有效期內可以持續使用。因此,在實施JWT方案時,應充分考慮其適用范圍,并配合合適的刷新策略、黑名單機制以及其他安全措施,確保系統的整體安全性和用戶體驗。

責任編輯:武曉燕 來源: 碼農Academy
相關推薦

2025-08-11 02:00:00

JWTSession系統

2021-08-09 08:53:30

HTTP狀態化協議

2024-04-03 15:33:04

JWTSession傳輸信息

2023-12-06 09:10:28

JWT微服務

2021-03-23 10:45:23

CookieSession前端

2025-03-07 00:11:00

JWTJSONSession

2022-06-07 08:39:35

RPCHTTP

2024-09-29 09:50:05

2020-11-25 09:36:17

HTTPRPC遠程

2019-08-05 14:23:43

DockerKubernetes容器

2024-07-11 10:41:07

HTTPSHTTP文本傳輸協議

2024-04-26 12:45:39

JWTCookieSession

2023-12-11 12:03:14

Python工具元組

2021-08-26 06:58:14

CookieSession應用

2023-01-12 09:01:01

MongoDBMySQL

2023-07-07 09:08:21

2021-03-29 09:25:58

JWTSpringNimbus

2025-03-24 07:35:00

開發注解Spring

2015-09-01 09:28:55

calayeruiview區別

2024-04-07 10:07:52

點贊
收藏

51CTO技術棧公眾號

午夜视频在线观看精品中文| 男人的天堂在线视频免费观看 | 国产成人精品亚洲777人妖| 欧美成人午夜免费视在线看片| 欧美成人精品在线观看| 尤物网站在线看| 亚洲小说区图片区都市| 波多野结衣91| 亚洲欧美激情精品一区二区| 激情五月亚洲色图| 美女黄视频在线观看| 国产69精品久久久久777| 欧美亚洲一级片| 国产一二三四区在线| 成人黄色91| 五月综合激情婷婷六月色窝| 日韩亚洲不卡在线| 国产又爽又黄免费软件| 亚洲手机视频| 在线看欧美日韩| 日韩av影视大全| 影音先锋在线播放| 91亚洲永久精品| 国产一区二区在线免费| 久草国产在线视频| 成人写真视频| 亚洲国产精品人人爽夜夜爽| 久久久综合亚洲91久久98 | 欧美性jizz18性欧美| 天堂精品视频| 狠狠躁日日躁夜夜躁av| 免费黄网站欧美| 韩国三级日本三级少妇99| 日本欧美一区二区三区不卡视频| 伊人久久亚洲| 欧美日韩精品电影| 日韩在线视频在线观看| a级在线观看| 97超碰欧美中文字幕| 成人有码在线视频| 丁香社区五月天| 黄色一区二区三区四区| 久久天堂av综合合色| 亚洲最大成人网站| 成人av地址| 777午夜精品视频在线播放| www.com毛片| 久久香蕉av| 成人免费在线播放视频| 欧美一区二区三区四区夜夜大片| 欧美 日韩 国产 在线| 色婷婷热久久| 亚洲精品久久久久久久久久久久久| 无尽裸体动漫2d在线观看| 视频在线不卡| 国产精品中文有码| 国产欧美欧洲在线观看| 久久精品视频7| 日韩一级大片| 久久久久一本一区二区青青蜜月| 国产午夜精品理论片| 欧美影院三区| 国产一区二区三区视频| 无码h肉动漫在线观看| 激情小说一区| 亚洲国产99精品国自产| 色婷婷狠狠18禁久久| 欧美第一在线视频| 日韩欧美国产系列| 毛片毛片毛片毛片毛| 亚洲久草在线| 欧美一区二区三区在线| 999热精品视频| 精品午夜视频| 日韩精品一区在线观看| 国产人妖在线观看| 成功精品影院| 日韩高清av一区二区三区| 99re久久精品国产| 日韩精品丝袜美腿| 亚洲欧美一区二区三区在线| 亚洲熟妇无码av| 欧美禁忌电影| 国产亚洲精品综合一区91| 在哪里可以看毛片| 成人女性视频| 另类专区欧美制服同性| 青娱乐国产在线视频| 雨宫琴音一区二区在线| 欧美韩国理论所午夜片917电影| 国内偷拍精品视频| 女人天堂亚洲aⅴ在线观看| 日韩美女一区二区三区| 性活交片大全免费看| 日本三级久久| 国产一区二区三区三区在线观看| 日本在线观看网址| 欧美成人tv| 97在线免费视频| 日本一本在线观看| 日韩高清在线一区| 川上优av一区二区线观看| www.黄色片| 久久不射中文字幕| 国产成人精品视频在线观看| 91丨九色丨海角社区| 秋霞电影一区二区| 成人免费在线视频网站| 国产高清不卡视频| 久久久久亚洲综合| 中文字幕成人一区| 热三久草你在线| 欧美中文字幕一区二区三区亚洲| 香蕉视频色在线观看| 欧美黑白配在线| 在线观看精品自拍私拍| 久久国产露脸精品国产| 日韩中文欧美在线| 99久久精品无码一区二区毛片 | 中文字幕一区久| 欧美美女黄视频| 狠狠人妻久久久久久综合蜜桃| 久久在线视频| 91国内产香蕉| 99视频国产精品免费观看a| 99re这里都是精品| 麻豆视频传媒入口| 二吊插入一穴一区二区| 精品欧美久久久| 国产黄a三级三级| 欧美巨大xxxx| 久久成人在线视频| 日本欧美www| 波多野结衣一区二区三区| 国产一区一区三区| 成人全视频免费观看在线看| 亚洲第一区在线观看| 午夜国产福利一区二区| 蜜桃精品在线观看| 国产伦一区二区三区色一情 | x99av成人免费| 久久久久久久久久久久久av| 国产精品18久久久久| 亚洲国产精品123| 欧美专区福利免费| 亚洲第一偷拍网| 欧美日韩激情在线观看| 精品一区二区三区在线播放视频| 国产日韩在线一区二区三区| caopo在线| 欧美高清激情brazzers| 日韩毛片无码永久免费看| 亚洲综合二区| 国产一区二区三区四区五区在线 | 色就是色欧美| av第一福利在线导航| 91精品国模一区二区三区| 中文字幕第20页| 乱码第一页成人| 精品在线视频一区二区| 国产免费福利视频| 国产精品麻豆久久久| 国产又黄又猛又粗又爽的视频| 欧美色片在线观看| 亚洲欧美日韩一区在线| 亚洲欧美综合另类| 91免费观看视频| 国产主播在线看| 精品一区毛片| 国产91网红主播在线观看| 邻家有女韩剧在线观看国语| 一本色道久久综合精品竹菊| 亚洲一级中文字幕| 日本va欧美va精品| 亚洲永久一区二区三区在线| 精品欧美日韩精品| 日韩视频中文字幕| 99产精品成人啪免费网站| 中文字幕五月欧美| 999在线观看| 欧美激情综合| 成人免费视频网站入口| av电影院在线看| 亚洲社区在线观看| 91久久久久久久久久久久| 亚洲色图清纯唯美| 在线观看免费的av| 欧美女人交a| 精品九九九九| 亚洲成人不卡| 久久精品亚洲精品| 国产77777| 色成人在线视频| 国产探花在线视频| 成人黄色av电影| 精品视频无码一区二区三区| 国产日产一区| 欧美精品手机在线| 日本免费不卡视频| 在线观看亚洲一区| 精品国产视频在线观看| 成人18视频在线播放| 免费黄色一级网站| 欧美久久一级| 欧美一区二区三区四区在线观看地址 | 免费一级欧美片在线观看网站| 久久99国产精品自在自在app| 国产自产一区二区| 欧美日韩久久久久| 色婷婷av777| 国产乱色国产精品免费视频| 国产一区二区三区小说| 蜜桃成人av| 国产在线视频一区| jizz一区二区三区| 中文国产成人精品久久一| www.蜜臀av| 色婷婷综合久久久久中文| 国产福利在线导航| 成人综合激情网| 一级黄色香蕉视频| 欧美日韩国产综合网| 欧美大陆一区二区| 精品国产不卡一区二区| 国产91色在线播放| 97人澡人人添人人爽欧美| 亚洲欧美日韩第一区| 国产伦精品一区二区三区视频痴汉| 亚洲一区二区免费视频| 中文字幕网站在线观看| 国产美女主播视频一区| 99久久激情视频| 亚洲精品日本| 日韩不卡视频一区二区| 欧美色图五月天| 91成人在线看| 日本成人在线网站| 欧美又大又粗又长| 日本不卡影院| 亚洲免费一级电影| 国产三级按摩推拿按摩| 欧美影院一区二区| 亚洲综合久久网| 激情av一区二区| 日韩免费一二三区| 夜夜嗨av一区二区三区网页 | av中文字幕在线不卡| 国产无色aaa| 麻豆成人免费电影| 亚洲精品日韩成人| 国产99精品| 久久婷婷人人澡人人喊人人爽| 永久免费精品视频| 97久久人人超碰caoprom欧美| 色综合久久久| 国产日韩在线视频| 日本中文字幕视频一区| 国产免费一区二区三区在线能观看| 欧美色999| 国产成人精品久久亚洲高清不卡| 免费观看一级欧美片| 91成人精品网站| 天堂电影一区| 欧美亚洲免费电影| 丝袜美腿诱惑一区二区三区| 日本高清久久天堂| 香蕉视频亚洲一级| 国产成人激情视频| 免费视频观看成人| 91免费精品国偷自产在线| 成人污版视频| 91九色极品视频| 哺乳挤奶一区二区三区免费看| 动漫3d精品一区二区三区| 91麻豆精品国产91久久久久推荐资源| 91视频最新| 国产96在线亚洲| 国产精品久久久久不卡| 日本孕妇大胆孕交无码| 国内精品久久久久久影视8| 人人草在线视频| 国产精品白丝jk喷水视频一区| 国产精品久久久久久妇女| 国产日韩欧美在线| 日韩综合一区二区三区| 国产日韩三区| 国产精品一区二区av交换| 亚洲天堂电影网| 亚洲色图欧美| 国产原创popny丨九色| 香蕉久久夜色精品| 视色视频在线观看| 国产福利精品导航| 538国产视频| 中文字幕av免费专区久久| 国语对白在线播放| 婷婷久久综合九色综合伊人色| 欧美性猛交bbbbb精品| 欧美日韩国产欧美日美国产精品| www香蕉视频| 亚洲男人天堂视频| 国产福利视频在线| 78色国产精品| 国产极品一区| 国产精品一区在线观看| 牲欧美videos精品| 一本一本久久a久久精品综合妖精| 欧美久久成人| 爱福利视频一区二区| 捆绑调教一区二区三区| 午夜福利123| 久久一二三国产| 最新av电影网站| 性感美女极品91精品| 国产精品视频在线观看免费| 亚洲精品狠狠操| 欧美jizz18hd性欧美| 欧美一级大片视频| 精品国产一级| 婷婷亚洲婷婷综合色香五月| 一区久久精品| 涩涩网站在线看| 久久人人97超碰com| 国产精品变态另类虐交| 5858s免费视频成人| 免费播放片a高清在线观看| 欧美高清自拍一区| 欧美天堂一区| 欧美一级二级三级| 亚洲激情影院| 日本一区二区三区在线免费观看| 国产午夜精品久久久久久久 | 日韩毛片在线一区二区毛片| 久久高清视频免费| 久久伊人国产| 日本在线视频一区| 欧美三级第一页| 亚洲视频第二页| 国产色产综合色产在线视频| 久一视频在线观看| 3751色影院一区二区三区| av在线电影网| 91高清视频免费观看| 亚洲国产视频二区| 国产女人18毛片| 蜜臀av一级做a爰片久久| 国产精品亚洲无码| 欧美性jizz18性欧美| 午夜视频福利在线观看| 欧美激情视频一区| 亚洲精品在线国产| 超级碰在线观看| 国产一区二区精品在线观看| 色婷婷粉嫩av| 777a∨成人精品桃花网| 91在线看黄| 国产精品自产拍在线观看中文| 国内精品久久久久久久影视简单| 日韩a在线播放| 久久久久久**毛片大全| chinese国产精品| 亚洲人成电影网站色xx| 国产精欧美一区二区三区蓝颜男同| 免费毛片一区二区三区久久久| 午夜亚洲性色福利视频| 亚洲人成人无码网www国产| 色综合久久久久网| 国内av一区二区三区| 国产精品成人一区| 精品久久视频| 色悠悠久久综合网| 国产精品国产三级国产aⅴ无密码| 中文区中文字幕免费看| 久久精彩免费视频| 秋霞一区二区三区| 精品成在人线av无码免费看| 99久久久无码国产精品| 日韩熟女一区二区| 中文字幕精品一区二区精品| 亚洲精品66| 国产一级做a爰片久久毛片男| zzijzzij亚洲日本少妇熟睡| 亚洲欧美自拍视频| 中文字幕欧美精品日韩中文字幕| 日韩伦理一区二区| 日韩一级免费看| 99re亚洲国产精品| 亚洲国产无线乱码在线观看| 久久艳片www.17c.com| 哺乳一区二区三区中文视频| 日日碰狠狠躁久久躁婷婷| 国产精品久久久久久久岛一牛影视| 伊人成年综合网| 欧美精品一区三区| 丝袜美腿一区二区三区动态图 | 日韩国产欧美在线观看| 国产免费久久久久| 亚洲韩国日本中文字幕| 精品美女一区| 日韩xxxx视频|