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

Java設(shè)置token有效期的5個(gè)應(yīng)用場(chǎng)景(雙token實(shí)現(xiàn))

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

token的簡(jiǎn)介和生成校驗(yàn)已經(jīng)在前面分享過,有需要的小伙伴可以先進(jìn)行回顧。

傳送門:token介紹,以及如何生成以及校驗(yàn)token

前言:

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

當(dāng)用戶進(jìn)行身份驗(yàn)證后,服務(wù)器會(huì)生成一個(gè)token并將其返回給客戶端,客戶端可以使用token來(lái)訪問受保護(hù)的資源,而不需要重新輸入用戶名和密碼。這種方式不僅可以提高安全性,還可以提高用戶體驗(yàn)。

在這里插入圖片描述

場(chǎng)景一:網(wǎng)吧計(jì)時(shí)

場(chǎng)景分析:

嚴(yán)格規(guī)定登陸時(shí)長(zhǎng),超時(shí)則跳轉(zhuǎn)登陸頁(yè)面,必須重新輸入密碼才能繼續(xù)使用

對(duì)token的要求:

登陸成功后,服務(wù)器生成的token需要攜帶時(shí)間戳(token時(shí)間戳=當(dāng)前時(shí)間+有效時(shí)長(zhǎng)),后臺(tái)定制一個(gè)有效期時(shí)長(zhǎng),在網(wǎng)吧計(jì)時(shí)收費(fèi)場(chǎng)景中有效范圍就是可以上機(jī)的時(shí)長(zhǎng)。

每次請(qǐng)求都要校驗(yàn)token是否過期( 時(shí)間戳是否小于現(xiàn)在時(shí)間)

token也只用存在瀏覽器緩存即可,減少服務(wù)器端的存儲(chǔ)壓力。

實(shí)操:

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 用戶手機(jī)號(hào)和用戶Id
     * @return: java.lang.String 返回token
     **/
    public static String getToken(String userPhone) {
        try{
            //從當(dāng)前時(shí)間算起,再加上有效時(shí)長(zhǎng)30分鐘
            Date date = new Date(System.currentTimeMillis() + 30*60*1000);
            //用秘鑰生成簽名
            Algorithm algorithm = Algorithm.HMAC256('P1ooisyGFJhgzrctMOofvaHLuiNFOmktedw');
            //默認(rèn)頭部+載荷(手機(jī)號(hào)/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;
        }
    }

驗(yàn)證token有效期:

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

攔截請(qǐng)求,開始驗(yàn)證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: 校驗(yàn)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("驗(yàn)證token失敗");
        }
    }
}

總結(jié):

優(yōu)點(diǎn):

  • 嚴(yán)格規(guī)定登陸時(shí)長(zhǎng),簡(jiǎn)單來(lái)說就是安全性高。
  • 代碼邏輯簡(jiǎn)單,token過期了就重定向到登陸頁(yè)面,不需要做延時(shí)等處理。

缺點(diǎn):

  • 時(shí)間一到就跳轉(zhuǎn)到登陸頁(yè)面,對(duì)用戶來(lái)說非常突然,某種程度上說用戶體驗(yàn)非常不好。
  • 用戶有可能停留在頁(yè)面不做任何操作,因此客戶端必須定期主動(dòng)的給服務(wù)器發(fā)請(qǐng)求(或使用消息隊(duì)列),以便及時(shí)發(fā)現(xiàn)校驗(yàn)token過期。

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

場(chǎng)景分析:

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

對(duì)token的要求:

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

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

實(shí)操:

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

總結(jié):

優(yōu)點(diǎn):

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

缺點(diǎn):

  • 如果有效期設(shè)計(jì)的短,用戶操作也不頻繁的情況下,會(huì)導(dǎo)致用戶頻繁登陸,體驗(yàn)較差。合理設(shè)置有效期非常重要。

場(chǎng)景三:微信、支付寶等app

場(chǎng)景分析:

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

對(duì)token的要求:

token有效期設(shè)置的很長(zhǎng)(3個(gè)月、6個(gè)月、一年等)

每次請(qǐng)求還是會(huì)驗(yàn)證token有效期,但如果token過期,則發(fā)消息給客戶端。

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

總結(jié):

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

優(yōu)點(diǎn):

  • 用戶只需要登陸一次,用戶體驗(yàn)很好

缺點(diǎn):

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

場(chǎng)景四:語(yǔ)雀等pc應(yīng)用程序(雙token)

場(chǎng)景分析:

場(chǎng)景四和場(chǎng)景二類似,區(qū)別是用戶長(zhǎng)時(shí)間不使用的情況下才會(huì)被強(qiáng)制用戶登錄。

例如“語(yǔ)雀”等應(yīng)用程序,長(zhǎng)時(shí)間不使用是會(huì)被要求重新登錄的。

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

還是靠設(shè)置token有效期,有效期設(shè)置的長(zhǎng)一些,例如3個(gè)月或6個(gè)月不使用才算非活躍用戶。

但是如何沿用場(chǎng)景二的token要求,設(shè)置有效期長(zhǎng)的token,會(huì)留下很大的安全隱患:token一旦被黑客截獲后長(zhǎng)時(shí)間可以被使用,還不會(huì)被服務(wù)器察覺。

解決方案:雙token

什么是雙token?以現(xiàn)有的短token的基礎(chǔ)上再增加一個(gè)長(zhǎng)token,形成兩個(gè)token校驗(yàn)有效期的模式。

短token可以防止被截獲后無(wú)休止使用,所以還要使用有效期短的token用來(lái)驗(yàn)證有效期。

而新增加的長(zhǎng)token,它的有效期用來(lái)區(qū)分“活躍用戶”和“非活躍用戶”,用來(lái)實(shí)現(xiàn)短token過期后,活躍用戶系統(tǒng)自動(dòng)給重置token,非活躍用戶需要重新登錄。

對(duì)token的要求:

用戶登錄成功后,服務(wù)器生成一短一長(zhǎng)兩個(gè)token返回給客戶端,客戶端每次請(qǐng)求服務(wù)器攜帶的是短token。

如果服務(wù)器發(fā)現(xiàn)短token過期,則通知給客戶端,此時(shí)客戶端攜帶長(zhǎng)token給服務(wù)器校驗(yàn)。

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

在這里插入圖片描述

總結(jié):

優(yōu)點(diǎn):

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

缺點(diǎn):

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

場(chǎng)景五:提升響應(yīng)速度(redis)

場(chǎng)景分析:

場(chǎng)景一到四的共同點(diǎn):①token內(nèi)置了時(shí)間戳,②服務(wù)器端不存儲(chǔ)token
場(chǎng)景五是對(duì)以上以上四個(gè)場(chǎng)景在驗(yàn)證速度上的優(yōu)化,親測(cè)使用redis可以提高2-4倍的驗(yàn)證速度。

對(duì)token的要求:

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

客戶端仍然保存著token,每次請(qǐng)求都攜帶token。服務(wù)器接到請(qǐng)求,把token當(dāng)做鍵去redis中拿數(shù)據(jù):

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

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

在這里插入圖片描述

實(shí)操:

        <!--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);
    }

    /**
     * 根據(jù)key 獲取過期時(shí)間
     * @param key 鍵 不能為null
     * @return 時(shí)間(秒) 返回0代表為永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
        /**
     * HashSet 并設(shè)置時(shí)間
     * @param key  鍵
     * @param map  對(duì)應(yīng)多個(gè)鍵值
     * @param time 時(shí)間(秒)
     * @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;
        }
    }
}

攔截請(qǐng)求,開始驗(yàn)證token

public class ShiroRealm extends AuthorizingRealm {

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

        //不使用token驗(yàn)證來(lái)校驗(yàn)token是否可用,改成把token存redis中,在redis中設(shè)置數(shù)據(jù)有效期
        String token = (String) auth.getCredentials();
        if (StringUtils.isEmpty(token)) {
            throw new AuthenticationException(Constant.TOKEN_EXPIRED);
        }
        //判斷是否能從redis中用token拿到過期時(shí)間
        try{
            Long times=redisUtil.getExpire(token);
            //如果還有0.5小時(shí)過期,就刷新token在redis中的有效時(shí)間(有效期設(shè)置為2小時(shí))
            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("驗(yàn)證token失敗");
        }
    }
}

驗(yàn)證redis校驗(yàn)速度

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

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

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

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

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

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

在這里插入圖片描述

總結(jié):

優(yōu)點(diǎn):

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

缺點(diǎn):

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

思想升華:

每種設(shè)置token有效期的方案都有對(duì)應(yīng)的場(chǎng)景,拋開場(chǎng)景談方案是狹隘的。再學(xué)習(xí)計(jì)算機(jī)的過程中,我發(fā)現(xiàn)無(wú)論是磁盤調(diào)度方式、內(nèi)存存儲(chǔ)方式、raid0-6,所有方式方法的誕生都和當(dāng)時(shí)的場(chǎng)景相關(guān),并且往往在時(shí)間和空間上面進(jìn)行取舍。所以沒有最優(yōu)的方案,只有當(dāng)下相對(duì)合適的方案。

到此這篇關(guān)于Java設(shè)置token有效期的5個(gè)應(yīng)用場(chǎng)景(雙token實(shí)現(xiàn))的文章就介紹到這了,更多相關(guān)Java設(shè)置token有效期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論