目录
  • 1、概述
  • 2、优势所在
  • 3、结构组成
    • 3.1、标头(Header)
    • 3.2、有效负载(Payload)
    • 3.3、签名(Signature)
  • 4、Spring boot整合JWT 导入依赖

    1、概述

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

    Spring Boot详解整合JWT教程

    2、优势所在

    在JavaWeb阶段,经常使用session来存储,以方便用来判断用户是否操作等等。但这又恰巧暴露了一个问题,用session存储是需要占用服务器内存的。当用户只有一两个的时候没什么问题,但是当用户成千上万的话,服务器就很难招架得住了。并且session是基于cookie来实现的,因此数据很容易被截获,遭遇CSRF跨域伪造攻击,因此不太安全。这时JWT的优势就突显出来的:

    • 简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
    • 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
    • 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
    • 不需要在服务端保存会话信息,特别适用于分布式微服务。

    3、结构组成

    JWT 中的 token 组成主要有三部分:

    • 标头(Header)
    • 有效载荷(Payload)
    • 签名(Signature)

    3.1、标头(Header)

    标头通常由两部分组成:令牌的类型 ( 即JWT ) 和所使用的签名算法,例如HMAC SHA256(默认)或RSA。它会使用Base64编码组成JWT结构的第一部分。值得注意的是,Base64是一种编码,也就是说,它是可以被翻译回原来的样子的,它并不是一种加密过程。

    {
      "alg": "HS256",
      "typ": "JWT"
    }

    3.2、有效负载(Payload)

    有效负荷是包含声明,通常将需要传递的数据存放在Payload中,同样的是使用Base64编码,但这同时说明这一块是可以被反编译的,这是什么意思呢?就是太敏感的信息存放在这里的话存在被捕获的可能,因此官方推荐在Payload中不要存放敏感的信息,例如女生的年龄。

    3.3、签名(Signature)

    签名是由编码后的标头和有效负荷以及我们提供的一个密钥,然后使用标头规定的签名算法进行签名。因此,签名的主要作用是保证JWT没有被篡改过,保证 token 的合法性。

    这里特别注意的是,密钥 secret 是验证 token 是否合法的重要凭证,如果用于验证的密钥被外界所获取到的话,我们所建立的验证防线将如同虚设!

    4、Spring boot整合JWT 导入依赖

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.19.0</version>
    </dependency>
    

    编写JwtUtils,提高代码的重用率

    public class JwtUtils {
        /** 过期时间,单位:秒,默认半小时过期 **/
        private static final long EXPIRATION = 60 * 30L;
        /** 密钥,一般长度较长,内容较复杂 **/
        private static final String SECRET = "my_secret";
        /**
         * @description 创建token
         * @author xBaozi
         * @date 20:49 2022/3/31
         **/
        public static String createToken(Map<String, String> claimMap) {
            // 当前时间戳加上设定的毫秒数(1秒 == 1000毫秒)
            Date expiration = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
            // 设置JWT头部
            Map<String, Object> map = new HashMap<>();
            map.put("alg", "HS256");
            map.put("typ", "JWT");
            // 创建token
            JWTCreator.Builder builder = JWT.create();
            //使用Lambda创建payload
            claimMap.forEach((k,v)->{
                builder.withClaim(k,v);
            });
            // 添加头部,可省略保持默认,默认即map中的键值对
            return builder.withHeader(map)
                    // 设置过期时间
                    .withExpiresAt(expiration)
                    // 设置签名解码算法
                    .sign(Algorithm.HMAC256(SECRET));
        }
        /**
         * @description 验证token
         * @author xBaozi
         * @date 23:36 2022/3/31
         **/
        public static DecodedJWT verifyToken(String token) {
            return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
        }
    }

    编写过滤器,这是为了使得不用在每一个controller前端控制器都写一遍获取token,将token放在请求体中请求,从而简化开发操作

    public class JwtInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Map<String, Object> map = new HashMap<>();
            //获取请求头中令牌
            String token = request.getHeader("token");
            try {
                //验证令牌
                JwtUtils.verifyToken(token);
                //验证成功,放行请求
                return true;
            } catch (SignatureVerificationException e) {
                e.printStackTrace();
                map.put("msg", "无效签名!");
            } catch (TokenExpiredException e) {
                e.printStackTrace();
                map.put("msg", "token过期!");
            } catch (AlgorithmMismatchException e) {
                e.printStackTrace();
                map.put("msg", "token算法不一致!");
            } catch (Exception e) {
                e.printStackTrace();
                map.put("msg", "token无效!!");
            }
            //设置状态
            map.put("state", false);
            //将map转为json
            String json = new ObjectMapper().writeValueAsString(map);
            // 相应json数据
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(json);
            return false;
        }
    }

    配置过滤器

    @Configuration
    public class JwtConfig implements WebMvcConfigurer{
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(new JwtInterceptor())
                        //添加拦截路径
                        .addPathPatterns("/**")
                        //添加放行路径
                        .excludePathPatterns("/user/login");
            }
    }

    编写前端控制器,这里以两个为例

    @RestController
    @RequestMapping("/user")
    @Slf4j
    public class UserController {
        // 这里只做演示,就不写Service层了
        // @Autowired
        // private UserService userService;
        /**
         * @description 登录功能
         * @author xBaozi
         * @date 0:02 2022/4/1
         * @param user  user对象,默认有值
         **/
        @GetMapping("/login")
        public Map<String,Object> login(User user){
            log.info("用户名: [{}]",user.getName());
            log.info("密码: [{}]",user.getPassword());
            Map<String, Object> map = new HashMap<>();
            try{
                Map<String,String> payload =  new HashMap<>();
                payload.put("id",user.getId());
                payload.put("name",user.getName());
                // 生成JWT的令牌
                String token = JwtUtils.createToken(payload);
                map.put("state",true);
                map.put("msg","认证成功");
                // 响应token
                map.put("token",token);
            }catch (Exception e){
                map.put("state",false);
                map.put("msg",e.getMessage());
            }
            return map;
        }
        @PostMapping("/other")
        public Map<String,Object> test(HttpServletRequest request){
            Map<String, Object> map = new HashMap<>();
            //处理自己业务逻辑
            String token = request.getHeader("token");
            DecodedJWT verify = JwtUtils.verifyToken(token);
            log.info("用户id: [{}]",verify.getClaim("id").asString());
            log.info("用户name: [{}]",verify.getClaim("name").asString());
            map.put("state",true);
            map.put("msg","请求成功!");
            return map;
        }
    }

    postman测试

    Spring Boot详解整合JWT教程

    Spring Boot详解整合JWT教程

    IDEA控制台查看信息

    Spring Boot详解整合JWT教程

    搞完收工,这里的token交由前端进行管理保存!!!溜了~

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。