单系统使用JWT实现登录案例

一、JWT简介

在上一篇中介绍了使用cookie+session实现登录的案例,最后介绍了其缺点,就是需要在服务器端存放用户的信息,增加了服务端的压力,并且如今移动端盛行,用户端不支持cookie的问题也要求我们需要寻找其它方案。

JWT,Json Web Token,定义了一种紧凑的,自包含的方式,用于在各方之间以json对象的方式安全地传输信息,该信息可用于验证身份信息。如下是JWT的工作流程示意图。

JWT工作流程示意图
  1. 用户登录后,服务端会将用户的识别信息进行加密生成一个有有效期的token返回给用户端;
  2. 用户端收到token后将其进行保存,并在以后的每一次请求时将该token带上发送给服务器;
  3. 服务器接收到用户的token后,进行验证用户的身份、权限、有效期等信息,验证通过即放行,验证不通过就拒绝服务;

JWT令牌通常由三个部分组成:

  • 标头header,标识令牌的类型、签名算法等元信息,用Base64编码表示;
  • 有效载荷payload,存储服务端想要记住的用户识别信息,用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("账号或者密码错误!");
        }

        // 模拟从数据库中获取的用户识别信息
        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总结

优点:

  1. 紧凑简洁,可以放在url后面、post的请求体、http header中发送出去,数据量小,不影响传输速度;
  2. 自包含,即token本身包含了用户识别信息,避免了服务端去存储介质中查询用户的信息;
  3. 不可篡改,签名确保了json中的用户识别信息是不可篡改的;
  4. 跨语言,token是json格式的,无论各方平台如何,都能支持;
  5. 服务端友好,区别于cookie+session模式,服务端不用为登录态的用户存储任何信息;
  6. 扩展性强,对于分布式系统,JWT天然支持应用水平扩展,无需修改任何代码;

缺点:

  1. 不安全,token中的用户识别信息仅仅使用base64编码,并非加密,所以是可以被解码获取的,因此不应该在token中存放用户的敏感信息;
  2. 时效不可控制,token一旦被设置了有效期颁发出去,那么在到期之前,其登录态就不能被控制了,无法进行服务端的干预;

参考资料:

JWT笔记(com.auth0)_L_S_Chen的博客-CSDN博客_com.auth0

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容