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

SpringBoot JWT實現(xiàn)token登錄刷新功能

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

1. 什么是JWT

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

2. JWT組成部分

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

  • 頭部

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

  • 載荷

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

  • 簽名

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

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

3. JWT加密方式

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

  • 對稱加密

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

  • 非對稱加密

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

4.實戰(zhàn)

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

1.maven關(guān)鍵配置如下

<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是否在有效期內(nèi)
  • token是否在刷新的有效期內(nèi)

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

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頭,設(shè)置簽名算法和類型
        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.HS256)
                .type(JOSEObjectType.JWT)
                .build();
        //將負(fù)載信息封裝到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;
        }
        // 校驗是否處于刷新時間內(nèi),重新生成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不存在從參數(shù)中獲取
            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()) {
            // 校驗是否處于刷新時間內(nèi)
            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.權(quán)限攔截
本例中采用了自定義注解+切面的方式來實現(xiàn)token的校驗過程。
自定義Auth注解提供了是否開啟校驗token,sign的選項,實際操作中可以添加更多的功能。

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

    /**
     * 是否校驗sign,默認(rèn)關(guān)閉
     */
    boolean sign() default false;
}

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

@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 {
        //獲取目標(biāo)對象對應(yīng)的字節(jié)碼對象
        Class<?> targetCls=joinPoint.getTarget().getClass();
        //獲取方法簽名信息從而獲取方法名和參數(shù)類型
        MethodSignature ms= (MethodSignature) joinPoint.getSignature();
        //獲取目標(biāo)方法對象上注解中的屬性值
        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.總結(jié)

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

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

相關(guān)文章

  • 淺談java8 stream flatMap流的扁平化操作

    淺談java8 stream flatMap流的扁平化操作

    這篇文章主要介紹了淺談java8 stream flatMap流的扁平化操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Spring Security 中如何讓上級擁有下級的所有權(quán)限(案例分析)

    Spring Security 中如何讓上級擁有下級的所有權(quán)限(案例分析)

    這篇文章主要介紹了Spring Security 中如何讓上級擁有下級的所有權(quán)限,本文通過案例分析給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • spring boot @ResponseBody轉(zhuǎn)換JSON 時 Date 類型處理方法【兩種方法】

    spring boot @ResponseBody轉(zhuǎn)換JSON 時 Date 類型處理方法【兩種方法】

    這篇文章主要介紹了spring boot @ResponseBody轉(zhuǎn)換JSON 時 Date 類型處理方法,主要給大家介紹Jackson和FastJson兩種方式,每一種方法給大家介紹的都非常詳細(xì),需要的朋友可以參考下
    2018-08-08
  • SpringBoot實現(xiàn)發(fā)送郵件、發(fā)送微信公眾號推送功能

    SpringBoot實現(xiàn)發(fā)送郵件、發(fā)送微信公眾號推送功能

    這篇文章主要介紹了SpringBoot實現(xiàn)發(fā)送郵件、發(fā)送微信公眾號推送功能,這里對成員變量JavaMailSender使用了@Resource注解,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • Java中LinkedList真的是查找慢增刪快

    Java中LinkedList真的是查找慢增刪快

    這篇文章主要介紹了Java中LinkedList真的是查找慢增刪快,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Java 基礎(chǔ)--Arrays工具類詳解

    Java 基礎(chǔ)--Arrays工具類詳解

    這篇文章主要介紹了Java Arrays工具類用法,結(jié)合實例形式分析了java Arrays工具類針對數(shù)組元素修改、復(fù)制、排序等操作使用技巧與相關(guān)注意事項,需要的朋友可以參考下
    2021-09-09
  • Java中Finally關(guān)鍵字

    Java中Finally關(guān)鍵字

    與其他語言的模型相比,finally 關(guān)鍵字是對 Java 異常處理模型的最佳補充。接下來通過本文給大家介紹Java中Finally關(guān)鍵字及finally關(guān)鍵字的使用相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧
    2016-05-05
  • 掌握模塊化開發(fā)Spring Boot子模塊使用技巧

    掌握模塊化開發(fā)Spring Boot子模塊使用技巧

    這篇文章主要為大家介紹了掌握模塊化開發(fā)Spring Boot子模塊使用技巧詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • Springboot中的Validation參數(shù)校驗詳解

    Springboot中的Validation參數(shù)校驗詳解

    這篇文章主要介紹了Springboot中的Validation參數(shù)校驗詳解,Springboot參數(shù)校驗是一種常用的驗證機制,在傳遞參數(shù)時進(jìn)行校驗,以確保參數(shù)的有效性和正確性,該機制可以幫助開發(fā)者在代碼實現(xiàn)前就避免一些常見的錯誤,需要的朋友可以參考下
    2023-10-10
  • 數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的深入分析

    數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的深入分析

    本篇文章是對數(shù)組重排序(如何將所有奇數(shù)都放在所有偶數(shù)前面)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06

最新評論