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

springboot+vue實現(xiàn)Token自動續(xù)期(雙Token方案)

 更新時間:2024年10月09日 10:54:38   作者:登山人在路上  
雙Token方案通過訪問令牌和刷新令牌提高用戶登錄安全性和體驗,訪問令牌有效期短,包含用戶信息,用于請求校驗,本文就來介紹一下springboot+vue實現(xiàn)Token自動續(xù)期(雙Token方案),感興趣的可以了解一下

本篇文章的代碼是在springboot+vue項目中使用jwt實現(xiàn)登錄認(rèn)證的基礎(chǔ)上實現(xiàn)的Token自動續(xù)期的功能。

一、雙Token方案介紹

雙token解決方案是一種用于增強(qiáng)用戶登錄安全性和提升用戶體驗的認(rèn)證機(jī)制。它主要涉及兩個令牌:訪問令牌(accessToken)和刷新令牌(refreshToken)。以下是對雙token解決方案的詳細(xì)介紹:

1. 令牌類型與功能

訪問令牌(accessToken):
有效期較短,通常設(shè)置為較短的時間,如兩小時或根據(jù)業(yè)務(wù)需求自定義(如10分鐘)。 儲存用戶信息權(quán)限等,包含用戶相關(guān)信息,如UserID、Username等。 用于前端與后端之間的通信認(rèn)證,前端在每次請求時攜帶此令牌進(jìn)行校驗。

刷新令牌(refreshToken):
有效期較長,可以設(shè)置為一星期、一個月或更長時間,具體根據(jù)業(yè)務(wù)需求自定義。 不儲存額外信息,只儲存用戶id,用于在accessToken過期后重新生成新的accessToken。 由于有效期長,因此降低了用戶需要頻繁登錄的頻率。

2.雙Token方案的優(yōu)點

增強(qiáng)安全性:通過短期有效的accessToken和長期有效的refreshToken的結(jié)合,即使accessToken泄露,攻擊者也只能在有限時間內(nèi)進(jìn)行模擬用戶行為,降低了安全風(fēng)險。

提升用戶體驗:由于refreshToken的存在,用戶無需頻繁登錄,特別是在長時間操作或后臺服務(wù)場景下,提高了用戶體驗。

3.實現(xiàn)流程

登錄:用戶輸入用戶名和密碼進(jìn)行登錄,后端驗證成功后生成accessToken和refreshToken,并發(fā)送給前端。

請求校驗:前端在每次請求時攜帶accessToken進(jìn)行校驗,如果accessToken有效,則允許請求繼續(xù);如果無效但refreshToken有效,則使用refreshToken重新生成accessToken。

令牌刷新:當(dāng)accessToken過期但refreshToken未過期時,前端可以使用refreshToken向后端請求新的accessToken,無需用戶重新登錄。

登出:用戶登出時,后端需要同時使accessToken和refreshToken失效,以確保用戶登出后的安全性。

二、具體實現(xiàn)

1.后端實現(xiàn)

1.1 jwt工具類

package com.etime.util;

import io.jsonwebtoken.*;

import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * @Date 2024/6/10 10:04
 * @Author liukang
 **/
public class JwtUtil {
//    private static long expire = 1000*60*5;// 單位是毫秒
    private static String secret = "secret";

    /**
     * 創(chuàng)建jwt
     * @author liukang
     * @date 10:36 2024/6/10
     * @param expire
     * @param map
     * @return java.lang.String
     **/
    public static String generateToken(long expire, Map map){
        // 床jwt構(gòu)造器
        JwtBuilder jwtBuilder = Jwts.builder();
        // 生成jwt字符串
        String jwt = jwtBuilder
                //頭部
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                // 載荷
                .setClaims(map) // 設(shè)置多個自定義數(shù)據(jù)  位置只能放在前面,如果放在后面,那前面的載荷會失效
                .setId(UUID.randomUUID().toString())// 唯一標(biāo)識
                .setIssuer("liukang")// 簽發(fā)人
                .setIssuedAt(new Date())// 簽發(fā)時間
                .setSubject("jwtDemo")// 主題
                .setExpiration(new Date(System.currentTimeMillis()+expire))//過期時間
                // 自定義數(shù)據(jù)
//                .claim("uname","liukang")
                // 簽名
                .signWith(SignatureAlgorithm.HS256,secret)
                .compact();
        return jwt;
    }
    /**
     * 創(chuàng)建jwt
     * @author liukang
     * @date 10:36 2024/6/10
     * @param expire
     * @return java.lang.String
     **/
    public static String generateToken(long expire){
        // 床jwt構(gòu)造器
        JwtBuilder jwtBuilder = Jwts.builder();
        // 生成jwt字符串
        String jwt = jwtBuilder
                //頭部
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                // 載荷
                .setId(UUID.randomUUID().toString())// 唯一標(biāo)識
                .setIssuer("liukang")// 簽發(fā)人
                .setIssuedAt(new Date())// 簽發(fā)時間
                .setSubject("jwtDemo")// 主題
                .setExpiration(new Date(System.currentTimeMillis()+expire))//過期時間
                // 自定義數(shù)據(jù)
//                .claim("uname","liukang")
                // 簽名
                .signWith(SignatureAlgorithm.HS256,secret)
                .compact();
        return jwt;
    }
    /**
     * 解析jwt
     * @author liukang
     * @date 10:36 2024/6/10
     * @param jwt
     * @return io.jsonwebtoken.Claims
     **/
    public static Claims parseToken(String jwt){
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt);
        Claims playload = claimsJws.getBody();
        return playload;

    }
}

1.2 響應(yīng)工具類

代碼如下(示例):

package com.etime.util;

import com.etime.vo.ResponseModel;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @Date 2024/6/10 10:00
 * @Author liukang
 **/
public class ResponseUtil {
    public static void write(ResponseModel rm, HttpServletResponse response) throws IOException {
        // 構(gòu)造響應(yīng)頭
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("utf-8");
        // 解決跨域問題 設(shè)置跨域頭
        response.setHeader("Access-Control-Allow-Origin","*");
        // 輸出流
        PrintWriter out = response.getWriter();
        // 輸出
        out.write(new ObjectMapper().writeValueAsString(rm));
        // 關(guān)閉流
        out.close();
    }
}

1.3 實體類

登錄用戶實體類

package com.etime.entity;

import lombok.Data;

/**
 * @Date 2024/6/10 10:39
 * @Author liukang
 **/
@Data
public class User {
    private String username;
    private String password;
}

響應(yīng)vo類

package com.etime.vo;

import lombok.Data;

import java.util.Objects;

/**
 * @Date 2024/6/10 10:37
 * @Author liukang
 **/
@Data
public class ResponseModel {
    private Integer code;
    private String msg;
    private Object token;

    public ResponseModel(Integer code, String msg, Object token) {
        this.code = code;
        this.msg = msg;
        this.token = token;
    }
}

1.4 過濾器

package com.etime.filter;

import com.etime.util.JwtUtil;
import com.etime.util.ResponseUtil;
import com.etime.vo.ResponseModel;
import com.sun.deploy.net.HttpResponse;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Description jwt過濾器
 * @Date 2024/6/10 9:46
 * @Author liukang
 **/
@WebFilter(urlPatterns = "/*") // 過濾所有路徑
public class JwtFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 得到兩個對象
        HttpServletRequest request =  (HttpServletRequest) servletRequest;
        HttpServletResponse response =  (HttpServletResponse) servletResponse;
        //直接放行
        if(HttpMethod.OPTIONS.toString().equals(request.getMethod())){
            filterChain.doFilter(request,response);
            return;
        }
        String requestURI = request.getRequestURI(); // 不含主機(jī)和端口號
        if(requestURI.contains("/login")){
            filterChain.doFilter(request,response);
            return;
        }
        // 得到請求頭的信息(accessToken)
        String token = request.getHeader("accessToken");
        if(!StringUtils.hasText(token)){
            //響應(yīng)前端錯誤的消息提示
            ResponseModel responseModel = new ResponseModel(500,"failure","令牌缺失!");
            ResponseUtil.write(responseModel,response);
            return;
        }
        // 解析Token信息
        try {
            JwtUtil.parseToken(token);
        }catch (Exception e){
            //響應(yīng)前端錯誤的消息提示
            ResponseModel responseModel = new ResponseModel(401,"failure","令牌過期!");
            ResponseUtil.write(responseModel,response);
            return;
        }
        filterChain.doFilter(request,response);


    }
}

1.5 controller

登錄Controller

package com.etime.controller;

import com.etime.entity.User;
import com.etime.util.JwtUtil;
import com.etime.vo.ResponseModel;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @Date 2024/6/10 10:38
 * @Author liukang
 **/
@RestController
@CrossOrigin
public class LoginController {
    @PostMapping("/login")
    public ResponseModel login(@RequestBody User user){
        Integer code = 200;
        String msg = "success";
        String accessToken = null;
        String refreshToken = null;
        Map tokenMap = new HashMap();
        if(user.getUsername().equals("admin")&&user.getPassword().equals("123")){
            // 生成jwt
            accessToken = JwtUtil.generateToken(1000*10);// 設(shè)置有效期為10s
            refreshToken = JwtUtil.generateToken(1000*30);// 設(shè)置有效期為30s
            tokenMap.put("accessToken",accessToken);
            tokenMap.put("refreshToken",refreshToken);
        }else {
            code = 500;
            msg = "failure";
        }
        return new ResponseModel(code,msg,tokenMap);
    }


}

測試請求Controller

package com.etime.controller;

import com.etime.vo.ResponseModel;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Date 2024/6/10 12:51
 * @Author liukang
 **/
@CrossOrigin
@RestController
public class TestController {
    @PostMapping("/test")
    public ResponseModel test() {
        return new ResponseModel(200,"success","測試請求接口成功!");
    }

}

刷新Token的Controller

package com.etime.controller;

import com.etime.util.JwtUtil;
import com.etime.vo.ResponseModel;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * @Date 2024/6/10 15:48
 * @Author liukang
 **/
@CrossOrigin
@RestController
public class NewTokenController {
    @GetMapping("/newToken")
    public ResponseModel newToken(){
        String accessToken = JwtUtil.generateToken(1000*10);
        String refreshToken = JwtUtil.generateToken(1000*30);
        Map tokenMap = new HashMap();
        tokenMap.put("accessToken",accessToken);
        tokenMap.put("refreshToken",refreshToken);
        return new ResponseModel(200,"success",tokenMap);
    }
}

1.6 啟動類

package com.etime;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

/**
 * @Author liukang
 * @Date 2022/7/4 11:32
 */
@SpringBootApplication
@ServletComponentScan(basePackages = "com.etime.filter")// 這個包下激活WebFilter這個注解
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

2、前端實現(xiàn)

2.1 登錄頁面

<template>
    <div class="hello">
      <form>
        用戶名:<input v-model="username"/>
        

        密碼<input v-model="password" />
        

        <button @click="login">登錄</button>
      </form>

    </div>
  </template>

  <script>
  export default {
    data () {
      return {
        username:'',
        password:'',

      }
    },

    methods:{

        login(){
          this.axios.post('http://localhost:8088/login',{

            username:this.username,
            password:this.password,

          }).then(response => {
              console.log(response.data);
              if(response.data.code==200){
                sessionStorage.setItem("accessToken",response.data.token.accessToken)
                sessionStorage.setItem("refreshToken",response.data.token.refreshToken)
                this.$router.push({ path: 'index'});
              }
            }).catch(error => {
              console.error(error);
            });

        }
    },
  }
  </script>

  <style scoped>

  </style>

2.2 index頁面

<template>
    <div>
        <button @click="test">請求受保護(hù)的接口</button>

    </div>
  </template>

  <script>
import intercepterConfig from './js/config'

  export default {
    data () {
      return {
      }
    },
    methods:{
      test(){
        const accessToken = sessionStorage.getItem('accessToken')
        let token = null
        if(accessToken){
          token = accessToken
        }
        // console.log(token)
        this.axios.post('http://localhost:8088/test',{},/*{headers:{accessToken:'token'}}*/
        ).then(response => {
          // if(response.data.code==200){
            console.log(response.data);
          // }
        }).catch(error => {
          console.error(error);
        });
      },
    },
  }
  </script>


  <style scoped>

  </style>

2.3 請求攔截器和響應(yīng)攔截器

import axios from "axios";
//axios請求攔截器
axios.interceptors.request.use(
  config=>{// 正確的請求攔截器
    let token = null;
    let url = config.url
    // url.indexOf('/newToken')==-1  如果是刷新Token的請求 不用在攔截器里面加accessToken 這個請求已經(jīng)在請求頭中設(shè)置accessToken,加了會覆蓋
    if(sessionStorage.getItem('accessToken')!=null && url.indexOf('/newToken')==-1){
      token = sessionStorage.getItem('accessToken')
      config.headers['accessToken'] = token
    }

    // 加入頭信息的配置
    return config // 這句沒寫請求會發(fā)不出去
  },
  error=>{ // 出現(xiàn)異常的請求攔截器
    return Promise.reject(error)
  })
// axios響應(yīng)攔截器
axios.interceptors.response.use(
  async res => {
    // 判斷 401狀態(tài)碼 自動續(xù)期
    if (res.data.code == 401 &&!res.config.isRefresh) {//!res.config.isRefresh  不是刷新Token的請求才攔截 是則不攔截
      // 1.自動續(xù)期
      const res2 = await getNewToken()
      if(res2.data.code == 200){
        console.log('自動續(xù)期成功'+new Date().toLocaleString())
        // 2.更新sessionStorage里面的Token   沒有這一步會死循環(huán)
        sessionStorage.setItem('accessToken',res2.data.token.accessToken)
        sessionStorage.setItem('refreshToken',res2.data.token.refreshToken)
        //3.重新發(fā)送請求
        res = await axios.request(res.config)// res.config 代表請求的所有參數(shù)(這里是上一次請求的所有參數(shù)),包括url和攜帶的所有數(shù)據(jù)

      }

    }
    return res     // 將重新請求的響應(yīng)作為響應(yīng)返回
  },
  error=>{
    return Promise.reject(error)
  })

function getNewToken(){
  let url = "http://localhost:8088/newToken"
  let token = null
  if(sessionStorage.getItem('refreshToken')!=null){
    token = sessionStorage.getItem('refreshToken')
  }
  return  axios.get(url,{headers:{accessToken:token},isRefresh:true})
  // 注意這里參數(shù)是accessToken:token  因為后端過濾器里面獲取的是accessToken,所以要寫這個,不然過濾器通不過過濾器
}

效果展示

1.登錄頁面

在這里插入圖片描述

2.輸入用戶名和密碼點擊【登錄】

在這里插入圖片描述

3.點擊【請求受保護(hù)的資源】按鈕

在這里插入圖片描述

3.等待10秒,accessToken過期,但refreshToken未過期時,點擊【請求受保護(hù)的資源】按鈕

在這里插入圖片描述

4.等待30秒后,refreshToken和accessToken都過期,再次點擊【請求受保護(hù)的資源】按鈕

在這里插入圖片描述

到此這篇關(guān)于springboot+vue實現(xiàn)Token自動續(xù)期(雙Token方案)的文章就介紹到這了,更多相關(guān)springboot Token自動續(xù)期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • Java使用泛型實現(xiàn)棧結(jié)構(gòu)的示例代碼

    Java使用泛型實現(xiàn)棧結(jié)構(gòu)的示例代碼

    泛型是JAVA重要的特性,使用泛型編程,可以使代碼復(fù)用率提高。本文將利用泛型實現(xiàn)簡單的棧結(jié)構(gòu),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2022-08-08
  • Java線程安全中的有序性淺析

    Java線程安全中的有序性淺析

    這篇文章主要介紹了Java線程安全中的有序性,在開發(fā)中,我們通常按照從上到下的順序編寫程序指令,并且希望cpu和編譯器按照我們預(yù)先編寫的順序去執(zhí)。但往往cpu和編譯器為了提高性能、優(yōu)化指令的執(zhí)行順序,會將我們編寫好的程序指令進(jìn)行重排序
    2023-02-02
  • Java實現(xiàn)手寫線程池的示例代碼

    Java實現(xiàn)手寫線程池的示例代碼

    在我們的日常的編程當(dāng)中,并發(fā)是始終離不開的主題,而在并發(fā)多線程當(dāng)中,線程池又是一個不可規(guī)避的問題。本文就來分享一下如何自己手寫一個線程池,需要的可以參考一下
    2022-08-08
  • 基于java中的null類型---有關(guān)null的9件事

    基于java中的null類型---有關(guān)null的9件事

    這篇文章主要介紹了java中的null類型---有關(guān)null的9件事,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 淺談JAVA中輸入輸出流實例詳解

    淺談JAVA中輸入輸出流實例詳解

    Java中的流分為兩種,一種是字節(jié)流,另一種是字符流。這篇文章主要介紹了JAVA中輸入輸出流的相關(guān)資料,需要的朋友可以參考下
    2016-07-07
  • 學(xué)習(xí)Java HashMap,看這篇就夠了

    學(xué)習(xí)Java HashMap,看這篇就夠了

    這篇文章主要介紹了Java HashMap的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • Mybatis集成到Spring容器的詳細(xì)步驟

    Mybatis集成到Spring容器的詳細(xì)步驟

    在現(xiàn)在的JavaEE開發(fā)過程中,我們經(jīng)常會使用到Spring+SpringMVC+Mybatis這個組合,那么Mybatis是如何集成到Spring中的呢,下面通過實例代碼給大家詳細(xì)講解,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • java  實現(xiàn)輸出隨機(jī)圖片實例代碼

    java 實現(xiàn)輸出隨機(jī)圖片實例代碼

    這篇文章主要介紹了java 實現(xiàn)輸出隨機(jī)圖片實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • JAVA實現(xiàn)遍歷文件夾下的所有文件(遞歸調(diào)用和非遞歸調(diào)用)

    JAVA實現(xiàn)遍歷文件夾下的所有文件(遞歸調(diào)用和非遞歸調(diào)用)

    本篇文章主要介紹了JAVA 遍歷文件夾下的所有文件(遞歸調(diào)用和非遞歸調(diào)用) ,具有一定的參考價值,有興趣的可以了解一下。
    2017-01-01
  • Java實現(xiàn)基礎(chǔ)銀行ATM系統(tǒng)

    Java實現(xiàn)基礎(chǔ)銀行ATM系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)基礎(chǔ)銀行ATM系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05

最新評論