jwt教程学习

5 篇文章 0 订阅
1 篇文章 0 订阅

JWT

1、什么是JWT

官网:https://jwt.io/

学习资料:https://baobao555.tech/archives/40

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于作为JSON对象在各方之间安全地传输信息。可以验证和信任该信息,因为它是数字签名的。jwt可以使用一个秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

简单来说,就是通过JSON形式作为Web应用中的令牌,用于在各方面之间安全的将信息作为JSON对象传输,在数据传输过程中还可以完成数据加密、签名等相关处理。

2、应用领域

  • 授权

这是JWT最常见的应用,一旦用户登录,每个后续请求都会包含JWT,从而允许用户访问该令牌允许的路由,服务和资源。单点登录是广泛使用JWT的一项功能,因为他开销小,在不同领域都能轻松使用。

  • 信息交换

由于JWT可以进行签名,可以用作各方之间的信息传输,此外,由于签名是使用标头和有效负载进行计算的,因此还可以验证内容是否被篡改。

3、为什么使用JWT

3.1 传统session认证

img

存在的问题

1、每个用户经过认证之后,都会在服务器做一次记录,以方便用户下次请求的鉴别,通常session都是保存在内存中,随着用户的不断增多,服务器的压力会变大。
2、传统session,对于分布式的应用并不适用,因为每个服务都用自己的session,并不会通用,这也导致了单点登录问题;再者如果部署服务集群,会限制了其相应的负载均衡能力。
3、session基于cookie来进行验证识别,如果cookie别截取,就很容易受到跨站伪造的攻击。
4、在前后端分离的项目中session会大大增加部署的复杂性,通常用户一次请求就要多次转发,如果用session,每次携带sessionid到服务器,服务器还要查询用户信息,在用户很多的情况下。这些信息存储在服务器内存中,给服务器增加负担。还有就是csrf(跨站伪造请求攻击)。再者,sessionid就是一个特征值,表达的信息不够丰富,不容易扩展,如果后端是多节点部署,就需要实现session的共享,不方便集群的部署。

在这里插入图片描述
在这里插入图片描述

3.2 JWT认证

  • 流程
img
  • 优势
1、 简介(Compact):可以通过URL,post参数或者在http请求头(Header)发送,数据量小,传输速度很快。
2、自包含(Self-contained):负载中包含了用户所需要的信息,避免了多次查询数据库。
3、token是以Json加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何形式的web都支持。
4、不需要在服务端保存会话信息,特别适用于分布式微服务。

4、JWT的结构

4.1、令牌(token)组成

  • 标头:Header
  • 有效负载:Payload
  • 签名:Signature
  • JWT是一个字符串String,样式为xxxx.yyyy.zzzz分别对应Header.Payload.Signature
  • 具体结构为:JWTString = Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)

4.2、Header

  • 由两部分组成:令牌的类型,即JWT和所使用的签名算法,如HMAC SHA256等。然后使用Base64编码组成JWT结构的第一部分
  • {
    “alg”:“HS256”,
    “typ”:“JWT”
    }

4.3、Payload(自包含)

  • 有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据,JWT指定七个默认字段供选择
  • 除以上默认字段外,我们还可以自定义私有字段,一般会把包含用户信息的数据放到payload中,如下例:
  • {
    “sub”: “1234567890”,
    “name”: “Helen”,
    “admin”: true
  • }
  • 同样的,他也会使用Base64编码组成JWT的第二部分。
  • 默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中,以防止信息泄露。JWT只是适合在网络中传输一些非敏感的信息

4.4、Signature

  • 签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名:
  • HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)
  • 注意JWT每部分的作用,在服务端接收到客户端发送过来的JWT token之后:
  • header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据.
  • signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,secretKey实际上代表的是盐值.

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象:

在这里插入图片描述

5、基本使用

  • 导坐标

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.18.2</version>
    </dependency>
    
  • 编写测试类

    @Test
    void test1() {
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND, 10);
        Map<String, Object> map = new HashMap<>();
        String token = JWT.create()
            .withHeader(map)  //使用默认的header,可以不写这一步
            .withClaim("userId", 10) //payload
            .withClaim("username", "zhangsan")
            .withExpiresAt(instance.getTime()) //指定token过期时间
            .sign(Algorithm.HMAC256("$%QWEwe14"));//签名
        System.out.println(token);
    }
    

在这里插入图片描述

  • 获取token相关内容

    @Test
    void test2(){
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256("$%QWEwe14")).build();
        DecodedJWT verify = 	verifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTM1NDIxNDMsInVzZXJJZCI6MTAsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.pI0ohIUYdODqQ80GcH63rdngiJGP4IiB6Bjh9t6iO-s");
        System.out.println(verify.getClaim("userId").asInt());
        System.out.println(verify.getClaim("username").asString());
        System.out.println("过期时间:"+verify.getExpiresAt());
    }
    

    异常信息:
    在这里插入图片描述

    SignatureVerificationException 签名不一致异常

    TokenExpiredException 令牌过期异常

    AlgorithmMismatchException 算法不匹配异常

    InvalidClaimException 失效的payload异常

6、Springboot整合JWT

  • 导坐标

    <!--jwt-->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.18.2</version>
    </dependency>
    <!--  mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.9</version>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    
  • 编写jwt工具类

    public class JWTUtils {
    
        private static final String SIGN = "QW!@#as17$&#";
    
        /**
         * 获取token
         *
         * @param map payload
         * @return
         */
        public static String getToken(Map<String, String> map) {
    
            //设置过期时间
            Calendar instance = Calendar.getInstance();
            instance.add(Calendar.DATE, 7); //7 天过期
            //创建jwt builder
            JWTCreator.Builder builder = JWT.create();
    
            //payload
            map.forEach((k, v) -> {
                builder.withClaim(k, v);
            });
    
            //设置过期时间
            String token = builder.withExpiresAt(instance.getTime())
                    .sign(Algorithm.HMAC256(SIGN));
            return token;
        }
    
        /**
         * 验证token
         *
         * @param token
         * @return jwt信息
         */
        public static DecodedJWT verify(String token) {
            return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
        }
    
    }
    
  • jwt拦截器

    public class JWTInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String token = request.getHeader("token");
            Map<String, Object> map = new HashMap<>();
            try {
                JWTUtils.verify(token); //验证token
                return true;
            } catch (SignatureVerificationException e) {
                map.put("msg", "签名异常");
                e.printStackTrace();
            } catch (TokenExpiredException e) {
                map.put("msg", "token过期");
                e.printStackTrace();
            } catch (AlgorithmMismatchException e) {
                map.put("msg", "算法不匹配");
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
                map.put("msg", "token无效");
            }
            map.put("status", false);
            //map->json
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(map);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().print(json);
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
    }
    
  • 配置JWTConfig

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
    
            registry.addInterceptor(new JWTInterceptor())
                    .addPathPatterns("/**") //拦截
                    .excludePathPatterns("/user/**"); //放行
        }
    }
    
  • 测试

    @RestController
    @Slf4j
    public class UserController {
    
        @Autowired
        private UserService userService;
    
    
        @RequestMapping("/user/login")
        public Map<String, Object> login(User user) {
            log.info("用户名:{}", user.getUsername());
            log.info("密码:{}", user.getPassword());
            Map<String, Object> map = new HashMap<>();
            try {
                User loginUser = userService.login(user);
                //payload
                Map<String, String> payload = new HashMap<>();
                payload.put("id", loginUser.getId());
                payload.put("username", loginUser.getUsername());
    
                //生成JWT token
                String token = JWTUtils.getToken(payload);
                map.put("status", true);
                map.put("msg", "登陆成功");
                map.put("token", token);
            } catch (Exception e) {
                map.put("status", false);
                map.put("msg", e.getMessage());
            }
            return map;
        }
    
        @RequestMapping("/test")
        public Map<String, Object> test(HttpServletRequest request) {
            String token = request.getHeader("token");
            //处理业务逻辑
            DecodedJWT verify = JWTUtils.verify(token);
            log.info("userId:{}",verify.getClaim("id"));
            log.info("username:{}",verify.getClaim("username"));
    
            Map<String, Object> map = new HashMap<>();
            map.put("status", true);
            map.put("msg", "请求成功");
            return map;
        }
    
    }
    

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot整合JWT是一种在SpringBoot框架中使用JWT(JSON Token)来设计用户认证和授权系统方法。JWT是一种在Web应用中实现单点登录的认证方式。[2] 为了实现SpringBoot整合JWT,我们需要引入java-jwt库,并使用其中的JWT.create()方法来生成令牌。我们可以在令牌中设置自定义的用户名和过期时间,并通过Algorithm.HMAC256()方法设置令牌的签名保密复杂度。最终生成的令牌可以通过System.out.println()方法输出。 JWT是一种具体实现方式,它是JSON Web Token的缩写。可以通过JWT官网获取更多关于JWT的信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [SpringbootJWT](https://blog.csdn.net/weixin_51146329/article/details/123620553)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [SpringBoot + jwt 详解+使用案例](https://blog.csdn.net/zkcJava/article/details/119935284)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值