IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot整合JWT -> 正文阅读

[Java知识库]SpringBoot整合JWT

JWT简介

JSON Web Token(JWT)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象安全传输信息。 这些信息可以通过数字签名进行验证和信任。 可以使用秘密(使用HMAC算法)或使用RSA的公钥/私钥对对JWT进行签名。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,该token也可直接被用于认证,也可被加密。是目前最流行的跨域认证解决方案。

JWT优点

  • 体积小,传输速度更快
  • 多样化的传输方式,可以通过URL传输、POST传输、请求头Header传输(常用)
  • 简单方便,载荷包含有关用户的所有必需信息,服务端拿到jwt后无需再次查询数据库校验token可用性,避免了多次查询数据库。
  • 在分布式系统中,很好地解决了单点登录问题
  • 很方便的解决了跨域授权问题,因为跨域无法共享cookie

JWT结构

jwt有3个组成部分,每部分通过点号来分割 header.payload.signature

  • 头部(header) 是一个 JSON 对象,描述 JWT 的元数据,
  • 载荷(payload) 是一个 JSON 对象,用来存放实际需要传递的数据
  • 签证(signature) 是对header和payload使用密钥进行签名,防止数据篡改。

【Header】

Jwt的头部是一个JSON,然后使用Base64URL编码,承载两部分信息:

  1. 标头通常由两部分组成: 令牌的类型(即JWT)和所使用的签名算法,例如HMAC SHA256或RSA。它会使用Base64编码组成JWT结构的第一部分
  2. 注意:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。
"alg" : "HS256",
"typ" : "JWT"
  • 声明类型typ,表示这个令牌(token)的类型(type),JWT令牌统一写为JWT
  • 声明加密的算法alg,通常直接使用HMACSHA256,就是HS256了,也可以使用RSA,支持很多算法(HS256、HS384、HS512、RS256、RS384、RS512、ES256、ES384、ES512、PS256、PS384)
    var header = Base64URL({ “alg”: “HS256”, “typ”: “JWT”})

Base64URL:Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

【Payload】载荷
payload即有效负载,也是一个JSON字符串,是承载消息具体内容的地方,也需要使用Base64URL编码

"sub" : "1234578",
"name" : "John Doe"
"admin" : true

payload中可以包含预定义的7个可用,它们不是强制性的,但推荐使用,也可以添加任意自定义的key

iss(issuer): jwt签发者
sub(subject): jwt所面向的用户
aud(audience): 接收jwt的一方, 受众
exp(expiration time): jwt的过期时间,这个过期时间必须要大于签发时间
nbf(Not Before): 生效时间,定义在什么时间之前.
iat(Issued At): jwt的签发时间
jti(JWT ID): jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

// 该token签发给1234567890,姓名为John Doe(自定义的字段),签发时间为1516239022

var payload = Base64URL( {“sub”: “1234567890”, “name”: “John Doe”, “iat”: 1516239022})

注意:JWT中payload是不加密的,只是Base64URL编码一下,任何人拿到都可以进行解码,所以,在负载里面不要加入任何敏感的数据!!!

【Signature】签名

Signature 部分是对前两部分的签名,防止数据篡改。前面两部分都是使用Base64进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header和 payload以及我们提供的一个密钥,然后使用header 中指定的签名算法(HS256)进行签名。签名的作用是保证JWT没有被篡改过,如:

HMACSHA256(base64UrlEncode(header) + "." + base64Ur1Encode(payload) , secret) ;
var header = Base64URL({ "alg": "HS256", "typ": "JWT"});
var payload = Base64URL( {"sub": "1234567890", "name": "John Doe", "iat": 1516239022});
var secret = "私钥";
var signature = HMACSHA256(header + "." + payload, secret);
var jwt = header + "." + payload + "." + signature;

注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。

签名实际上是对头部以及负载内容进行签名,防止内容被窜改。如果有人对头部以及负载的内容解码之后进行修改,再进行编码,最后加上之前的签名组合形成新的JWT的话,那么服务器端会判断出新的头部和负载形成的签名和JWT附带上的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。

SpringBoot集成JWT

项目结构

在这里插入图片描述

添加Maven依赖

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

1. 创建JWT工具类

创建操作JWT的工具类

@Service
public class JwtUtil {

    /**
     * 生成token
     */
    public static String createToken(User user) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 7); //默认令牌过期时间7天
        JWTCreator.Builder builder = JWT.create();
        builder.withClaim("uid", user.getId())
                .withClaim("username", user.getUsername())
                .withClaim("role", user.getRole())
                .withClaim("permission", user.getPermission());

        return builder.withExpiresAt(calendar.getTime())
                .sign(Algorithm.HMAC256(user.getPassword()));
    }

    /**
     * 解析token
     */
    public static DecodedJWT verifyToken(String token) {
        if (token==null){
            System.out.println("token不能为空");
        }
        //获取登录用户真正的密码假如数据库查出来的是123456
        String password = "admin";
        JWTVerifier build = JWT.require(Algorithm.HMAC256(password)).build();
        return build.verify(token);
    }
}

2. 创建JWT拦截器

/**
 * @Description: token 拦截器
 */
@Slf4j
public class JWTInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("token");
        log.info("token:" + token);

        if (token == null) {
            log.error("token为空");
        }
        try {
            JwtUtil.verifyToken(token);
        } catch (SignatureVerificationException e) {
            log.error("无效签名");
            return false;
        } catch (TokenExpiredException e) {
            log.error("token过");
            return false;
        } catch (AlgorithmMismatchException e) {
            log.error("token算法不一致");
            return false;
        } catch (Exception e) {
            log.error("token无效");
            return false;
        }
        return true;
    }
}

3. 创建JWT配置类

IntercaptorConfig

package com.demo.jwt.config;

import com.demo.jwt.jwt.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


/**
 * @Description: JWT拦截器
 * @Date: 2022/4/5 17:30
 * @Author: Yang Yezhuang
 */
@Configuration
public class IntercaptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                //拦截的路径
                .addPathPatterns("/info/*", "/test")

                //排除接口
                .excludePathPatterns("/", "/login", "/register");
    }
}

4. 创建测试类

controller

@Slf4j
@RestController
public class UserController {

    @Autowired
    UserService userService;

    @PostMapping("/login")
    public String login(@RequestBody User user) {
        // 查询数据库用户名,密码
        User u = userService.login(user.getUsername());
        // 判断用户名,密码是否正确
        if (user.getUsername().equals(u.getUsername()) & user.getPassword().equals(u.getPassword())) {
            String token = JwtUtil.createToken(u);
            return token;
        } else {
            return "用户名或密码错误";
        }
    }

    @GetMapping("/info/{username}")
    public User info(@PathVariable("username") String username) {
        User u = userService.login(username);
        log.info(u.toString());
        return u;
    }

    @GetMapping("/test")
    public String info(HttpServletRequest request) {
        String token = request.getHeader("token");
        log.info("This is test + " + token);
        return "This is test";
    }
}


感谢大家的耐心阅读,如有建议请私信或评论留言
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:04:04  更:2022-04-06 16:04:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:23:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码