亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot集成?JWT實(shí)現(xiàn)用戶登錄認(rèn)證的項(xiàng)目實(shí)踐

 更新時(shí)間:2023年08月06日 11:31:42   作者:Java后端何哥  
當(dāng)今前后端分離時(shí)代,基于Token的會(huì)話保持機(jī)制比傳統(tǒng)的Session/Cookie機(jī)制更加方便,本文主要介紹了SpringBoot集成?JWT實(shí)現(xiàn)用戶登錄認(rèn)證的項(xiàng)目實(shí)踐,感興趣的可以了解一下

前言:當(dāng)今前后端分離時(shí)代,基于Token的會(huì)話保持機(jī)制比傳統(tǒng)的Session/Cookie機(jī)制更加方便,下面我會(huì)介紹SpringBoot快速集成JWT庫(kù)java-jwt以完成用戶登錄認(rèn)證。

一、JWT 簡(jiǎn)介

1.1、 JWT的概念

JWT 是 JSON Web Token 的縮寫,是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于  JSON  的開放標(biāo)準(zhǔn)((RFC 7519)。定義了一種簡(jiǎn)潔的,自包含的方法用于通信雙方之間以  JSON  對(duì)象的形式安全的傳遞信息。因?yàn)閿?shù)字簽名的存在,這些信息是可信的,JWT 可以使用  HMAC  算法或者是  RSA  的公私秘鑰對(duì)進(jìn)行簽名。

1.2、JWT請(qǐng)求流程

JWT 請(qǐng)求流程

  • 用戶使用賬號(hào)和密碼發(fā)起 POST 請(qǐng)求;
  • 服務(wù)器使用私鑰創(chuàng)建一個(gè) JWT;
  • 服務(wù)器返回這個(gè) JWT 給瀏覽器;
  • 瀏覽器將該 JWT 串在請(qǐng)求頭中像服務(wù)器發(fā)送請(qǐng)求;
  • 服務(wù)器驗(yàn)證該 JWT;
  • 返回響應(yīng)的資源給瀏覽器。

1.3、JWT 的主要應(yīng)用場(chǎng)景

身份認(rèn)證在這種場(chǎng)景下,一旦用戶完成了登錄,在接下來(lái)的每個(gè)請(qǐng)求中包含 JWT,可以用來(lái)驗(yàn)證用戶身份以及對(duì)路由,服務(wù)和資源的訪問權(quán)限進(jìn)行驗(yàn)證。由于它的開銷非常小,可以輕松的在不同域名的系統(tǒng)中傳遞,所有目前在單點(diǎn)登錄(SSO)中比較廣泛的使用了該技術(shù)。 信息交換在通信的雙方之間使用 JWT 對(duì)數(shù)據(jù)進(jìn)行編碼是一種非常安全的方式,由于它的信息是經(jīng)過簽名的,可以確保發(fā)送者發(fā)送的信息是沒有經(jīng)過偽造的。

1.4、JWT 數(shù)據(jù)結(jié)構(gòu)

JWT 是由三段信息構(gòu)成的,將這三段信息文本用  .  連接一起就構(gòu)成了 JWT 字符串。JWT 的三個(gè)部分依次為頭部:Header,負(fù)載:Payload 和簽名:Signature。

①Header:

Header 部分是一個(gè) JSON 對(duì)象,描述 JWT 的元數(shù)據(jù),通常是下面的樣子。

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

上面代碼中, alg  屬性表示簽名的算法(algorithm),默認(rèn)是 HMAC SHA256(寫成 HS256); typ  屬性表示這個(gè)令牌(token)的類型(type),JWT 令牌統(tǒng)一寫為  JWT 。

最后,將上面的 JSON 對(duì)象使用 Base64URL 算法轉(zhuǎn)成字符串。

②Payload:

Payload 部分也是一個(gè) JSON 對(duì)象,用來(lái)存放實(shí)際需要傳遞的有效信息。有效信息包含三個(gè)部分:

  • 標(biāo)準(zhǔn)中注冊(cè)的聲明
  • 公共的聲明
  • 私有的聲明

標(biāo)準(zhǔn)中注冊(cè)的聲明 (建議但不強(qiáng)制使用) :

  • iss (issuer):簽發(fā)人
  • exp (expiration time):過期時(shí)間,必須要大于簽發(fā)時(shí)間
  • sub (subject):主題
  • aud (audience):受眾
  • nbf (Not Before):生效時(shí)間
  • iat (Issued At):簽發(fā)時(shí)間
  • jti (JWT ID):編號(hào),JWT 的唯一身份標(biāo)識(shí),主要用來(lái)作為一次性  token ,從而回避重放攻擊。

公共的聲明 :公共的聲明可以添加任何的信息,一般添加用戶的相關(guān)信息或其他業(yè)務(wù)需要的必要信息。但不建議添加敏感信息,因?yàn)樵摬糠衷诳蛻舳丝山饷堋?/p>

私有的聲明 :私有聲明是提供者和消費(fèi)者所共同定義的聲明,一般不建議存放敏感信息,因?yàn)?nbsp; base64  是對(duì)稱解碼的,意味著該部分信息可以歸類為明文信息。這個(gè) JSON 對(duì)象也要使用 Base64URL 算法轉(zhuǎn)成字符串。

③Signature:

Signature 部分是對(duì)前兩部分的簽名,防止數(shù)據(jù)篡改。

首先,需要指定一個(gè)密鑰(secret)。這個(gè)密鑰只有服務(wù)器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認(rèn)是 HMAC SHA256),按照下面的公式產(chǎn)生簽名。

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

算出簽名以后,把 Header、Payload、Signature 三個(gè)部分拼成一個(gè)字符串,每個(gè)部分之間用"點(diǎn)"( . )分隔,就可以返回給用戶。

Base64URL

前面提到,Header 和 Payload 串型化的算法是 Base64URL。這個(gè)算法跟 Base64 算法基本類似,但有一些小的不同。

JWT 作為一個(gè)令牌(token),有些場(chǎng)合可能會(huì)放到 URL(比如  api.example.com/?token=xxx )。Base64 有三個(gè)字符  + 、  /  和  = ,在 URL 里面有特殊含義,所以要被替換掉: =  被省略、 +  替換成  - , /  替換成  _  。這就是 Base64URL 算法。

1.5、JWT 的使用方式

客戶端收到服務(wù)器返回的 JWT 之后需要在本地做保存。此后,客戶端每次與服務(wù)器通信,都要帶上這個(gè) JWT。一般的的做法是放在 HTTP 請(qǐng)求的頭信息  Authorization  字段里面。

Authorization: Bearer <token>

這樣每個(gè)請(qǐng)求中,服務(wù)端就可以在請(qǐng)求頭中拿到 JWT  進(jìn)行解析與認(rèn)證。

1.6、JWT 的特性

  • JWT 默認(rèn)是不加密,但也是可以加密的。生成原始 Token 以后,可以用密鑰再加密一次。
  • JWT 不加密的情況下,不能將秘密數(shù)據(jù)寫入 JWT。
  • JWT 不僅可以用于認(rèn)證,也可以用于交換信息。有效使用 JWT,可以降低服務(wù)器查詢數(shù)據(jù)庫(kù)的次數(shù)。
  • JWT 的最大缺點(diǎn)是,由于服務(wù)器不保存 session 狀態(tài),因此無(wú)法在使用過程中廢止某個(gè) token,或者更改 token 的權(quán)限。也就是說,一旦 JWT 簽發(fā)了,在到期之前就會(huì)始終有效,除非服務(wù)器部署額外的邏輯。
  • JWT 本身包含了認(rèn)證信息,一旦泄露,任何人都可以獲得該令牌的所有權(quán)限。為了減少盜用,JWT 的有效期應(yīng)該設(shè)置得比較短。對(duì)于一些比較重要的權(quán)限,使用時(shí)應(yīng)該再次對(duì)用戶進(jìn)行認(rèn)證。
  • 為了減少盜用,JWT 不應(yīng)該使用 HTTP 協(xié)議明碼傳輸,要使用 HTTPS 協(xié)議傳輸。

二、SpringBoot整合JWT

新建一個(gè)spring boot項(xiàng)目spring-boot-jwt,按照下面步驟操作。

2.1、pom.xml引入jar包

<!-- 引入jwt-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.8.2</version>
</dependency>

順便貼一下下面要用到的User類:

package com.hs.demo.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel
public class User
{
    //實(shí)體類中,Integer類型的屬性加@ApiModelProperty時(shí),必須要給example參數(shù)賦值,且值必須為數(shù)字類型。
    @ApiModelProperty(value = "用戶id",example = "1")
    private Integer id;
    @ApiModelProperty(value = "用戶名")
    private String userName;
    @ApiModelProperty(value = "用戶密碼")
    private String password;
    //getter/setter用@Data注解自動(dòng)生成
}

2.2、新建Jwt工具類

Jwt工具類進(jìn)行token的生成和認(rèn)證,工具類代碼如下:

package com.hs.demo.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.hs.demo.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * @description: Jwt工具類,生成JWT和認(rèn)證
 * @author: heshi
 */
public class JwtUtil {
    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
    /**
     * 密鑰
     */
    private static final String SECRET = "my_secret";
    /**
     * 過期時(shí)間
     **/
    private static final long EXPIRATION = 1800L;//單位為秒
    /**
     * 生成用戶token,設(shè)置token超時(shí)時(shí)間
     */
    public static String createToken(User user) {
        //過期時(shí)間
        Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");
        String token = JWT.create()
                .withHeader(map)// 添加頭部
                //可以將基本信息放到claims中
                .withClaim("id", user.getId())//userId
                .withClaim("userName", user.getUserName())//userName
                .withClaim("password", user.getPassword())//password
                .withExpiresAt(expireDate) //超時(shí)設(shè)置,設(shè)置過期的日期
                .withIssuedAt(new Date()) //簽發(fā)時(shí)間
                .sign(Algorithm.HMAC256(SECRET)); //SECRET加密
        return token;
    }
    /**
     * 校驗(yàn)token并解析token
     */
    public static Map<String, Claim> verifyToken(String token) {
        DecodedJWT jwt = null;
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
            jwt = verifier.verify(token);
            //decodedJWT.getClaim("屬性").asString()  獲取負(fù)載中的屬性值
        } catch (Exception e) {
            logger.error(e.getMessage());
            logger.error("token解碼異常");
            //解碼異常則拋出異常
            return null;
        }
        return jwt.getClaims();
    }
}

2.3、添加JWT過濾器

JWT過濾器中進(jìn)行token的校驗(yàn)和判斷,token不合法直接返回,合法則解密數(shù)據(jù)并把數(shù)據(jù)放到request中供后續(xù)使用。

為了使過濾器生效,需要在啟動(dòng)類添加注解@ServletComponentScan(basePackages = "com.hs.demo.jwt")。

JWT過濾器代碼如下:

package com.hs.demo.jwt;
import com.auth0.jwt.interfaces.Claim;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
/**
 * JWT過濾器,攔截 /secure的請(qǐng)求
 */
@Slf4j
@WebFilter(filterName = "JwtFilter", urlPatterns = "/secure/*")
public class JwtFilter implements Filter
 {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) req;
        final HttpServletResponse response = (HttpServletResponse) res;
        response.setCharacterEncoding("UTF-8");
        //獲取 header里的token
        final String token = request.getHeader("authorization");
        if ("OPTIONS".equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            chain.doFilter(request, response);
        }
        // Except OPTIONS, other request should be checked by JWT
        else {
            if (token == null) {
                response.getWriter().write("沒有token!");
                return;
            }
            Map<String, Claim> userData = JwtUtil.verifyToken(token);
            if (userData == null) {
                response.getWriter().write("token不合法!");
                return;
            }
            Integer id = userData.get("id").asInt();
            String userName = userData.get("userName").asString();
            String password= userData.get("password").asString();
            //攔截器 拿到用戶信息,放到request中
            request.setAttribute("id", id);
            request.setAttribute("userName", userName);
            request.setAttribute("password", password);
            chain.doFilter(req, res);
        }
    }
    @Override
    public void destroy() {
    }
}

2.4、添加LoginController

LoginController進(jìn)行登錄操作,登錄成功后生產(chǎn)token并返回。

LoginController代碼如下:

package com.hs.demo.jwt;
import com.hs.demo.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
 * 登錄Controller
 */
@Slf4j
@RestController
public class LoginController
 {
    static Map<Integer, User> userMap = new HashMap<>();
    static {
        //模擬數(shù)據(jù)庫(kù)
        User user1 = new User(1,"張三","123456");
        userMap.put(1, user1);
        User user2 = new User(2,"李四","123123");
        userMap.put(2, user2);
    }
    /**
     * 模擬用戶 登錄
     */
    @RequestMapping("/login")
    public String login(User user)
    {
        for (User dbUser : userMap.values()) {
            if (dbUser.getUserName().equals(user.getUserName()) && dbUser.getPassword().equals(user.getPassword())) {
                log.info("登錄成功!生成token!");
                String token = JwtUtil.createToken(dbUser);
                return token;
            }
        }
        return "";
    }
}

2.5、添加SecureController

SecureController中的請(qǐng)求會(huì)被JWT過濾器攔截,合法后才能訪問。

SecureController代碼如下:

package com.hs.demo.jwt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
 * 需要登錄后攜帶JWT才能訪問
 */
@Slf4j
@RestController
public class SecureController 
{
    /**
     * 查詢 用戶信息,登錄后攜帶JWT才能訪問
     */
    @RequestMapping("/secure/getUserInfo")
    public String login(HttpServletRequest request) {
        Integer id = (Integer) request.getAttribute("id");
        String userName = request.getAttribute("userName").toString();
        String password= request.getAttribute("password").toString();
        return "當(dāng)前用戶信息id=" + id + ",userName=" + userName+ ",password=" + password;
    }
}

三、接口測(cè)試

測(cè)試分兩步,首先訪問登錄接口,登錄成功后獲取token,然后拿著token在訪問查詢用戶信息接口。

3.1、訪問登錄接口

打開PostMan,訪問http://localhost:8080/login?userName=zhangsan&password=123456,登錄成功后接口返回token,請(qǐng)求成功截圖如下:

3.2、訪問用戶信息接口

打開PostMan,訪問http://localhost:8080/secure/getUserInfo,header里需要攜帶token,請(qǐng)求成功截圖如下:

四、Token 認(rèn)證的優(yōu)勢(shì)

相比于 Session 認(rèn)證的方式來(lái)說,使用 token 進(jìn)行身份認(rèn)證主要有下面三個(gè)優(yōu)勢(shì):

4.1、無(wú)狀態(tài)

JWT實(shí)現(xiàn)的Token 自身包含了身份驗(yàn)證所需要的所有信息,使得我們的服務(wù)器不需要存儲(chǔ) Session 信息,這顯然增加了系統(tǒng)的可用性和伸縮性,大大減輕了服務(wù)端的壓力。但是,也正是由于 token 的無(wú)狀態(tài),也導(dǎo)致了它最大的缺點(diǎn):當(dāng)后端在token 有效期內(nèi)廢棄一個(gè) token 或者更改它的權(quán)限的話,不會(huì)立即生效,一般需要等到有效期過后才可以。另外,當(dāng)用戶 Logout 的話,token 也還有效。除非,我們?cè)诤蠖嗽黾宇~外的處理邏輯。

4.2、有效避免了CSRF 攻擊

CSRF(Cross Site Request Forgery) 一般被翻譯為 跨站請(qǐng)求偽造,屬于網(wǎng)絡(luò)攻擊領(lǐng)域范圍。相比于 SQL 腳本注入、XSS等等安全攻擊方式,CSRF 的知名度并沒有它們高。但是,它的確是每個(gè)系統(tǒng)都要考慮的安全隱患,就連技術(shù)帝國(guó) Google 的 Gmail 在早些年也被曝出過存在  CSRF 漏洞,這給 Gmail 的用戶造成了很大的損失。

那么究竟什么是  跨站請(qǐng)求偽造 呢?說簡(jiǎn)單用你的身份去發(fā)送一些對(duì)你不友好的請(qǐng)求。舉個(gè)簡(jiǎn)單的例子:

小壯登錄了某網(wǎng)上銀行,他來(lái)到了網(wǎng)上銀行的帖子區(qū),看到一個(gè)帖子下面有一個(gè)鏈接寫著“科學(xué)理財(cái),年盈利率過萬(wàn)”,小壯好奇的點(diǎn)開了這個(gè)鏈接,結(jié)果發(fā)現(xiàn)自己的賬戶少了10000元。這是這么回事呢?原來(lái)黑客在鏈接中藏了一個(gè)請(qǐng)求,這個(gè)請(qǐng)求直接利用小壯的身份給銀行發(fā)送了一個(gè)轉(zhuǎn)賬請(qǐng)求,也就是通過你的 Cookie 向銀行發(fā)出請(qǐng)求。

<a src=http://www.mybank.com/Transfer?bankId=11&money=10000>科學(xué)理財(cái),年盈利率過萬(wàn)</>

導(dǎo)致這個(gè)問題很大的原因就是:Session 認(rèn)證中 Cookie 中的 session_id 是由瀏覽器發(fā)送到服務(wù)端的,借助這個(gè)特性,攻擊者就可以通過讓用戶誤點(diǎn)攻擊鏈接,達(dá)到攻擊效果。

那為什么 token 不會(huì)存在這種問題呢?

我是這樣理解的:一般情況下我們使用 JWT 的話,在我們登錄成功獲得 token 之后,一般會(huì)選擇存放在  local storage 中。然后我們?cè)谇岸送ㄟ^某些方式會(huì)給每個(gè)發(fā)到后端的請(qǐng)求加上這個(gè) token,這樣就不會(huì)出現(xiàn) CSRF 漏洞的問題。因?yàn)?,即使有個(gè)你點(diǎn)擊了非法鏈接發(fā)送了請(qǐng)求到服務(wù)端,這個(gè)非法請(qǐng)求是不會(huì)攜帶 token 的,所以這個(gè)請(qǐng)求將是非法的。

但是這樣會(huì)存在  XSS 攻擊中被盜的風(fēng)險(xiǎn),為了避免 XSS 攻擊,你可以選擇將 token 存儲(chǔ)在標(biāo)記為 httpOnly   的cookie 中。但是,這樣又導(dǎo)致了你必須自己提供CSRF保護(hù)。

具體采用上面哪兩種方式存儲(chǔ) token 呢,大部分情況下存放在  local storage 下都是最好的選擇,某些情況下可能需要存放在標(biāo)記為 httpOnly  的cookie 中會(huì)更好。

4.3、適合移動(dòng)端應(yīng)用

使用 Session 進(jìn)行身份認(rèn)證的話,需要保存一份信息在服務(wù)器端,而且這種方式會(huì)依賴到 Cookie(需要 Cookie 保存 SessionId),所以不適合移動(dòng)端。

但是,使用 token 進(jìn)行身份認(rèn)證就不會(huì)存在這種問題,因?yàn)橹灰?token 可以被客戶端存儲(chǔ)就能夠使用,而且 token 還可以跨語(yǔ)言使用。

4.4、單點(diǎn)登錄友好

使用 Session 進(jìn)行身份認(rèn)證的話,實(shí)現(xiàn)單點(diǎn)登錄,需要我們把用戶的 Session 信息保存在一臺(tái)電腦上,并且還會(huì)遇到常見的 Cookie 跨域的問題。但是,使用 token 進(jìn)行認(rèn)證的話, token 被保存在客戶端,不會(huì)存在這些問題。

五、Token 認(rèn)證常見問題以及解決辦法

5.1、注銷登錄等場(chǎng)景下 token 還有效

  • 與之類似的具體相關(guān)場(chǎng)景有:
  • 退出登錄;
  • 修改密碼;
  • 服務(wù)端修改了某個(gè)用戶具有的權(quán)限或者角色;
  • 用戶的帳戶被刪除/暫停。
  • 用戶由管理員注銷;

這個(gè)問題不存在于 Session  認(rèn)證方式中,因?yàn)樵? Session  認(rèn)證方式中,遇到這種情況的話服務(wù)端刪除對(duì)應(yīng)的 Session 記錄即可。但是,使用 token 認(rèn)證的方式就不好解決了。我們也說過了,token 一旦派發(fā)出去,如果后端不增加其他邏輯的話,它在失效之前都是有效的。那么,我們?nèi)绾谓鉀Q這個(gè)問題呢?查閱了很多資料,總結(jié)了下面幾種方案:

  • 將 token 存入內(nèi)存數(shù)據(jù)庫(kù):將 token 存入 DB 中,redis 內(nèi)存數(shù)據(jù)庫(kù)在這里是是不錯(cuò)的選擇。如果需要讓某個(gè) token 失效就直接從 redis 中刪除這個(gè) token 即可。但是,這樣會(huì)導(dǎo)致每次使用 token 發(fā)送請(qǐng)求都要先從 DB 中查詢 token 是否存在的步驟,而且違背了 JWT 的無(wú)狀態(tài)原則。
  • 黑名單機(jī)制:和上面的方式類似,使用內(nèi)存數(shù)據(jù)庫(kù)比如 redis 維護(hù)一個(gè)黑名單,如果想讓某個(gè) token 失效的話就直接將這個(gè) token 加入到 黑名單 即可。然后,每次使用 token 進(jìn)行請(qǐng)求的話都會(huì)先判斷這個(gè) token 是否存在于黑名單中。
  • 修改密鑰 (Secret) : 我們?yōu)槊總€(gè)用戶都創(chuàng)建一個(gè)專屬密鑰,如果我們想讓某個(gè) token 失效,我們直接修改對(duì)應(yīng)用戶的密鑰即可。但是,這樣相比于前兩種引入內(nèi)存數(shù)據(jù)庫(kù)帶來(lái)了危害更大,比如:1??如果服務(wù)是分布式的,則每次發(fā)出新的 token 時(shí)都必須在多臺(tái)機(jī)器同步密鑰。為此,你需要將必須將機(jī)密存儲(chǔ)在數(shù)據(jù)庫(kù)或其他外部服務(wù)中,這樣和 Session 認(rèn)證就沒太大區(qū)別了。2??  如果用戶同時(shí)在兩個(gè)瀏覽器打開系統(tǒng),或者在手機(jī)端也打開了系統(tǒng),如果它從一個(gè)地方將賬號(hào)退出,那么其他地方都要重新進(jìn)行登錄,這是不可取的。
  • 保持令牌的有效期限短并經(jīng)常輪換 :很簡(jiǎn)單的一種方式。但是,會(huì)導(dǎo)致用戶登錄狀態(tài)不會(huì)被持久記錄,而且需要用戶經(jīng)常登錄。

對(duì)于修改密碼后 token 還有效問題的解決還是比較容易的,說一種我覺得比較好的方式:使用用戶的密碼的哈希值對(duì) token 進(jìn)行簽名。因此,如果密碼更改,則任何先前的令牌將自動(dòng)無(wú)法驗(yàn)證。

5.2、token 的續(xù)簽問題

token 有效期一般都建議設(shè)置的不太長(zhǎng),那么 token 過期后如何認(rèn)證,如何實(shí)現(xiàn)動(dòng)態(tài)刷新 token,避免用戶經(jīng)常需要重新登錄?

我們先來(lái)看看在 Session 認(rèn)證中一般的做法:假如 session 的有效期30分鐘,如果 30 分鐘內(nèi)用戶有訪問,就把 session 有效期被延長(zhǎng)30分鐘。

  • 類似于 Session 認(rèn)證中的做法:這種方案滿足于大部分場(chǎng)景。假設(shè)服務(wù)端給的 token 有效期設(shè)置為30分鐘,服務(wù)端每次進(jìn)行校驗(yàn)時(shí),如果發(fā)現(xiàn) token 的有效期馬上快過期了,服務(wù)端就重新生成 token 給客戶端??蛻舳嗣看握?qǐng)求都檢查新舊token,如果不一致,則更新本地的token。這種做法的問題是僅僅在快過期的時(shí)候請(qǐng)求才會(huì)更新 token ,對(duì)客戶端不是很友好。
  • 每次請(qǐng)求都返回新 token :這種方案的的思路很簡(jiǎn)單,但是,很明顯,開銷會(huì)比較大。
  • token 有效期設(shè)置到半夜 :這種方案是一種折衷的方案,保證了大部分用戶白天可以正常登錄,適用于對(duì)安全性要求不高的系統(tǒng)。
  • 用戶登錄返回兩個(gè) token :第一個(gè)是 acessToken ,它的過期時(shí)間 token 本身的過期時(shí)間比如半個(gè)小時(shí),另外一個(gè)是 refreshToken 它的過期時(shí)間更長(zhǎng)一點(diǎn)比如為1天??蛻舳说卿浐?,將 accessToken和refreshToken 保存在本地,每次訪問將 accessToken 傳給服務(wù)端。服務(wù)端校驗(yàn) accessToken 的有效性,如果過期的話,就將 refreshToken 傳給服務(wù)端。如果有效,服務(wù)端就生成新的 accessToken 給客戶端。否則,客戶端就重新登錄即可。該方案的不足是:1??需要客戶端來(lái)配合;2??用戶注銷的時(shí)候需要同時(shí)保證兩個(gè)  token 都無(wú)效;3??重新請(qǐng)求獲取 token  的過程中會(huì)有短暫 token 不可用的情況(可以通過在客戶端設(shè)置定時(shí)器,當(dāng)accessToken 快過期的時(shí)候,提前去通過 refreshToken 獲取新的accessToken)。

總結(jié)

JWT 最適合的場(chǎng)景是不需要服務(wù)端保存用戶狀態(tài)的場(chǎng)景,如果考慮到 token 注銷和 token 續(xù)簽的場(chǎng)景話,沒有特別好的解決方案,大部分解決方案都給 token 加上了狀態(tài),這就有點(diǎn)類似 Session 認(rèn)證了。

參考鏈接:

基于Token的WEB后臺(tái)認(rèn)證機(jī)制

SpringBoot攔截器Interceptor

Spring Boot實(shí)戰(zhàn):攔截器與過濾器

chain.doFilter(request,response)含義

到此這篇關(guān)于SpringBoot集成 JWT實(shí)現(xiàn)用戶登錄認(rèn)證的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)SpringBoot JWT用戶登錄認(rèn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論