0%

区块链实训4

学习任务包括区块链中的公钥私钥以及钱包相关知识,以及java中ORM框架mybatis

第⼀部分:区块链中的公钥私钥以及钱包

使用java生成钱包地址公钥以及私钥

在gitee上拉取项目代码

1
git clone https://gitee.com/daitoulin/testmysql.git

首先需要在pom文件中引入开源包web3j(当然项目里已经引入了)

web3j包当中内置了生成钱包地址公钥私钥的算法,我们只需调用就可以很便捷的生成。

文件路径:testmysql\src\main\java\com\example\testmysql\utils\EthUtils.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.example.testmysql.utils;

import com.example.testmysql.entity.EthWallet;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Sign;
import org.web3j.crypto.Sign.SignatureData;

import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;

public class EthUtils {

public static void main(String[] args) {
try {
// 1. 生成公钥和私钥对
ECKeyPair keyPair = Keys.createEcKeyPair();
BigInteger privateKey = keyPair.getPrivateKey();
BigInteger publicKey = keyPair.getPublicKey();

System.out.println("Private Key: " + privateKey.toString(16));
System.out.println("Public Key: " + publicKey.toString(16));

// 2. 计算以太坊地址
String address = publicKeyToAddress(publicKey);
System.out.println("Address: " + address);

// 3. 签名和验证
String message = "Hello blockchain";
SignatureData signature = signMessage(message, privateKey);
System.out.println("Signature: " +
bytesToHex(signature.getR()) +
bytesToHex(signature.getS()) +
String.format("%02x", signature.getV()[0]) // 处理 byte 数组的第一个字节
);

String signatureStr = bytesToHex(signature.getR()) + bytesToHex(signature.getS()) + String.format("%02x", signature.getV()[0]);
System.out.println(signatureStr);

boolean isValid = verifySignature(message, signature, address);
System.out.println("Is signature valid: " + isValid);



} catch (Exception e) {
e.printStackTrace();
}
}

public static String getSignStr(SignatureData signature){
return bytesToHex(signature.getR()) + bytesToHex(signature.getS()) + String.format("%02x", signature.getV()[0]);
}

public static EthWallet getWallet() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException {
EthWallet wallet = new EthWallet();
ECKeyPair keyPair = Keys.createEcKeyPair();
BigInteger privateKey = keyPair.getPrivateKey();
BigInteger publicKey = keyPair.getPublicKey();
String address = publicKeyToAddress(publicKey);
wallet.setPrivateKey(privateKey.toString(16));
wallet.setPublicKey(publicKey.toString(16));
wallet.setAddress(address);
return wallet;
}

public static String publicKeyToAddress(BigInteger publicKey) {
return "0x" + Keys.getAddress(publicKey);
}


public static SignatureData signMessage(String message, BigInteger privateKey) throws Exception {
byte[] messageHash = Hash.sha3(message.getBytes());
ECKeyPair keyPair = ECKeyPair.create(privateKey);
return Sign.signMessage(messageHash, keyPair, false);
}

public static boolean verifySignature(String message, SignatureData signature, String address) {
try {
byte[] messageHash = Hash.sha3(message.getBytes());
BigInteger recoveredPublicKey = Sign.signedMessageHashToKey(messageHash, signature);
String keysAddress = publicKeyToAddress(recoveredPublicKey);
System.out.println("keysAddress: " + keysAddress);
return keysAddress.equals(address);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}


public static SignatureData stringToSignatureData(String signatureStr) {
// Ensure the string length is valid (r + s + v should be hex)
if (signatureStr.length() < 130) {
throw new IllegalArgumentException("Invalid signature string length.");
}

// Extract r, s, and v components
String rHex = signatureStr.substring(0, 64); // First 64 characters for r
String sHex = signatureStr.substring(64, 128); // Next 64 characters for s
String vHex = signatureStr.substring(128, 130); // Last 2 characters for v

// Convert hex to byte arrays
byte[] r = hexToBytes(rHex);
byte[] s = hexToBytes(sHex);
byte[] v = new byte[]{(byte) Integer.parseInt(vHex, 16)}; // Convert to byte

return new SignatureData(v, r, s);
}

// Helper method to convert hex string to byte array
public static byte[] hexToBytes(String hex) {
int length = hex.length();
byte[] data = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+ Character.digit(hex.charAt(i + 1), 16));
}
return data;
}

}

第二部分:数据库配置

MySQL安装

首先将压缩包解压到文件夹下(注意路径需要为全英文)

创建配置文件my.ini,写入以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[mysqld]
# 设置3306端⼝
port=3306
# 自定义设置mysql的安装目录,即解压mysql压缩包的目录
basedir=D:\mysql-8.0.20-winx64
# 自定义设置mysql数据库的数据存放目录
datadir=D:\mysql-8.0.20-winx64\data
# 允许最大连接数
max_connections=1000
# 允许连接失败的次数,这是为了防止有人从该主机试图攻击数据库系统
max_connect_errors=10
# 服务端使用的字符集默认为UTF8
character-set-server=utf8mb4
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 默认使用“mysql_native_password”插件认证
default_authentication_plugin=mysql_native_password
# 去除mysql8对ONLY_FULL_GROUP_BY的限制
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISI
[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8mb4
[client]
# 设置mysql客户端连接服务端时默认使用的端⼝和默认字符集
port=3306
default-character-set=utf8mb4

进入管理员模式的命令行后,移动到bin路径下

初始化mysql,得到默认密码 .rcnizekl4Jt

1
mysqld --initialize --console

进行mysql服务安装

1
mysqld --install "MySQL" --defaults-file="D:\work\mysql\my.ini"

不过启动不成功,先把刚刚安装的删了,修改下安装命令即可

1
2
mysqld --remove "MySQL"
mysqld --install

我们进行登录

1
mysql -u root -p .rcnizekl4Jt

提示需要更改密码,我们重置一下

1
ALTER USER 'root'@'localhost' IDENTIFIED BY 'root';

最后配置一下环境变量即可

数据表创建

先新建个数据库,默认就可以

然后新建查询,创建我们要用到的表

1
2
3
4
5
6
7
8
9
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nickname` varchar(50) NOT NULL COMMENT '昵称',
`username` varchar(50) NOT NULL COMMENT '账号',
`password` varchar(50) NOT NULL COMMENT '密码',
`create_time` varchar(40) NOT NULL COMMENT '创建时间',
`del_status` int(4) NOT NULL COMMENT '删除状态0删除,1存在',
PRIMARY KEY (`id`)
);

可以看到创建成功

插入两条数据试试

1
2
3
4
5
6
insert into t_user (`nickname`,`username`,`password`,`create_time`,`del_status`)
values("⽤户1","user1","e10adc3949ba59abbe56e057f20f883e","2024-10-01 13:
00:41",1);
insert into t_user (`nickname`,`username`,`password`,`create_time`,`del_status`)
values("⽤户2","user2","e10adc3949ba59abbe56e057f20f883e","2024-10-01 13:
00:41",1);

检查一下成功插入数据

第三部分:学习java中ORM框架 mybatis

mybatis的作用

  1. 简化数据库操作

MyBatis允许开发者通过XML或注解的方式定义SQL语句,简化JDBC的繁琐操作,比如连接管理,语句创建和结果处理。

  1. 灵活的SQL映射

与ORM(对象关系映射)框架不同,MyBatis允许开发者直接编写SQL语句,提供了更大的灵活性和控制权,适合复杂的查询和性能调优。

  1. 对象映射

MyBatis能够将数据库查询结果映射到java对象,支持复杂的对象关系映射,使得数据处理更加便捷。

项目引入mybatis

打开pom.xml文件,引入mybatis依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--mybatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.2</version>
</dependency>


<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>

打开配置文件application.properties,添加一下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
spring.application.name=testMysql
server.port=8087


### mybatis
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
mybatis.configuration.map-underscore-to-camel-case=true

### xxl-job, datasource
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useUnicode=true&useSSL=false&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

### datasource-pool
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=HikariCP
spring.datasource.hikari.max-lifetime=900000
spring.datasource.hikari.connection-timeout=10000
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.validation-timeout=1000

实现完整的用户模块

编写接口

用户注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@RequestMapping(value = "/register")
public ResponseEntity<JSONObject> save(@RequestBody TUser user){
JSONObject jo = new JSONObject();

if (user.getUsername() == null || "".equals(user.getUsername())){
jo.setCode("-1");
jo.setMsg("username不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}
if (user.getPassword() == null || "".equals(user.getPassword())){
jo.setCode("-1");
jo.setMsg("password不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}
if (user.getNickname() == null || "".equals(user.getNickname())){
jo.setCode("-1");
jo.setMsg("nickname不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}


TUser exist = userService.queryUserByUsername(user.getUsername());
if (exist != null){
jo.setCode("-1");
jo.setMsg("username已存在,请更改");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

user.setPassword(MD5Utils.string2MD5(user.getPassword()));
user.setCreateTime(DateUtils.getTime());
user.setDelStatus(1);
userService.save(user);

jo.setCode("1");
jo.setMsg("注册成功");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);

}

mybatis代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<insert id="save" parameterType="com.example.testmysql.entity.TUser" useGeneratedKeys="true" keyProperty="id" >
insert into t_user (
`username`,
`password`,
`nickname`,
`create_time`,
`del_status`
) VALUES (
#{username},
#{password},
#{nickname},
#{createTime},
#{delStatus}
);
</insert>
用户登录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@RequestMapping(value = "/login")
public ResponseEntity<JSONObject> login(@RequestBody TUser user){
JSONObject jo = new JSONObject();

if (user.getUsername() == null || "".equals(user.getUsername())){
jo.setCode("-1");
jo.setMsg("username不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}
if (user.getPassword() == null || "".equals(user.getPassword())){
jo.setCode("-1");
jo.setMsg("password不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

TUser exist = userService.queryUserByUsername(user.getUsername());
if (exist == null){
jo.setCode("-1");
jo.setMsg("username不存在,请先注册");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

if (!MD5Utils.passwordIsTrue(user.getPassword(),exist.getPassword())){
jo.setCode("-1");
jo.setMsg("密码错误,请重试");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}


jo.setCode("1");
jo.setMsg("登录成功");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

mybatis代码

1
2
3
<select id="queryUserByUsername" parameterType="string" resultType="com.example.testmysql.entity.TUser">
select * from t_user where username = #{username} and del_status = 1
</select>
修改密码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@RequestMapping(value = "/updatePassword")
public ResponseEntity<JSONObject> updatePassword(@RequestBody UpdateUser user){
JSONObject jo = new JSONObject();

if (user.getUsername() == null || "".equals(user.getUsername())){
jo.setCode("-1");
jo.setMsg("username不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}
if (user.getOldPassword() == null || "".equals(user.getOldPassword())){
jo.setCode("-1");
jo.setMsg("旧密码不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

if (user.getNewPassword() == null || "".equals(user.getNewPassword())){
jo.setCode("-1");
jo.setMsg("新密码不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

TUser exist = userService.queryUserByUsername(user.getUsername());
if (exist == null){
jo.setCode("-1");
jo.setMsg("username不存在,请先注册");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

if (!MD5Utils.passwordIsTrue(user.getOldPassword(),exist.getPassword())){
jo.setCode("-1");
jo.setMsg("旧密码错误,请重试");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

user.setNewPassword(MD5Utils.string2MD5(user.getNewPassword()));
userService.updatePassword(user);

jo.setCode("1");
jo.setMsg("修改成功");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

mybatis代码

1
2
3
<update id="updatePassword" parameterType="com.example.testmysql.entity.UpdateUser">
update t_user set password = #{newPassword} where username = #{username}
</update>
删除用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@RequestMapping(value = "/deleteUser")
public ResponseEntity<JSONObject> deleteUser(@RequestBody TUser user) {
JSONObject jo = new JSONObject();

if (user.getUsername() == null || "".equals(user.getUsername())){
jo.setCode("-1");
jo.setMsg("username不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}
if (user.getPassword() == null || "".equals(user.getPassword())){
jo.setCode("-1");
jo.setMsg("password不能为空");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

TUser exist = userService.queryUserByUsername(user.getUsername());
if (exist == null){
jo.setCode("-1");
jo.setMsg("username不存在,请先注册");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

if (!MD5Utils.passwordIsTrue(user.getPassword(),exist.getPassword())){
jo.setCode("-1");
jo.setMsg("密码错误,请重试");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

userService.deleteUser(user);

jo.setCode("1");
jo.setMsg("删除成功");
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

mybatis代码

1
2
3
<update id="deleteUser" parameterType="com.example.testmysql.entity.UpdateUser">
update t_user set del_status = 0 where username = #{username}
</update>
查询所有用户
1
2
3
4
5
6
7
8
9
10
11
@RequestMapping(value = "/queryAllUser")
public ResponseEntity<JSONObject> queryAllUser(){
JSONObject jo = new JSONObject();

List<TUser> users = userService.queryAllUser();

jo.setCode("1");
jo.setMsg("查询成功");
jo.setO(users);
return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
}

mybatis代码

1
2
3
<select id="queryAllUser" resultType="com.example.testmysql.entity.TUser">
select * from t_user
</select>

测试接口

用户注册 /register

发送注册信息

1
2
3
4
5
{
"nickname":"rev1ve",
"username":"test",
"password":"test"
}

用户登录 /login

发送登录json数据包

1
2
3
4
{
"username":"test",
"password":"test"
}

修改密码 /updatePassword

新旧密码的参数可以在/entity/UpdateUser.java类找到

1
2
3
4
5
{
"username":"test",
"oldPassword":"test",
"newPassword":"123456"
}

查询所有用户 /queryAllUser


参考文章

解决本地mysql和phpstudy的mysql冲突问题