如何去开发一个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;@Overridepublic 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;@Beanpublic 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 {@Resourceprivate XWHKINGClient xwhkingClient;@Testvoid testClient(){xwhkingClient.getName("xwhking") ;}}
调用结果:
出来了 xwhking
结果成功。
这样开发一个starter 就结束了