最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot JWT實現token登錄刷新功能

 更新時間:2021年09月28日 10:35:02   作者:雨夜歸人93  
JWT本身是無狀態(tài)的,這點有別于傳統的session,不在服務端存儲憑證。這種特性使其在分布式場景,更便于擴展使用。接下來通過本文給大家分享SpringBoot JWT實現token登錄刷新功能,感興趣的朋友一起看看吧

1. 什么是JWT

Json web token (JWT) 是為了在網絡應用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標準。簡答理解就是一個身份憑證,用于服務識別。
JWT本身是無狀態(tài)的,這點有別于傳統的session,不在服務端存儲憑證。這種特性使其在分布式場景,更便于擴展使用。

2. JWT組成部分

JWT有三部分組成,頭部(header),載荷(payload),是簽名(signature)。

  • 頭部

頭部主要聲明了類型(jwt),以及使用的加密算法( HMAC SHA256)

  • 載荷

載荷就是存放有自定義信息的地方,例如用戶標識,截止日期等

  • 簽名

簽名進行對之前的數據添加一層防護,防止被篡改。
簽名生成過程: base64加密后的header和base64加密后的payload使用.連接組成的字符串,然后通過header中聲明的加密方式進行加鹽secret組合加密。

// base64加密后的header和base64加密后的payload使用.連接組成的字符串
String str=base64(header).base64(payload);
// 加鹽secret進行加密
String sign=HMACSHA256(encodedString, 'secret');

3. JWT加密方式

jwt加密分為兩種對稱加密和非對稱加密。

  • 對稱加密

對稱加密指使用同一秘鑰進行加密,解密的操作。加密解密的速度比較快,適合數據比較長時的使用。常見的算法為DES、3DES等

  • 非對稱加密

非對稱指通過公鑰進行加密,通過私鑰進行解密。加密和解密花費的時間長、速度相對較慢,但安全性更高,只適合對少量數據的使用。常見的算法RSA、ECC等。
兩種加密方法沒有誰更好,只有哪種場景更合適。

4.實戰(zhàn)

本例采用了spring2.x,jwt使用了nimbus-jose-jwt版本,當然其他的jwt版本也都類似,封裝的都是不錯的。

1.maven關鍵配置如下

<dependency>
            <groupId>com.nimbusds</groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
            <version>9.12.1</version>
        </dependency>
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>

2.jwt工具類

對于這里的秘鑰:采用了userId+salt+uuid的方式保證,即使是同一個用戶每次生成的serect都是不同的

對于校驗token有效性,包含三個過程:

  • 格式是否合法
  • token是否在有效期內
  • token是否在刷新的有效期內

對于token超過有效期,但在刷新有效期內,返回特定的code,前端進行識別,發(fā)起請求刷新token,達到用戶無感知的過程。

public class JwtUtil {
    private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);

    private static final String BEARER_TYPE = "Bearer";
    private static final String PARAM_TOKEN = "token";
    /**
     * 秘鑰
     */
    private static final String SECRET = "dfg#fh!Fdh3443";
    /**
     * 有效期12小時
     */
    private static final long EXPIRE_TIME = 12 * 3600 * 1000;
    /**
     * 刷新時間7天
     */
    private static final long REFRESH_TIME = 7 * 24 * 3600 * 1000;


    public static String generate(PayloadDTO payloadDTO)  {
        //創(chuàng)建JWS頭,設置簽名算法和類型
        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256)
                .type(JOSEObjectType.JWT)
                .build();
        //將負載信息封裝到Payload中
        Payload payload = new Payload(JSON.toJSONString(payloadDTO));
        //創(chuàng)建JWS對象
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        try {
            //創(chuàng)建HMAC簽名器
            JWSSigner jwsSigner = new MACSigner(payloadDTO.getUserId() + SECRET+payloadDTO.getJti());
            //簽名
            jwsObject.sign(jwsSigner);
            return jwsObject.serialize();
        } catch (JOSEException e) {
            log.error("jwt生成器異常",e);
            throw new BizException(TOKEN_SIGNER);
        }
    }


    public static String freshToken(String token)   {
        PayloadDTO payloadDTO;
        try {
            //從token中解析JWS對象
            JWSObject jwsObject = JWSObject.parse(token);
            payloadDTO = JSON.parseObject(jwsObject.getPayload().toString(), PayloadDTO.class);
            // 校驗格式是否合適
            verifyFormat(payloadDTO, jwsObject);
        }catch (ParseException e) {
            log.error("jwt解析異常",e);
            throw new BizException(TOKEN_PARSE);
        } catch (JOSEException e) {
            log.error("jwt生成器異常",e);
            throw new BizException(TOKEN_SIGNER);
        }
        // 校驗是否過期,未過期直接返回原token
        if (payloadDTO.getExp() >= System.currentTimeMillis()) {
            return token;
        }
        // 校驗是否處于刷新時間內,重新生成token
        if (payloadDTO.getRef() >= System.currentTimeMillis()) {
            getRefreshPayload(payloadDTO);
            return generate(payloadDTO);
        }
        throw new BizException(TOKEN_EXP);
    }



    private static void verifyFormat(PayloadDTO payloadDTO, JWSObject jwsObject) throws JOSEException {
        //創(chuàng)建HMAC驗證器
        JWSVerifier jwsVerifier = new MACVerifier(payloadDTO.getUserId() + SECRET+payloadDTO.getJti());
        if (!jwsObject.verify(jwsVerifier)) {
            throw new BizException(TOKEN_ERROR);
        }
    }


    public static String getTokenFromHeader(HttpServletRequest request) {
        // 先從header取值
        String value = request.getHeader("Authorization");
        if (!StringUtils.hasText(value)) {
            // header不存在從參數中獲取
            value = request.getParameter(PARAM_TOKEN);
            if (!StringUtils.hasText(value)) {
                throw new BizException(TOKEN_MUST);
            }
        }
        if (value.toLowerCase().startsWith(BEARER_TYPE.toLowerCase())) {
            return value.substring(BEARER_TYPE.length()).trim();
        }
        return value;
    }


    public static PayloadDTO verify(String token)  {
        PayloadDTO payloadDTO;
        try {
            //從token中解析JWS對象
            JWSObject jwsObject = JWSObject.parse(token);
            payloadDTO = JSON.parseObject(jwsObject.getPayload().toString(), PayloadDTO.class);
            // 校驗格式是否合適
            verifyFormat(payloadDTO, jwsObject);
        }catch (ParseException e) {
            log.error("jwt解析異常",e);
            throw new BizException(TOKEN_PARSE);
        } catch (JOSEException e) {
            log.error("jwt生成器異常",e);
            throw new BizException(TOKEN_SIGNER);
        }
        // 校驗是否過期
        if (payloadDTO.getExp() < System.currentTimeMillis()) {
            // 校驗是否處于刷新時間內
            if (payloadDTO.getRef() >= System.currentTimeMillis()) {
                throw new BizException(TOKEN_REFRESH);
            }
            throw new BizException(TOKEN_EXP);
        }
        return payloadDTO;
    }

    public static PayloadDTO getDefaultPayload(Long userId) {
        long currentTimeMillis = System.currentTimeMillis();
        PayloadDTO payloadDTO = new PayloadDTO();
        payloadDTO.setJti(UUID.randomUUID().toString());
        payloadDTO.setExp(currentTimeMillis + EXPIRE_TIME);
        payloadDTO.setRef(currentTimeMillis + REFRESH_TIME);
        payloadDTO.setUserId(userId);
        return payloadDTO;

    }

    public static void getRefreshPayload(PayloadDTO payload) {
        long currentTimeMillis = System.currentTimeMillis();
        payload.setJti(UUID.randomUUID().toString());
        payload.setExp(currentTimeMillis + EXPIRE_TIME);
        payload.setRef(currentTimeMillis + REFRESH_TIME);
    }
}

3.權限攔截
本例中采用了自定義注解+切面的方式來實現token的校驗過程。
自定義Auth注解提供了是否開啟校驗token,sign的選項,實際操作中可以添加更多的功能。

@Target(value = ElementType.METHOD)
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Auth {
    /**
     * 是否校驗token,默認開啟
     */
    boolean token() default true;

    /**
     * 是否校驗sign,默認關閉
     */
    boolean sign() default false;
}

切面部分指定了對Auth進行切面,這種方法比采用攔截器方式更加靈活些。

@Component
@Aspect
public class AuthAspect {
    @Autowired
    private HttpServletRequest request;

    @Pointcut("@annotation(com.rain.jwt.config.Auth)")
    private void authPointcut(){}

    @Around("authPointcut()")
    public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        //獲取目標對象對應的字節(jié)碼對象
        Class<?> targetCls=joinPoint.getTarget().getClass();
        //獲取方法簽名信息從而獲取方法名和參數類型
        MethodSignature ms= (MethodSignature) joinPoint.getSignature();
        //獲取目標方法對象上注解中的屬性值
        Auth auth=ms.getMethod().getAnnotation(Auth.class);
        // 校驗簽名
        if (auth.token()) {
            String token = JwtUtil.getTokenFromHeader(request);
            JwtUtil.verify(token);
        }
        // 校驗簽名
        if (auth.sign()) {
            // todo
        }
        return joinPoint.proceed();
    }
}

4.測試接口

@RestController
@RequestMapping(value="/user")
@Api(tags = "用戶")
public class UserController {


    @PostMapping(value = "/login")
    @Auth(token = false)
    @ApiOperation("登錄")
    public Result<String> login(String username,String password) {
        // 用戶常規(guī)校驗
        Long userId = 100L;
        // 用戶信息存入緩存
        // 生成token
        String token = JwtUtil.generate(JwtUtil.getDefaultPayload(userId));
        return Result.success(token);
    }

    @GetMapping(value = "refreshToken")
    @Auth
    @ApiOperation("刷新token")
    public Result<String> refreshToken(String token) {
        String freshToken = JwtUtil.freshToken(token);
        return Result.success(freshToken);
    }

    @GetMapping(value = "test")
    @Auth
    @ApiOperation("測試")
    public Result<String> test() {
        return Result.success("測試成功");
    }
}

5.總結

許多同學使用jwt經常將獲取到的token放在redis中,在服務器端控制其有效性。這是一種處理token的方式,但這種方式跟jwt的思路是背道而去的,jwt本身就提供了過期的信息,將token的生命周期放入服務器中,又何必采用jwt的方式呢?直接來個uuid不香么。
最后來個項目地址。

到此這篇關于SpringBoot JWT實現登錄刷新token的文章就介紹到這了,更多相關SpringBoot JWT實現token登錄內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java Proxy機制詳細解讀

    Java Proxy機制詳細解讀

    這篇文章主要介紹了Java Proxy機制詳細解讀,還是非常不錯的,這里分享給大家,需要的朋友可以參考下。
    2017-10-10
  • 使用dubbo+zookeeper+spring boot構建服務的方法詳解

    使用dubbo+zookeeper+spring boot構建服務的方法詳解

    這篇文章主要給大家介紹了關于如何使用dubbo+zookeeper+spring boot構建服務的相關資料,文中通過示例代碼及圖片介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧
    2018-05-05
  • Java進階核心之InputStream流深入講解

    Java進階核心之InputStream流深入講解

    這篇文章主要給大家介紹了關于Java進階核心之InputStream流的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-02-02
  • hadoop運行java程序(jar包)并運行時動態(tài)指定參數

    hadoop運行java程序(jar包)并運行時動態(tài)指定參數

    這篇文章主要介紹了hadoop如何運行java程序(jar包)并運行時動態(tài)指定參數,使用hadoop 運行 java jar包,Main函數一定要加上全限定類名,需要的朋友可以參考下
    2021-06-06
  • Java日期時間使用方法匯總

    Java日期時間使用方法匯總

    這篇文章主要針對Java日期時間使用方法進行匯總,感興趣的朋友可以參考一下
    2016-03-03
  • java字符串替換排序實例

    java字符串替換排序實例

    這篇文章主要介紹了java字符串替換排序實例,有需要的朋友可以參考一下
    2014-01-01
  • JVM的垃圾回收機制詳解和調優(yōu)

    JVM的垃圾回收機制詳解和調優(yōu)

    JVM的垃圾回收機制詳解和調優(yōu)...
    2006-12-12
  • 淺談Java BitSet使用場景和代碼示例

    淺談Java BitSet使用場景和代碼示例

    這篇文章主要介紹了淺談Java BitSet使用場景和代碼示例,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • JAVA多線程之中斷機制及處理中斷的方法

    JAVA多線程之中斷機制及處理中斷的方法

    這篇文章主要記錄使用 interrupt() 方法中斷線程,以及如何對InterruptedException進行處理,感覺對InterruptedException異常進行處理是一件謹慎且有技巧的活兒,需要的朋友可以參考下
    2023-02-02
  • Java刷題之最小k個數的思路及具體實現

    Java刷題之最小k個數的思路及具體實現

    這篇文章主要介紹了Java刷題之最小k個數的思路及具體實現,最小K個數是一個經典的top-K問題,可以通過整體排序、建立小根堆或大根堆的方式解決,排序方式時間復雜度較高,適合數據量小的場景,小根堆適合k較小的情況,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-10-10

最新評論

达孜县| 阿城市| 江达县| 株洲县| 黄骅市| 本溪| 榆社县| 桃江县| 大理市| 阳谷县| 德钦县| 丁青县| 航空| 缙云县| 临清市| 连城县| 荣昌县| 贵阳市| 莒南县| 潞西市| 西畴县| 武乡县| 西吉县| 信宜市| 丹棱县| 福州市| 班玛县| 和平区| 永善县| 如东县| 新竹县| 定远县| 姚安县| 宝兴县| 监利县| 应城市| 马公市| 辉县市| 万荣县| 赤城县| 汝城县|