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安全估计还得要十几篇文章。

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

相关推荐
小万编程15 分钟前
【2025最新计算机毕业设计】基于SSM的医院挂号住院系统(高质量源码,提供文档,免费部署到本地)【提供源码+答辩PPT+文档+项目部署】
java·spring boot·毕业设计·计算机毕业设计·项目源码·毕设源码·java毕业设计
白宇横流学长16 分钟前
基于Java的银行排号系统的设计与实现【源码+文档+部署讲解】
java·开发语言·数据库
123yhy传奇16 分钟前
【学习总结|DAY027】JAVA操作数据库
java·数据库·spring boot·学习·mybatis
code2roc19 分钟前
SpringBoot集成ECDH密钥交换
spring boot·ecdh·密钥交换·密钥协商
想要打 Acm 的小周同学呀21 分钟前
亚信科技Java后端外包一面
java·求职·java后端
lishiming03084 小时前
TestEngine with ID ‘junit-jupiter‘ failed to discover tests 解决方法
java·junit·intellij-idea
HEU_firejef4 小时前
设计模式——工厂模式
java·开发语言·设计模式
Kobebryant-Manba4 小时前
单元测试学习2.0+修改私有属性
java·单元测试·log4j
fajianchen4 小时前
应用架构模式
java·开发语言