Java安全—SpringBoot&JWT身份权鉴&打包部署

前言

今天讲SpringBoot的最后一个漏洞点,就是JWT(JSON Web Token)身份权鉴。这是Java特有的一种身份鉴别技术,类似于PHP中的Cookie和Session。

什么是JWT

从这张图可以看出来浏览器通过POST请求发送账号和密码到服务端,服务端创建一个JWT并且返回给浏览器。浏览器访问某些页面时通过发送JWT到服务端,服务端检验JWT的合法性,返回对应的功能或者页面。

JWT(JSON Web Token)是由服务端用加密算法对信息签名 来保证其完整性和不可伪造;

Token里可以包含所有必要信息,这样服务端就无需保存任何关于用户或会话的信息;

JWT用于身份认证、会话维持等。由三部分组成,header、payload与signature。

Header(头部): JWT 的头部通常包含两部分信息:声明类型(typ)和使用的签名算法(alg) 。这些信息以 JSON 格式存在,然后进行 Base64 编码,形成 JWT 的第一个部分。头部用于描述关于该 JWT 的元数据信息。

复制代码
{
  "alg": "HS256",
  "typ": "JWT"
}

Payload(负载): JWT 的负载包含有关 JWT 主题(subject)及其它声明的信息。与头部一样,负载也是以 JSON 格式存在,然后进行 Base64 编码,形成 JWT 的第二个部分。

复制代码
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Signature(签名): JWT 的签名是由头部、负载以及一个密钥生成的,用于验证 JWT 的真实性和完整性。签名是由指定的签名算法对经过 Base64 编码的头部和负载组合而成的字符串进行签名生成的。

例如,使用 HMAC SHA-256 算法生成签名:

复制代码
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

项目搭建

新建一个项目名为Java-demo。

依赖选择。

这里要引入一下依赖,引入3.4.0版本的JWT。

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

安全问题

创建一个Java类叫JWTcontroller,并且写入以下代码。

复制代码
package com.sf.maven.jwtdemo.demos.web;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

public class JWTcontroller {

    public static void main(String[] args) {
       String JWTstring = JWT.create()
                //创建header部分
                //.withHeader()

                //创建payload部分
                .withClaim("userid",1)
                .withClaim("username","admin")
                .withClaim("password","123456")

                //创建signature,并且设置签名算法为HMAC256,密钥为wlwnb
                .sign(Algorithm.HMAC256("wlwnb"));
       System.out.println(JWTstring);
    }
}

直接运行一下代码,看看输出的JWT是啥样子的,可以看到生成了一个JWT数据。

这个JWT数据被两个点隔成三部分,分别是

Header:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

payload:eyJwYXNzd29yZCI6IjEyMzQ1NiIsInVzZXJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiJ9

**Signature:**C2bW9w4bU3UGFTCpf9x247-DqUeso9TbIB6KeGzmJdM

我们拿到JWT解密网站去看看,可以看到我们解密的payload和刚刚我们设置的对得上。

JSON Web Tokens - jwt.io

有人可能说那么我把admin改为其它的用户不就可以实现任意用户登录了?其实我们往下可以看到签名这里显示密钥为空,你修改后加密出来的JWT是不可被服务端识别的,除非你知道加密的密钥。

直接做个实验就懂了,我们先写一个表单提交的页面,在resource/static/index.html里面写入以下代码,创建一个登录提交表单。

复制代码
<form action="../jwtcreate" method="post">
    id:<input type="text" name="id"><br>
    user:<input type="text" name="username"><br>
    password:<input type="text" name="password"><br>
    <input type="submit" value="create">
</form>

接着我们在创建JWT的代码里面添加一下解密JWT的代码,并且提取数据。

复制代码
package com.sf.maven.jwtdemo.demos.web;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class JWTcontroller {
    //创建JWT
    @PostMapping("/jwtcreate")
    @ResponseBody
    public static String main(Integer id,String username,String password) {
       String JWTstring = JWT.create()
                .withClaim("userid",id)
                .withClaim("username",username)
                .withClaim("password",password)
                .sign(Algorithm.HMAC256("wlwnb"));
       System.out.println(JWTstring);
      return JWTstring;

    }

    //模拟用户身份检测,JWT数据解密
    @PostMapping("/JWTcheck")
    @ResponseBody
    public static void JWTcheck(String JWTdata) {
        //构建解密注册
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256("wlwnb")).build();
        //解密注册数据
        DecodedJWT jwt = verifier.verify(JWTdata);
        //提取注册解密数据,
        Integer userid = jwt.getClaim("userid").asInt();
        String username = jwt.getClaim("username").asString();
        String password = jwt.getClaim("password").asString();
        System.out.println(userid+" "+username+" "+password);
        //获取头部数据
        //jwt.getHeader();
        //获取签名数据
        //jwt.getSignature();

    }

}

运行JwtDemoApplication,部署页面,可以看到这是我们刚刚写的表单页面。

填入数据点击create,可以看到返回了生成的JWT。

现在我们来模拟攻击者,先在原有的代码上添加一段判断的代码。

接着在index页面写个JWT数据的提交表单,使其可以解密JWT数据。

运行代码访问页面,把生成的JWT数据提交解密。

可以看到如果是admin用户的话就会返回you are admin。

如果不是admin,返回如下。

OK,那么我们现在来做个实验,可以看到我们现在JWT解密的payload为wlwnb。

那么我们现在把wlwnb改为admin,再在这个网站加密成JWT数据去提交,看看是否返回admin页面,可以看到是返回了一个报错页面。

这是为啥呢,其实上面我们讲过了,就是没有密钥的问题,你没有密钥那么加密出来的数据代码这边就无法解密,自然就是返回报错页面。

也就是说JWT不需要考虑你的账号密码正确性,只需看你传过来的JWT是否能解密即可,因为你不知道密钥,伪造的数据无法解密直接报错。我们添加密钥再去伪造admin的JWT的数据试试。

结果是代码判定我们是admin,这里更加验证了我们上面的说法。所以JWT的安全问题主要是算法泄露或者在开发的时候alg设置为"none",这就意味着不用算法对数据进行加密,那么此时没有密钥也可以解密。

打包部署

这个其实没啥可说的,就是顺便讲一下Java的项目该如何在服务器上面部署。其实是非常简单,就是把项目打包成jar包,再放到服务器上面运行即可。

来到我们Maven这里,点击生命周期,点击clean,清除一下缓存。

接着再点击package。

此时在目录这里便生成了一个jar包。

总结

基本关于SpringBoot的知识点都讲完了,后续会讲一下该如何利用,Java安全估计还得要十几篇文章。

最后,以上仅为个人的拙见,如何有不对的地方,欢迎各位师傅指正与补充,有兴趣的师傅可以一起交流学习。

相关推荐
linweidong4 小时前
中科闻歌Java面试题及参考答案
java·python·大模型·提示词·ai agent·mcp·rag原理
OpenAnolis小助手4 小时前
一句话看透 JVM,SysOM 诊断 Skill 新增 Java 应用诊断能力
java·开发语言·jvm·阿里云·操作系统控制台·sysom
黎阳之光4 小时前
数字孪生赋能全域水网,实现水资源管控与节水降碳双向提升
人工智能·物联网·算法·安全·数字孪生
ForteScarlet4 小时前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
萧瑟余晖4 小时前
Java深入解析篇六十二之安全机制详解
java·开发语言
福大大架构师每日一题5 小时前
mediamtx v1.21.0 发布:Media-over-QUIC、RTSP、HLS、WebRTC 与安全能力全面升级
安全·webrtc
xian_wwq5 小时前
【学习笔记】OWASP Agentic 应用安全(二)
笔记·学习·安全·owasp
小刘在重生~5 小时前
Java 常用类|包装类 装箱拆箱、Integer 缓存面试题
java·python·缓存
Orange_sparkle5 小时前
Pi Agent vs LangGraph:从人机交互、企业 RAG 到持久化工作流的完整选型指南
java·网络·人机交互
前端精髓6 小时前
NestJS 是什么(对着 Spring Boot 一起学习)
spring boot·后端·学习