如何去开发一个springboot starter
我们在平时用 Java 开发的时候,在 pom.xml 文件中引入一个依赖就可以很方便的使用了,但是你们知道这是如何实现的吗。
现在我们就来解决这一个问题!
创建 SpringBoot 项目
首先我们要做的就是把你想要给别人方便使用的内容自己的写好
我这里直接另外创建了一个 springboot 的 web 项目,在这个 web 项目中我用 controller 写了几个简单的接口,用于后面的调用,然后再创建一个 springboot 项目,这个新的 springboot 项目就是用来开发 starter 的方便使用者更好、更方便使用。
现在是具体流程:
springboot 的 web 项目创建
用 IDEA 快速创建一个 springboot 项目,创建方法如下:
- 选择 spring Initializer
- 自己写一个项目的名称
- 语言选择 Java
- 包管理工具选择 Maven
- 组这个可以自己写 例如我的昵称 xwhking 就可以写
com.xwkhing
- jdk 选择1.8
- Java 选择8
- 然后就是下一步
进入下一步后
-
选择 springboot 的版本,我一般选择2.7左右的
-
然后选择开发工具
- Spring Boot Devtools
- Spring COnfiguration Processor 主要用于后面我们在工程中使用的使用在 yml 中写配置时能够自动提示配置
- Lombok 通过使用 annotation 快速的生成主要的 getter 和 setter
- Spring Web 加上也没事
-
然后就是创建了。
创建好了以后就进入写代码环节,写一个简单的 web 请求的 Demo
我这里的目录结构如下:
UserController的代码如下:
package com.xwhking.interface_test.controller;
import com.xwhking.interface_test.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import static com.xwhking.interface_test.utils.GenSign.genSign;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/name")
public String getName(@RequestParam String name , HttpServletRequest request){
System.out.println("请求参数名字为 : " + name);
return "GET 请求参数名字为 : " + name;
}
@GetMapping("/getOne")
public User getUser(HttpServletRequest request){
User user = new User();
user.setId(123l);
user.setUsername("xwhking");
user.setPassword("admin123");
System.out.println(user);
return user ;
}
}
User代码
package com.xwhking.interface_test.entity;
import lombok.Data;
@Data
public class User {
private Long id;
private String username;
private String password;
@Override
public String toString(){
return "User { " +
"id: " + id + "," +
"name:" + username + ","+
"password: " + password + "}";
}
}
这些完成以后就可以启动这一个项目了。
可以用浏览器试试
starter开发
以同样的方式创建一个springboot项目,需要特别注意的就是,我们需要把 pom.xml 文件中的 build 部分代码全部去掉。
pom.xml 文件
注意这里引入了 Hutool 工具库,用于后面开发,并且文件里面是没有 build 模块的,这里还需要注意把版本的snapshot去掉
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xwhking</groupId>
<artifactId>InterfaceStarter</artifactId>
<version>0.0.1</version>
<name>InterfaceStarter</name>
<description>InterfaceStarter</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
</dependencies>
</project>
目录结构
- client 目录 这里就是真正使用的类,对对应的功能进行了封装。
- config 目录 这里对 client 进行配置,并且把 client 包装成一个 Bean 返回
- utils 工具类,这里用来生成签名的工具
- META-INF.spring.factories 这个非常重要,用于别人调用的时候,在写配置的之后能够进行提示与自动装配
client 代码
package com.xwhking.interfacestarter.client;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import static com.xwhking.interfacestarter.utils.GenSign.genSign;
/**
* 发起请求时候一定要注意,不要把secretKey直接传送,只需要进行一个签名传送就好了,然后后端通过同样的方式进行签名
* 的生成,进行对比,验证身份。
*/
@Data
public class XWHKINGClient {
private String accessKey;
private String secretKey;
private Long userId;
public XWHKINGClient(String accessKey,String secretKey,Long userId){
this.userId = userId;
this.accessKey = accessKey;
this.secretKey = secretKey;
}
public String getName(String name){
//可以单独传入http参数,这样参数会自动做URL编码,拼接在URL中
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("name", name);
HashMap<String,String> headerMap = new HashMap<>();
headerMap.put("accessKey",accessKey);
headerMap.put("userId",userId.toString());
headerMap.put("sign", genSign(accessKey,secretKey,userId));
headerMap.put("timestamp",Long.toString(new Date().getTime()));
String result1= HttpRequest.get("http://localhost:8080/user/name")
.addHeaders(headerMap)
.form(paramMap).execute().body();
System.out.println(result1);
return result1;
}
public String GetUser(){
HashMap<String,String> headerMap = new HashMap<>();
headerMap.put("accessKey",accessKey);
headerMap.put("userId",userId.toString());
headerMap.put("sign", genSign(accessKey,secretKey,userId));
headerMap.put("timestamp",Long.toString(new Date().getTime()));
String result1= HttpRequest.get("http://localhost:8080/user/getOne")
.addHeaders(headerMap)
.execute().body();
System.out.println(result1);
return result1;
}
public static void main(String[] args) {
new XWHKINGClient("xwhking","admin123",123123l).getName("XWHKING");
}
}
这里都加了请求头,加请求头的目的是为了,进行签名认证。
为什么要进行签名认证
- 保证安全性,一个人不能随便调用,如果随便调用的话,自己的服务器资源会收到压迫,以及资源的损失。
- 适用于无需保存登录态。只认签名,不关注用户登录态。
如何进行签名认证
通过 http request header 头传递参数
这里主要传递的参数
- accessKey: 调用的标识, 需要复杂、 无序、无规律,这里我没有实现,只是简单的模拟,如果需要实现的话可以使用现成的签名实现工具包,例如 hutool
- secretKey: 调用的密钥,需要复杂、 无序、无规律,该参数一定一定不能放到请求头中,不然可能会被别人抓包,以及可能造成泄露。
- 用户请求的参数
- sign: 签名,由 accessKey 和 secretKey 以及 userId 等信息生成,用于传递信息。然后后端通过同样的方式进行生成对比验证权限。
- 上述问题满足了,可能还抵挡不了别人的攻击,例如别人用重放就可以再次调用 API 了,限制重放的方法:
- 加随机数,只能使用一次,服务端要保存使用过的随机数
- 加 timestamp 时间戳,检验时间戳是否过期,我这里就是通过时间戳,超过10s就失效。
对web中的UserController进行更新
更新就是为了校验,然后如果更新了,项目记得重启
package com.xwhking.interface_test.controller;
import com.xwhking.interface_test.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import static com.xwhking.interface_test.utils.GenSign.genSign;
@RestController
@RequestMapping("/user")
public class UserController {
/**
* 校验签名,以及其他信息,注意这里的 secretKey 是模拟的一般用户的 secretKey 是需要去从数据库取出来,
* 然后进行验证。
* @param request
*/
private void verifyRequest(HttpServletRequest request){
String sign = request.getHeader("sign");
String accessKey = request.getHeader("accessKey");
String secretKey = "admin123";
String userId = request.getHeader("userId");
String requestTime = request.getHeader("timestamp");
long oldTime = Long.parseLong(requestTime);
long newTime = new Date().getTime();
if(newTime - oldTime > 10000){
throw new RuntimeException("检测到请求异常");
}
String newSign = genSign(accessKey,secretKey,Long.parseLong(userId));
if(!newSign.equals(sign)){
throw new RuntimeException("签名错误");
}
}
@GetMapping("/name")
public String getName(@RequestParam String name , HttpServletRequest request){
verifyRequest(request);
System.out.println("请求参数名字为 : " + name);
return "GET 请求参数名字为 : " + name;
}
@GetMapping("/getOne")
public User getUser(HttpServletRequest request){
verifyRequest(request);
User user = new User();
user.setId(123l);
user.setUsername("xwhking");
user.setPassword("admin123");
System.out.println(user);
return user ;
}
}
config 代码
要注意其中的注解
package com.xwhking.interfacestarter.config;
import com.xwhking.interfacestarter.client.XWHKINGClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "xwhking.client")
@Data
@ComponentScan
public class XWHKINGClientConfig {
private String accessKey;
private String secretKey;
private String userId;
@Bean
public XWHKINGClient xwhkingClient(){
return new XWHKINGClient(accessKey,secretKey,Long.parseLong(userId));
}
}
生成签名代码
这里直接使用了 Hutool 工具库中的 sh256 的生成方法
package com.xwhking.interfacestarter.utils;
import cn.hutool.crypto.SecureUtil;
import lombok.Data;
@Data
public class GenSign {
public static String genSign(String accessKey,String secretKey,Long userId){
String key = "xwhking" + "." + accessKey + "." + secretKey + "." + userId;
return SecureUtil.sha256(key);
}
}
META-INF 中文件的内容
通过看内容就可以看出,你需要把后面的内容进行替换,写入你的地址。
org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.xwhking.interfacestarter.XWHKINGClientConfig
到这里 Starter 就完了,然后使用 maven 进行打包(调用install)。这里的打包会把包直接放入你的 maven 仓库,打包成功就会出现 BUILD SUCCESS
在其他项目中使用
首先复制这段代码
<groupId>com.xwhking</groupId>
<artifactId>InterfaceStarter</artifactId>
<version>0.0.1</version>
然后在你的其他项目中添加进依赖
<dependency>
<groupId>com.xwhking</groupId>
<artifactId>InterfaceStarter</artifactId>
<version>0.0.1</version>
</dependency>
刷新 maven 仓库
然后再 yml 配置中加入配置
在 config中的prefix 可以设置前缀,也就是可以更改xwhking.client
然后在你的代码中使用 Test 进行测试
@SpringBootTest
class MainApplicationTests {
@Resource
private XWHKINGClient xwhkingClient;
@Test
void testClient(){
xwhkingClient.getName("xwhking") ;
}
}
调用结果:
出来了 xwhking
结果成功。
这样开发一个starter 就结束了