簡體   English   中英

JWT 認證:如何實現注銷?

[英]JWT authentication: How to implement logout?

我為 Spring boot 應用程序實現了 JWT 身份驗證。 總的來說,它是這樣工作的:

  1. 客戶端將用戶名、密碼發送到登錄端點。
  2. 服務器檢查提供的憑據是否有效。
  3. 如果不是,它會返回一個錯誤
  4. 如果是,它將返回一個令牌,該令牌實際上包括
  5. 客戶端在以后的每個請求中發送該令牌

問題是,我們應該如何實現注銷?

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.util.Date;

class TokenAuthenticationService {
    static final long EXPIRATIONTIME = 864_000_000; // 10 days
    static final String SECRET = "ThisIsASecret";
    static final String TOKEN_PREFIX = "Bearer";
    static final String HEADER_STRING = "Authorization";

    static void addAuthentication(HttpServletResponse res, String username) {
        String JWT = Jwts
                .builder()
                .setSubject(username)
                .setExpiration(
                        new Date(System.currentTimeMillis() + EXPIRATIONTIME))
                .signWith(SignatureAlgorithm.HS512, SECRET).compact();
        res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT);
    }

    static Authentication getAuthentication(HttpServletRequest request, UserDetailsService customUserDetailsService) {
        String token = request.getHeader(HEADER_STRING);
        if (token != null) {
            // parse the token.
            Claims claims = Jwts.parser().setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
            String userName = claims.getSubject();
            Date expirationTime = claims.getExpiration();
            if (expirationTime.compareTo(new Date()) < 0) {
                return null;
            }
            UserDetails user = customUserDetailsService.loadUserByUsername(userName);
            return user != null ? new UsernamePasswordAuthenticationToken(user.getUsername(),
                    user.getPassword(), user.getAuthorities()) : null;
        }
        return null;
    }
}

addAuthentication類使用JWTLoginFilter來在登錄時發送身份驗證代碼,JWTAuthenticationFilter is used by the “getAuthentication is used by the來過濾對端點的所有請求。

這里的最佳做法是什么?

我認為這里沒有最佳實踐。 我想這取決於您正在構建的應用程序及其要求。

JWT 的好處是它們是無狀態的。 您不需要查詢數據庫來驗證令牌。 當您希望減少數據庫的負載時,這很好,但當您想讓現有的未過期令牌無效時,這很糟糕。

可能的解決方案:

  • 將 JWT 存儲在數據庫中。 您可以檢查哪些令牌是有效的,哪些是被撤銷的,但在我看來,這完全違背了使用 JWT 的目的。
  • 從客戶端刪除令牌。 這將阻止客戶端發出經過身份驗證的請求,但如果令牌仍然有效並且其他人可以訪問它,則仍然可以使用令牌。 這將我引向我的下一點。
  • 令牌壽命短。 讓令牌快速過期。 根據應用程序的不同,可能需要幾分鍾或半小時。 當客戶端刪除其令牌時,它仍然可以使用的時間很短。 從客戶端刪除令牌並具有較短的令牌生命周期不需要對后端進行重大修改。 但是令牌生命周期短意味着用戶會因為令牌已過期而不斷被注銷。
  • 輪換令牌。 也許引入刷新令牌的概念。 當用戶登錄時,為他們提供 JWT 和刷新令牌。 將刷新令牌存儲在數據庫中。 對於經過身份驗證的請求,客戶端可以使用 JWT,但是當令牌過期(或即將過期)時,讓客戶端使用刷新令牌發出請求以換取新的 JWT。 這樣,您只需在用戶登錄或請求新的 JWT 時訪問數據庫。 當用戶注銷時,您需要使存儲的刷新令牌無效。 否則,即使用戶已注銷,監聽連接的人仍然可以獲得新的 JWT。
  • 創建 JWT 黑名單。 根據到期時間,當客戶端刪除其令牌時,它可能仍然有效一段時間。 如果令牌生命周期很短,這可能不是問題,但如果您仍然希望令牌立即失效,則可以創建令牌黑名單。 當后端收到注銷請求時,從請求中取出 JWT 並將其存儲在內存數據庫中。 對於每個經過身份驗證的請求,您都需要檢查內存數據庫以查看令牌是否已失效。 為了保持搜索空間較小,您可以從黑名單中刪除已經過期的令牌。

我不知道什么是最佳實踐,但在我見過內部結構的系統中,有一個中央身份驗證管理器知道所有當前有效的身份驗證令牌,因此注銷只需從有效令牌集合中刪除令牌.

因此,下次向身份驗證管理器詢問令牌是否有效時,它將以“否”作為響應。

1)只需從客戶端刪除令牌

2)創建令牌黑名單

3)保持令牌到期時間短並經常輪換它們

請查看 Invalidating JSON Web Tokens Invalidating JSON Web Tokens

如果令牌保存在 localStorage 中:

    localStorage.removeItem('token');

在服務器中,如果 jwt 在會話中:

     req.session = null;

如果你想清除你設置的cookie

  req.session.destroy((err) => { //express-session
    res.clearCookie("qid"); //qid is the cookie name
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM