初识Jwt(结合SpringBoot)

最近接触JWT,顺便记录下

目录

JWT简介

JWT是JSON Web Token的简称,是目前流行的跨域的认证解决方案,作为传递信息的凭证,它是由服务器端签发的且是带签名的,服务器端通过数字签名来保证数据的完整性和有效性。

JWT组成

Jwt由三部分组成: Header(头部)、Payload(负载)、Signature(签名)

这里简单讲解下吧,其实已经很多博主都讲了的,我这边就...照搬~
头部(Header) :JWT 的头部通常由两部分信息组成:令牌的类型(即JWT)和所使用的签名算法,例如:{ "alg": "HS256", "typ": "JWT"}
载荷(Payload) :JWT的载荷包含了一些声明(Claim),用于描述用户信息、权限、过期时间等等,例如:{ "name": "zhang", "userid": "123"}
签名(Signature):JWT的签名由头部和载荷组成,并使用密钥进行加密生成

JWT使用流程

基于系统登陆 :用户使用用户名和密码进行登录,服务器验证用户信息是否正确。

1、服务器通过JWT生成token,将用户信息、权限等信息写入载荷中,并使用密钥对头部和载荷进行签名。

2、服务器将生成的token返回给客户端,客户端将其存储在本地,通常是在浏览器的cookie或本地存储中。

3、客户端在后续的请求中,将token作为请求头部或请求参数传递给服务器。

4、服务器收到请求后,验证token的签名是否正确,如果正确则解析出用户信息、权限等信息,进行后续操作。
基于对接验证 :第三方对外接口时可使用JWT生成的token作为验证凭证(有时间限制)

1、请求第三方服务器,将第三方授权信息等信息写入载荷中,并使用第三方提供的密钥对头部和载荷进行签名。

2、第三方将生成的token返回给请求方,请求方将token存储至数据库或缓存,可设置自动失效或失效后自动获取token

3、请求方在后续的请求接口中,将token作为请求头部或请求参数传递给第三方。

4、第三方收到请求后,验证token的签名是否正确,如果正确则解析出授权信息进行验证,若无误再进行后续操作

JWT实战

引入Maven

复制代码
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

核心代码

Jwt工具类

java 复制代码
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;

public class JwtUtils {
    //密钥(自定义)
    private static String secret = "JwtTest";
    //过期时间(分钟)
    private static int expireMinutes = 10;

    //创建Token
    public static String getToken(Map<String,String> map){

        Calendar instance = Calendar.getInstance();
        //获取具体失效时间
        instance.add(Calendar.SECOND,60 * expireMinutes);

        JWTCreator.Builder builder = JWT.create();
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        //设置过期时间
        builder.withExpiresAt(instance.getTime());

        return builder.sign(Algorithm.HMAC256(secret));
    }

    //验证Token
    public static void isVerify(String token){
        DecodedJWT jwt = JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
    }
}

拦截器(实现登录后验证获取信息)

java 复制代码
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhangximing.springboot_jwt.util.JwtUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;

/**
 * JWT拦截器
 */
public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HashMap<String, Object> map = new HashMap<>();
        System.out.println("JWT拦截器拦截到请求");
        //获取请求头中的token
        String token = request.getHeader("token");
        try {
            //验证令牌
            JwtUtils.isVerify(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","算法不一致");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg","token无效");
        }
        map.put("state",false);

        // 将map转为json,响应给前端
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}
java 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 拦截器配置
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/**")
                //除登陆方法外其他路径都拦截
                .excludePathPatterns("/user/login");
    }

    /**
     * 如果实现了Filter跨域拦截,这个跨域无效
     * 拦截器实现 跨域支持
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")  //本人测试时springboot2.7版本用的是这个
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT","OPTIONS","HEAD")
                .allowedHeaders("*")
                .maxAge(3600);
    }
}

测试方法

java 复制代码
import com.zhangximing.springboot_jwt.util.JwtUtils;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/user")
public class UserController {

    //测试验证
    @RequestMapping("/test")
    public Map<String,Object> test(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("state",true);
        map.put("msg","请求成功");

        return map;
    }

    //用户登录,认证token
    @RequestMapping("/login")
    public Map<String,Object> login(@RequestBody Map<String,Object> paramMap){
        HashMap<String, Object> map = new HashMap<>();
        try {
            //模拟登陆
            Map<String, String> loginInfo = MyLogin((String) paramMap.get("userName"), (String) paramMap.get("password"));

            //登陆成功后,返回token
            Map<String,String> payload = new HashMap<>();
            payload.put("userId",loginInfo.get("userId"));
            payload.put("userName",loginInfo.get("userName"));
            //生成JWT令牌
            String token = JwtUtils.getToken(payload);
            //token返回
            map.put("state",true);
            map.put("msg","认证成功");
            map.put("token",token);
        } catch (Exception e) {
            map.put("state",false);
            map.put("msg",e.getMessage());
        }
        return map;
    }

    //模拟登陆
    public Map<String,String> MyLogin(String userName,String password){
        if ("zhangximing".equals(userName) && "123456".equals(password)){
            Map<String,String> map = new HashMap<>();
            map.put("userId","1");
            map.put("userName","zhangximing");

            return map;
        }else{
            throw new RuntimeException("用户名或密码错误");
        }
    }
}

测试结果(模拟登陆、正常登陆、登陆失效)


JWT优缺点

引用一个博主的总结 https://blog.csdn.net/wingrant/article/details/126445880

JWT优点:

1、因为json的通用性,JWT是可以跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等语言都能使用。

2、因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。

3、便于传输。JWT的构成非常简单,字节占用很小,所以它是非常便于传输的。 它不需要在服务端保存会话信息, 所以它易于应用的扩展。
JWT缺点:

1、占带宽: 正常情况下要比 session_id 更大,需要消耗更多流量,挤占更多带宽,假如你的网站每月有 10万次的浏览器,就意味着要多开销几十兆的流量。听起来并不多,但日积月累也是不小一笔开销。实际上,许多人会在 JWT 中存储的信息会更多。

2、无法在服务端注销。用户主动注销时一般会让前端清理token,后端不理会,很难解决劫持问题。其他解决办法都是有状态的,例如通过Redis存储token副本、Redis存储token版本号、Redis存储过期时间等。

3、性能问题: JWT 的卖点之一就是加密签名,由于这个特性,接收方得以验证 JWT 是否有效且被信任。但是大多数 Web身份认证应用中,JWT 都会被存储到 Cookie中,这就是说你有了两个层面的签名。听着似乎很牛逼,但是没有任何优势,为此,你需要花费两倍的 CPU 开销来验证签名。对于有着严格性能要求的Web 应用,这并不理想,尤其对于单线程环境。

相关推荐
今天背单词了吗980几秒前
算法学习笔记:8.Bellman-Ford 算法——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·开发语言·后端·算法·最短路径问题
天天摸鱼的java工程师3 分钟前
使用 Spring Boot 整合高德地图实现路线规划功能
java·后端
东阳马生架构19 分钟前
订单初版—2.生单链路中的技术问题说明文档
java
咖啡啡不加糖33 分钟前
暴力破解漏洞与命令执行漏洞
java·后端·web安全
风象南36 分钟前
SpringBoot敏感配置项加密与解密实战
java·spring boot·后端
DKPT1 小时前
Java享元模式实现方式与应用场景分析
java·笔记·学习·设计模式·享元模式
Percep_gan1 小时前
idea的使用小技巧,个人向
java·ide·intellij-idea
缘来是庄1 小时前
设计模式之迭代器模式
java·设计模式·迭代器模式
Liudef061 小时前
基于HTML与Java的简易在线会议系统实现
java·前端·html
JosieBook1 小时前
【Java编程动手学】Java常用工具类
java·python·mysql