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

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的開放標準。簡答理解就是一個身份憑證,用于服務(wù)識別。
JWT本身是無狀態(tài)的,這點有別于傳統(tǒng)的session,不在服務(wù)端存儲憑證。這種特性使其在分布式場景,更便于擴展使用。

2. JWT組成部分

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

  • 頭部

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

  • 載荷

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

  • 簽名

簽名進行對之前的數(shù)據(jù)添加一層防護,防止被篡改。
簽名生成過程: 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加密分為兩種對稱加密和非對稱加密。

  • 對稱加密

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

  • 非對稱加密

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

4.實戰(zhàn)

本例采用了spring2.x,jwt使用了nimbus-jose-jwt版本,當然其他的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,前端進行識別,發(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頭,設(shè)置簽名算法和類型
        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;
        }
        // 校驗是否處于刷新時間內(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,默認開啟
     */
    boolean token() default true;

    /**
     * 是否校驗sign,默認關(guān)閉
     */
    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 {
        //獲取目標對象對應(yīng)的字節(jié)碼對象
        Class<?> targetCls=joinPoint.getTarget().getClass();
        //獲取方法簽名信息從而獲取方法名和參數(shù)類型
        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.總結(jié)

許多同學使用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)文章

最新評論

永和县| 古田县| 台东县| 边坝县| 镇安县| 监利县| 肃南| 虞城县| 灵山县| 永泰县| 四子王旗| 胶州市| 伊宁县| 长岭县| 中牟县| 海口市| 格尔木市| 富锦市| 浦江县| 阳新县| 犍为县| 湟源县| 东乌| 桂平市| 教育| 青海省| 新乐市| 玛曲县| 乐清市| 长沙市| 军事| 香格里拉县| 西乡县| 东兴市| 闻喜县| 泾阳县| 台山市| 望都县| 商南县| 合阳县| 蒙自县|