欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java設置token有效期的5個應用場景(雙token實現)

 更新時間:2024年04月14日 10:52:49   作者:夏詩曼CharmaineXia  
Token最常見的應用場景之一就是身份驗證,本文主要介紹了Java設置token有效期的5個應用場景(雙token實現),具有一定的參考價值,感興趣的可以來了解一下

token的簡介和生成校驗已經在前面分享過,有需要的小伙伴可以先進行回顧。

傳送門:token介紹,以及如何生成以及校驗token

前言:

Token最常見的應用場景之一就是身份驗證。在傳統(tǒng)的身份驗證方式中,用戶需要輸入用戶名和密碼才能登錄系統(tǒng),這種方式容易被破解和盜用。而使用token方式進行身份驗證,可以有效防止用戶身份信息被盜用。

當用戶進行身份驗證后,服務器會生成一個token并將其返回給客戶端,客戶端可以使用token來訪問受保護的資源,而不需要重新輸入用戶名和密碼。這種方式不僅可以提高安全性,還可以提高用戶體驗。

在這里插入圖片描述

場景一:網吧計時

場景分析:

嚴格規(guī)定登陸時長,超時則跳轉登陸頁面,必須重新輸入密碼才能繼續(xù)使用

對token的要求:

登陸成功后,服務器生成的token需要攜帶時間戳(token時間戳=當前時間+有效時長),后臺定制一個有效期時長,在網吧計時收費場景中有效范圍就是可以上機的時長。

每次請求都要校驗token是否過期( 時間戳是否小于現在時間)

token也只用存在瀏覽器緩存即可,減少服務器端的存儲壓力。

實操:

javaWebToken為例:

    <!-- 引入jwt -->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.8.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.8.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-web</artifactId>
        <version>1.8.0</version>
    </dependency>
     /**
     * @description: 生成token
     * @param:  userInfo 用戶手機號和用戶Id
     * @return: java.lang.String 返回token
     **/
    public static String getToken(String userPhone) {
        try{
            //從當前時間算起,再加上有效時長30分鐘
            Date date = new Date(System.currentTimeMillis() + 30*60*1000);
            //用秘鑰生成簽名
            Algorithm algorithm = Algorithm.HMAC256('P1ooisyGFJhgzrctMOofvaHLuiNFOmktedw');
            //默認頭部+載荷(手機號/id)+過期日期+簽名=jwt
            String jwtToken= JWT.create()
                    .withClaim("userPhone", userPhone)
                    .withClaim("userId", "xxxxxxx")
                    .withExpiresAt(date)
                    .sign(algorithm);
            return jwtToken;
        }catch (Exception e){
            log.error("用戶{}的token生成異常:{}",userPhone,e);
            return null;
        }
    }

驗證token有效期:

    // 判斷 token 是否過期
    public static String isExpire(String token) {
        DecodedJWT jwt = JWT.decode(token);//解碼token
        // 如果token的有效期小于當前時間,則表示已過期,為true
        boolean isExpire = jwt.getExpiresAt().getTime() < System.currentTimeMillis();
        if(isExpire){
           return jwt.getClaim("userPhone").asString();//獲取token攜帶的數據
        }else{
            return null;
        }
    }

攔截請求,開始驗證token

public class JwtToken implements AuthenticationToken {
    /**
     * JWT的字符token
     */
    private String token;

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

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}


@Component
public class ShiroRealm extends AuthorizingRealm {

    /**
     * @Title: doGetAuthenticationInfo
     * @description: 校驗token是否正確
     * @param:  auth
     * @return: org.apache.shiro.authc.AuthenticationInfo
     **/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        try{
            String token = (String) auth.getCredentials();
            String userPhone=JwtUtil.isExpire(token);
            return new SimpleAuthenticationInfo(userPhone, token, getName());
        }catch(Exception e){
            throw new AuthenticationException("驗證token失敗");
        }
    }
}

總結:

優(yōu)點:

  • 嚴格規(guī)定登陸時長,簡單來說就是安全性高。
  • 代碼邏輯簡單,token過期了就重定向到登陸頁面,不需要做延時等處理。

缺點:

  • 時間一到就跳轉到登陸頁面,對用戶來說非常突然,某種程度上說用戶體驗非常不好。
  • 用戶有可能停留在頁面不做任何操作,因此客戶端必須定期主動的給服務器發(fā)請求(或使用消息隊列),以便及時發(fā)現校驗token過期。

場景二: Esxi系統(tǒng)頁面、jumpServer

場景分析:

Esxi系統(tǒng)頁面、jumpServer的web終端頁面等,超過一段時間內不操作(例如0.5小時)自動退出登錄,再想繼續(xù)操作需要重新登錄。

對token的要求:

和場景一類似,區(qū)別是增加了一個判斷:每次請求都會判斷token是否快要過期(例如設置token還有10分鐘過期)。

如果將要過期,則重置token有效期(服務器發(fā)個新token給客戶端);如果已經過期,需要重新登錄。

實操:

重新生成一個新的token,前端收到新的token后把舊token丟棄(前端代碼略)

總結:

優(yōu)點:

  • 用戶一直在使用頁面,token就會被一直重置,對活躍用戶友好。
  • token有效期短,人離開一段時間就無法繼續(xù)使用軟件,這個設計安全性較高。
  • 用戶在token過期后再次操作才會要求再次登陸,也就是說,不需要客戶端時時給服務器發(fā)消息驗證token是否有效,減少網絡開銷。

缺點:

  • 如果有效期設計的短,用戶操作也不頻繁的情況下,會導致用戶頻繁登陸,體驗較差。合理設置有效期非常重要。

場景三:微信、支付寶等app

場景分析:

微信、支付寶等手機app,我們一旦安裝并登陸以后,除了涉及資金或信息安全的場景需要輸入一些密碼,基本上我們打開app就能用,不會讓我們重復登陸。

對token的要求:

token有效期設置的很長(3個月、6個月、一年等)

每次請求還是會驗證token有效期,但如果token過期,則發(fā)消息給客戶端。

客戶端收到消息以后,給服務器發(fā)送重置token的請求。

總結:

適用于安全性有保證的場景(例如手機App:手機有鎖屏碼等安全機制,涉及到金錢等重要信息還有其他驗證方式)

優(yōu)點:

  • 用戶只需要登陸一次,用戶體驗很好

缺點:

  • 客戶端可以發(fā)送重置token的請求,故token一直有效,手機鎖屏被破解,任何人都能使用,也是個安全隱患。
  • 只用登陸一次,用戶很有可能忘記密碼,想要避免用戶體驗差,必須綁定手機號,支持驗證碼登陸。

場景四:語雀等pc應用程序(雙token)

場景分析:

場景四和場景二類似,區(qū)別是用戶長時間不使用的情況下才會被強制用戶登錄。

例如“語雀”等應用程序,長時間不使用是會被要求重新登錄的。

我們每個人都安裝過一些使用頻率不高的軟件,這些軟件在產品設計時就決定了用戶的使用頻率和周期,那他們是如何界定“一直在使用的活躍用戶”和“長時間不使用的非活躍用戶”呢?

還是靠設置token有效期,有效期設置的長一些,例如3個月或6個月不使用才算非活躍用戶。

但是如何沿用場景二的token要求,設置有效期長的token,會留下很大的安全隱患:token一旦被黑客截獲后長時間可以被使用,還不會被服務器察覺。

解決方案:雙token

什么是雙token?以現有的短token的基礎上再增加一個長token,形成兩個token校驗有效期的模式。

短token可以防止被截獲后無休止使用,所以還要使用有效期短的token用來驗證有效期。

而新增加的長token,它的有效期用來區(qū)分“活躍用戶”和“非活躍用戶”,用來實現短token過期后,活躍用戶系統(tǒng)自動給重置token,非活躍用戶需要重新登錄。

對token的要求:

用戶登錄成功后,服務器生成一短一長兩個token返回給客戶端,客戶端每次請求服務器攜帶的是短token。

如果服務器發(fā)現短token過期,則通知給客戶端,此時客戶端攜帶長token給服務器校驗。

如果長token未過期,表示用戶為“活躍用戶”,服務器重置短token和長token發(fā)給客戶端。
如果長token過期,表示用戶為“非活躍用戶”,用戶需要重新登錄。

在這里插入圖片描述

總結:

優(yōu)點:

  • 幫助使用頻率不高的軟件區(qū)別“活躍用戶”和“非活躍用戶”,提升“活躍用戶”的體驗,保證“非活躍用戶”的信息安全

缺點:

  • 刷新token期間,原有的token不能用,在并發(fā)情況下會導致其他問題。

場景五:提升響應速度(redis)

場景分析:

場景一到四的共同點:①token內置了時間戳,②服務器端不存儲token
場景五是對以上以上四個場景在驗證速度上的優(yōu)化,親測使用redis可以提高2-4倍的驗證速度。

對token的要求:

token內不設置時間戳,而是將token存在redis中(例如:鍵為token,值為用戶信息),并設置token存在redis中的保存時間。

客戶端仍然保存著token,每次請求都攜帶token。服務器接到請求,把token當做鍵去redis中拿數據:

如果能拿出數據,則說名token沒過期,如果拿不到,則說明token過期了。

想要重置token有效期,直接根據鍵重置數據在redis中的有效期。

在這里插入圖片描述

實操:

        <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>3.0.0</version>
        </dependency>

redis工具類

/**
 * Redis工具類
 */
@Component
public final class RedisUtil<V> {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 普通緩存獲取
     * @param key 鍵
     * @return 值
     */
    public String get(String key) {
        return key == null ? null : (String) redisTemplate.opsForValue().get(key);
    }

    /**
     * 根據key 獲取過期時間
     * @param key 鍵 不能為null
     * @return 時間(秒) 返回0代表為永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
        /**
     * HashSet 并設置時間
     * @param key  鍵
     * @param map  對應多個鍵值
     * @param time 時間(秒)
     * @return true成功 false失敗
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

攔截請求,開始驗證token

public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private RedisUtil redisUtil;
 
   /**
     * @Title: doGetAuthenticationInfo
     * @description: 校驗token是否正確
     * @param:  auth
     * @return: org.apache.shiro.authc.AuthenticationInfo
     **/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {

        //不使用token驗證來校驗token是否可用,改成把token存redis中,在redis中設置數據有效期
        String token = (String) auth.getCredentials();
        if (StringUtils.isEmpty(token)) {
            throw new AuthenticationException(Constant.TOKEN_EXPIRED);
        }
        //判斷是否能從redis中用token拿到過期時間
        try{
            Long times=redisUtil.getExpire(token);
            //如果還有0.5小時過期,就刷新token在redis中的有效時間(有效期設置為2小時)
            if(1800>times){
                redisUtil.expire(token,7200);
            }
            String userId =redisUtil.get(token);  //獲取key中的用戶id
            return new SimpleAuthenticationInfo(userId, token, getName());
        }catch(Exception e){
            throw new AuthenticationException("驗證token失敗");
        }
    }
}

驗證redis校驗速度

       //token時間戳校驗
       long startTime=System.currentTimeMillis();   //獲取開始時間

       for(int i=0;i<99;i++){
           String userPhone = JwtUtil.isExpire(token);
       }

       long endTime=System.currentTimeMillis(); //獲取結束時間

       System.out.println("用時間戳方式校驗100次token是否有效: "+(endTime-startTime)+"毫秒");
       //redis校驗
       long startTime=System.currentTimeMillis();   //獲取開始時間

       for(int i=0;i<99;i++){
           if(StringUtils.isEmpty(redisUtil.get(token))){
               return null;
           }
       }
       long endTime=System.currentTimeMillis(); //獲取結束時間

       System.out.println("用redis設置過期時間方式校驗100次token是否有效: "+(endTime-startTime)+"毫秒");

在這里插入圖片描述

總結:

優(yōu)點:

  • 服務器生成了token就直接存到redis中,token是不會被攔截篡改的,因此默認token是正確的,也就減少了驗證token是否正確這一步驟
  • 重置了token,token本身也不會變,減少客戶端處理廢棄token、再存儲新token的邏輯
  • redis的數據存在內存中,IO速度快

缺點:

  • 依賴第三方軟件,需要搭建redis服務器,考慮到redis掛了軟件也不能使用,還要搭建redis集群

思想升華:

每種設置token有效期的方案都有對應的場景,拋開場景談方案是狹隘的。再學習計算機的過程中,我發(fā)現無論是磁盤調度方式、內存存儲方式、raid0-6,所有方式方法的誕生都和當時的場景相關,并且往往在時間和空間上面進行取舍。所以沒有最優(yōu)的方案,只有當下相對合適的方案。

到此這篇關于Java設置token有效期的5個應用場景(雙token實現)的文章就介紹到這了,更多相關Java設置token有效期內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論