單系統(tǒng)使用JWT實現(xiàn)登錄案例

一、JWT簡介

在上一篇中介紹了使用cookie+session實現(xiàn)登錄的案例,最后介紹了其缺點,就是需要在服務(wù)器端存放用戶的信息,增加了服務(wù)端的壓力,并且如今移動端盛行,用戶端不支持cookie的問題也要求我們需要尋找其它方案。

JWT,Json Web Token,定義了一種緊湊的,自包含的方式,用于在各方之間以json對象的方式安全地傳輸信息,該信息可用于驗證身份信息。如下是JWT的工作流程示意圖。

JWT工作流程示意圖
  1. 用戶登錄后,服務(wù)端會將用戶的識別信息進行加密生成一個有有效期的token返回給用戶端;
  2. 用戶端收到token后將其進行保存,并在以后的每一次請求時將該token帶上發(fā)送給服務(wù)器;
  3. 服務(wù)器接收到用戶的token后,進行驗證用戶的身份、權(quán)限、有效期等信息,驗證通過即放行,驗證不通過就拒絕服務(wù);

JWT令牌通常由三個部分組成:

  • 標頭header,標識令牌的類型、簽名算法等元信息,用Base64編碼表示;
  • 有效載荷payload,存儲服務(wù)端想要記住的用戶識別信息,用Base64編碼表示;
  • 簽名signature,使用指定的算法以及密鑰,對前兩部分進行簽名得到的字符串,主要目的是為了防篡改;

一個token的三個部分用點號連接,如下所示:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJaSEFOR1hVTiIsImJvZHkiOnsidXNlclJvbGUiOiJhZG1pbiIsInVzZXJpZCI6IjAwMSJ9LCJleHAiOjE2NjI5NTIxNjIsImlhdCI6MTY2Mjk1MTU1NywianRpIjoiZGZhN2MyZjUtNGNjMC00OWFhLWFiMDUtYzZhY2M4M2YxMDViIn0.xOleM21i7-EI0oOq83Xm-nQVOufajHCupY2QjkpwreQ

二、JWT案例

<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.0.0</version>
</dependency>
@Slf4j
@RestController
public class TokenController {
    @Autowired
    private TokenUtil tokenUtil;

    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password){
        if(!"admin".equals(username) || !"123".equals(password)){
            log.info("賬號或者密碼錯誤!");
        }

        // 模擬從數(shù)據(jù)庫中獲取的用戶識別信息
        String userId = "001";
        String userRole = "admin";
        Map<String,Object> dataMap = new HashMap<>();
        dataMap.put("userId", userId);
        dataMap.put("userRole", userRole);

        // 將用戶識別信息存儲到token中
        String token = tokenUtil.createToken(dataMap);
        log.info("生成的token為:{}", token);
        return token;
    }

    @PostMapping("/getUserInfo")
    public String getUserInfo(@RequestParam String token){
        if(ObjectUtils.isEmpty(token)){
            log.info("token不能為空!");
            return "token不能為空!";
        }

        log.info("收到的token為:{}", token);
        Map<String, Object> dataMap = tokenUtil.parseToken(token);
        String userRole = dataMap.get("userRole").toString();
        if(!"admin".equals(userRole)){
            log.info("非管理員角色,不允許訪問!");
            return "非管理員角色,不允許訪問!";
        }

        String userId = dataMap.get("userId").toString();
        return "允許登錄,用戶為:" + userId;
    }
}
@Component
public class TokenUtil {

    /**
     * 默認密鑰
     */
    private static final String DEFAULT_SECRET = "999";

    private static final String DEFAULT_DATA_KEY = "body";

    private static final String DEFAULT_ISSUER = "ZHANGXUN";

    private static final Long DEFAULT_EXPIRE_TIME = 7*24*60*60L;


    public String createToken(Map<String, Object> dataMap){
        return createToken(dataMap, DEFAULT_SECRET);
    }

    public String createToken(Map<String, Object> dataMap, String secret){
        // 指定使用的加密算法
        Algorithm algorithm = Algorithm.HMAC256(secret);
        return JWT.create()
                .withClaim(DEFAULT_DATA_KEY, dataMap)
                .withIssuer(DEFAULT_ISSUER)
                .withIssuedAt(new Date())
                .withExpiresAt(new Date(System.currentTimeMillis() + DEFAULT_EXPIRE_TIME * 1000))
                .withJWTId(UUID.randomUUID().toString())
                .sign(algorithm);
    }

    public Map<String, Object> parseToken(String token){
        return parseToken(token, DEFAULT_SECRET);
    }

    public Map<String, Object> parseToken(String token, String secret){
        // 指定使用的加密算法
        Algorithm algorithm = Algorithm.HMAC256(secret);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        return decodedJWT.getClaim(DEFAULT_DATA_KEY).asMap();
    }

}

三、JWT總結(jié)

優(yōu)點:

  1. 緊湊簡潔,可以放在url后面、post的請求體、http header中發(fā)送出去,數(shù)據(jù)量小,不影響傳輸速度;
  2. 自包含,即token本身包含了用戶識別信息,避免了服務(wù)端去存儲介質(zhì)中查詢用戶的信息;
  3. 不可篡改,簽名確保了json中的用戶識別信息是不可篡改的;
  4. 跨語言,token是json格式的,無論各方平臺如何,都能支持;
  5. 服務(wù)端友好,區(qū)別于cookie+session模式,服務(wù)端不用為登錄態(tài)的用戶存儲任何信息;
  6. 擴展性強,對于分布式系統(tǒng),JWT天然支持應(yīng)用水平擴展,無需修改任何代碼;

缺點:

  1. 不安全,token中的用戶識別信息僅僅使用base64編碼,并非加密,所以是可以被解碼獲取的,因此不應(yīng)該在token中存放用戶的敏感信息;
  2. 時效不可控制,token一旦被設(shè)置了有效期頒發(fā)出去,那么在到期之前,其登錄態(tài)就不能被控制了,無法進行服務(wù)端的干預(yù);

參考資料:

JWT筆記(com.auth0)_L_S_Chen的博客-CSDN博客_com.auth0

GitHub - auth0/java-jwt: Java implementation of JSON Web Token (JWT)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容