项目第一步,配置jwt
有三类算法:HS,RS,EC.
两种场景:
场景一: 如果创建token和验证token都在同一个项目中,则可以使用keystore的方式。
见 swak-test项目下的 KeyStoreTes
创建keystore 的方式:
keystore文件可以存放在classpath目录或者文件系统,配置时指定存储的路径。
keystore 加载规则:
classpath 的配置方式:直接放在src/main/resources 目录下,打包时会打进包中。
然后在代码中设置keystore 的名称即可。
文件系统:file:path 这样的配置方式。
场景二: 如果创建token和验证token需要分开使用不同的项目,则可以使用RS和EC的算法。
见 swak-test项目下的RsaTest.java、EcTest.java、RsaReadOnlyTest.java
支持公匙和私匙的配置方式。
创建token则需要配置公匙和私匙。
解密token则只需要配置公匙
keytool -genseckey -keystore D:\keystore.jceks -storetype jceks -storepass secret -keyalg HMacSHA256 -keysize 2048 -alias HS256 -keypass secret
请修改-storepass -keypass 设定的密码,这两个密码需要设置的一致
项目介绍
springboot-vertx 是基于swak 的一个演示项目,主要演示了如下部分:
基本的启动框架
api的配置方式,例如AdminApi、AnnoApi、ParamApi、UserApi。
api权限的校验,简单的权限校验方式,配置方式AppConfiguration 第 48 行代码到第 54 行代码。
参数的传递方式,详细见测试代码 ParamTest.java
返回值的使用方式,详细见测试代码 ParamTest.java
项目如何测试继承AppRunnerTest,详细见测试代码 ParamTest.java
service 的配置 UserServiceImpl
自定义的项目启动
大家都很熟悉springboot的四大特性项目依赖,自动化配置。本人也非常喜欢,但不太喜欢引入过多的start.jar来引入组件。
所以本人对spring做了一些简单的定制。将他的启动属性文件spring.factories 改为 swak.factories.
改变的方法也很简单中有详细的源码:swak-starter。所有我们熟悉的组件都放在 com.swak.config 中来自启动。
AppConfiguration.java -- 项目级别的启动配置
AppRunner.java -- 启动入口,执行main方法
执行注解com.swak.ApplicationBoot所引入的com.swak.selector.AutoConfigurationImportSelector,此类将会执行swak.factories中引入的启动配置。
如果是web环境(依赖的jar有vertx的相关类)则会实例化 ReactiveServerApplicationContext,否则会实例化 AnnotationConfigApplicationContext。
ReactiveServerApplicationContext 实例化之后会启动 ReactiveServer,vertx 开始初始化。
vertx 的启动依赖如下:(swak-vertx)
ReactiveServer -> MainVerticle -> ServiceVerticle(多个) -> HttpVerticle(一个多实例)
如上是简单的解释,后续在完善。
API的配置方式、参数处理、权限校验、返回值处理
通过注解的方式来配置,类似springmvc 的方式.注解只有:PageController、RestController、PostMapping、GetMapping简单明了。
PageController 和 RestController 没有过多的差别,只是说明此Controller是来做展示页面的。
API 中最重要的类是 HandlerAdapter, 将api的path绑定到vertx中的 Router,调用api的method,处理参数,处理返回值等。
响应式HTTP服务器的简单介绍
何为响应式,说白了就是回调,IO处理好之后回调注册的方法。这样线程就不需要等待IO完成才继续执行后续代码.
vertx 被称为java中的node.js,多线程版node.js。
swak是基于vertx做了简单的封装,让我们能像使用springmvc一样使用vertx。
响应式开发并不难,难的是需要弄清楚回调之后的代码在哪个线程中执行。以前我们开发ssm项目时,一个请求对应一个线程,从头走到尾,我们基本上不用理会当前线程是谁,执行是否需要花费很长时间。
我觉得响应式最大的特点是在请求响应这条事务中可以任意(只是比如而已)的切换线程。
例如,本例子中,Controller的执行在netty的eventloop线程中,UserServiceImpl 的执行在 vertx的work线程中。
下面简单解释下这个执行过程。vertx 的关键点其内部的 Eventbus。可以简单的把他想象成为一个map。map 的key是接口类型,value是实现类对象的封装。
Controller 调用 service 时,实际上是发送一个消息到 Eventbus,消息中包含service的接口类型,Eventbus获取相应的实现类对象并在相应线程中执行,将返回值通过Eventbus通过消息的形式返回。
具体参见 InvokerHandler 第 73 行 发送消息代码。
service 对象执行 参见 ServiceVerticle handle 部分
项目中为啥会出现这两类接口
UserService、UserServiceAsync
UserService -- 和我们做ssm时后的service接口一样,用于同步返回,一帮用于jdbc 的操作接口。
UserServiceAsync -- 异步返回版本,这个可以自动生成,但自动生成的代码返回值中的范型无法动态设置,所以现在都手动生成此异步接口。
关于开发响应式系统的一些经验
以前我们开发ssm时喜欢将系统分为三层的开发模式,Controller、service、dao。现在也一样也是三层。不同点在于Controller在eventloop线程中执行,service、dao和之前一样在work线程中执行,service、dao以前怎么开发现在也怎么开发,没变。
Controller 调用 service 通过异步发消息的方式来调用。
Controller 的 代码
@RestController(path = "/api/user", value = "userApi")
public class UserApi {
@VertxReferer
private UserServiceAsync userService;
/**
* 获取用户
*
* @param subject
* @return
*/
@GetMapping("/get")
public CompletableFuture get(Subject subject) {
return userService.get(subject.getIdAsLong()).thenApply(res -> Result.success(res));
}
}
service 的代码,和之前一样使用spring声明式事务等。
@VertxService
public class UserServiceImpl implements UserService {
@Override
public User get(Long id) {
return new User().setId(id);
}
}
java的jdbc是同步执行的,spring的事务业务依赖当前线程的,所以不要在service中切换线程。
其他一些io,例如redis,http 客户端,我都是用的基于netty的异步客户端。
相关说明后续补充...
SWAK 项目