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";
    }
}


感谢大家的耐心阅读,如有建议请私信或评论留言
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值