初识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 应用,这并不理想,尤其对于单线程环境。

相关推荐
Buleall6 分钟前
期末考学C
java·开发语言
重生之绝世牛码8 分钟前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行14 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
Allen Bright21 分钟前
Spring Boot 整合 RabbitMQ:手动 ACK 与 QoS 配置详解
spring boot·rabbitmq·java-rabbitmq
新手小袁_J38 分钟前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11
呆呆小雅39 分钟前
C#关键字volatile
java·redis·c#
Monly2139 分钟前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
Ttang2342 分钟前
Tomcat原理(6)——tomcat完整实现
java·tomcat
goTsHgo43 分钟前
在 Spring Boot 的 MVC 框架中 路径匹配的实现 详解
spring boot·后端·mvc
钱多多_qdd1 小时前
spring cache源码解析(四)——从@EnableCaching开始来阅读源码
java·spring boot·spring