1.
首先,ApiBoot是一个开源框架
官方网站是:http://apiboot.minbox.io
ApiBoot™ 基于SpringBoot为接口服务而生,组件化Api的首选落地解决方案
最近有一个项目需要开发api接口
顺手在码云搜索了一下
第一眼就看到了这个项目
大概看了看文档,感觉真心不错
就开始用这个搭建框架,进行开发了
项目很完善,框架几乎可以做到0代码搭建
2.
很快,接口系统框架就搭建好了
这个框架的功能很多
我根据项目的需要整合了swagger、Security、OAuth、Data Switch、Mybatis Enhance、Mybatis Enhance Codegen六个模块
主要实现了在线接口文档、Oauth2.0授权认证机制、MySQL数据库操作、curd代码生成功能
个人还是认为:写代码跟做事一样,合适最重要
不一定非得用最负责最高级最完美的实现,合适就好
鉴于很多人可能没什么耐心把全部内容看完
我把代码共享到了码云上边,连接地址在这
https://gitee.com/daijiyong/apiboot-test.git
效果图如下:
3. 下面说说怎么具体实现的
官方文档:
http://apiboot.minbox.io/zh-cn/docs/introduce.html
3.1 创建springboot项目
浏览器输入地址:https://start.spring.io/
填写参数,我选择的是war包形式发布
点击生成代码
然后作为maven项目将代码导入idea
3.2 配置pom.xml文件
<?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.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shxt</groupId>
<artifactId>nemt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>某项目api接口</name>
<description>某项目api接口</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.boot.version>2.2.1.RELEASE</spring.boot.version>
<fastjson.version>1.2.60</fastjson.version>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--SpringBoot Dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-swagger</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<!-- <version>4.4</version>-->
</dependency>
<!--MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--ApiBoot DataSource Switch-->
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-datasource-switch</artifactId>
</dependency>
<!--ApiBoot Http Converter-->
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-http-converter</artifactId>
</dependency>
<!--ApiBoot Security Oauth-->
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-security-oauth-jwt</artifactId>
</dependency>
<!--ApiBoot Mybatis Enhance-->
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-mybatis-enhance</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
3.3 配置application.properties 大多数默认配置就行
一些个性化的东西需要自己配置
配置springboot相关内容
spring.application.name=项目名
配置swagger在线文档的内容
# 接口文档相关
api.boot.swagger.title=某项目
api.boot.swagger.version=V1.0
api.boot.swagger.description=某项目api接口文档,接口遵循restful设计规范,返回数据均为json
api.boot.swagger.base-package=com.djy.shxt.nemt.api
api.boot.swagger.contact.name=戴先生
api.boot.swagger.contact.email=
api.boot.swagger.contact.website=
api.boot.swagger.license=
api.boot.swagger.license-url=
配置token认证授权机制参数
#安全配置相关
api.boot.security.away=jdbc
api.boot.security.enable-default-store-delegate=false
api.boot.oauth.clients[0].client-id=daijiyong
api.boot.oauth.clients[0].client-secret=nemt
api.boot.oauth.clients[0].grant-types=password,refresh_token
配置数据库参数
# 数据库相关
api.boot.datasource.hikari.master.url=jdbc:mysql://localhost:3306/nemt?characterEncoding=utf8&serverTimezone=Asia/Shanghai
api.boot.datasource.hikari.master.username=root
api.boot.datasource.hikari.master.password=root
3.4 配置curd代码生成插件
在pom.xml文件中添加如下配置内容
其中com.djy.shxt.nemt是生成代码的路径
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-mybatis-enhance-maven-codegen</artifactId>
<version>2.1.5.RELEASE</version>
<dependencies>
<!--数据驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>generator</goal>
</goals>
</execution>
</executions>
<configuration>
<execute>true</execute>
<dbName>nemt</dbName>
<dbUrl>jdbc:mysql://localhost:3306</dbUrl>
<dbUserName>root</dbUserName>
<dbPassword>root</dbPassword>
<packageName>com.djy.shxt.nemt</packageName>
</configuration>
</plugin>
</plugins>
</build>
reimport之后可以看到这里多了一个插件
双击之后,就会在这个路径生成两类文件
一类是实体类
@Id的主键生成策略,会根据表内主键是否定义了自增来进行判断,如果是自增使用KeyGeneratorTypeEnum.AUTO
如果不是则使用KeyGeneratorTypeEnum.UUID
如果你项目内是自定义的主键,可以进行修改为KeyGeneratorTypeEnum.DIY。
package com.djy.shxt.nemt.enity;
import com.gitee.hengboy.mybatis.enhance.common.annotation.Column;
import com.gitee.hengboy.mybatis.enhance.common.annotation.Id;
import com.gitee.hengboy.mybatis.enhance.common.annotation.Table;
import com.gitee.hengboy.mybatis.enhance.common.enums.KeyGeneratorTypeEnum;
import lombok.Data;
import java.io.Serializable;
/**
*
* @author ApiBoot Mybatis Enhance Codegen
*/
@Data
@Table(name = "speciality_post")
public class SpecialityPost implements Serializable {
/**
*
*/
@Id(generatorType = KeyGeneratorTypeEnum.AUTO)
@Column(name = "id")
private Integer id;
/**
*
*/
@Column(name = "area")
private String area;
/**
*
*/
@Column(name = "school")
private String school;
/**
*
*/
@Column(name = "degree")
private String degree;
/**
*
*/
@Column(name = "original_speciality")
private String originalSpeciality;
/**
*
*/
@Column(name = "speciality")
private String speciality;
/**
*
*/
@Column(name = "original_post")
private String originalPost;
/**
*
*/
@Column(name = "post")
private String post;
/**
*
*/
@Column(name = "number")
private Integer number;
}
二类是动态查询实体
package com.djy.shxt.nemt.dsl;
import com.djy.shxt.nemt.enity.SpecialityPost;
import com.gitee.hengboy.mybatis.enhance.dsl.expression.ColumnExpression;
import com.gitee.hengboy.mybatis.enhance.dsl.expression.TableExpression;
/**
* @author ApiBoot Mybatis Enhance Codegen
*/
public class DSpecialityPost extends TableExpression<SpecialityPost> {
public DSpecialityPost(String root) {
super(root);
}
public static DSpecialityPost DSL() {
return new DSpecialityPost("speciality_post");
}
/**
*
*/
public ColumnExpression id = new ColumnExpression("id", this);
/**
*
*/
public ColumnExpression area = new ColumnExpression("area", this);
/**
*
*/
public ColumnExpression school = new ColumnExpression("school", this);
/**
*
*/
public ColumnExpression degree = new ColumnExpression("degree", this);
/**
*
*/
public ColumnExpression originalSpeciality = new ColumnExpression("original_speciality", this);
/**
*
*/
public ColumnExpression speciality = new ColumnExpression("speciality", this);
/**
*
*/
public ColumnExpression originalPost = new ColumnExpression("original_post", this);
/**
*
*/
public ColumnExpression post = new ColumnExpression("post", this);
/**
*
*/
public ColumnExpression number = new ColumnExpression("number", this);
@Override
public ColumnExpression[] getColumns() {
return new ColumnExpression[]{id, area, school, degree, originalSpeciality, speciality, originalPost, post, number};
}
}
把这两类文件放到对应的包下
使用动态查询实体示例
package com.djy.shxt.nemt.service;
import com.djy.shxt.nemt.dsl.DSpecialityPost;
import com.djy.shxt.nemt.enity.SpecialityPost;
import com.djy.shxt.nemt.mapper.ISpecialityPost;
import com.gitee.hengboy.mybatis.enhance.dsl.factory.EnhanceDslFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class SpecialityPostService {
@Autowired
ISpecialityPost iSpecialityPost;
@Autowired
private EnhanceDslFactory dslFactory;
public Long countAll() {
return iSpecialityPost.countAll();
}
public List<SpecialityPost> selectBySchool(String school) {
DSpecialityPost dSpecialityPost = DSpecialityPost.DSL();
return dslFactory.createSearchable()
.selectFrom(dSpecialityPost)
.where(dSpecialityPost.school.like("", school, "%"))
.resultType(SpecialityPost.class)
.fetch();
}
}
至此,通过自动生成的curd代码做数据库的读写就完成了
3.5 整合Spring Security的JDBC方式获取AccessToken
ApiBoot Security内部提供了两种方式进行读取需要认证的用户信息,
ApiBoot Security使用内存方式(memory)不写一行代码就可以实现用户的
认证并获取AccessToken
我使用JDBC方式实现了,其他方式可以看一下官方文档
数据库方式(jdbc)是将用户信息保存到数据库内,ApiBoot Security定义了一个默
认表结构的用户信息数据表,我们可以从官网找到建表语句直接在自己的数据库内创建即可,当然如果不使用默认的表结构可以进行自定义读取用户信息。
注意:在数据库内存放用户的密码必须是通过BCryptPasswordEncoder加密后的密文字符串。
3.5.1 创建默认用户表结构
我们在数据库内创建一张名为system_user的系统用户信息表,表结构如下所示:
CREATE TABLE `system_user` (
`su_id` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户编号',
`su_login_name` varchar(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '登录名',
`su_nick_name` varchar(30) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '昵称',
`su_password` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户密码',
`su_create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`su_status` int(11) DEFAULT '1' COMMENT '用户状态,1:正常,0:冻结,-1:已删除',
PRIMARY KEY (`su_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统用户信息表';
system_user用户表创建完成后,我们往这张表内添加一条用户数据,如下所示
INSERT INTO `system_user` VALUES ('9b69fd26-14db-11ea-b743-dcd28627348e','dai','你好戴先生','$2a$10$RbJGpi.v3PwkjrYENzOzTuMxazuanX3Qa2hwI/f55cYsZhFT/nX3.','2019-12-02 08:13:22',1);
在登录时用户名对应su_login_name字段,而密码则是对应su_password字段,dai这个用户的密码初始化为123456,密码的格式必须为BCryptPasswordEncoder加密后的密文。
实体类可以使用代码生成工具生成
针对system_user表我们需要来创建一个ApiBoot MyBatis Enhance使用的实体,创建一个名为SystemUser的实体如下所示:
package com.djy.shxt.nemt.enity;
import com.gitee.hengboy.mybatis.enhance.common.annotation.Column;
import com.gitee.hengboy.mybatis.enhance.common.annotation.Id;
import com.gitee.hengboy.mybatis.enhance.common.annotation.Table;
import com.gitee.hengboy.mybatis.enhance.common.enums.KeyGeneratorTypeEnum;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
/**
* @author daijiyong
*/
@Data
@Table(name = "system_user")
public class SystemUser implements UserDetails {
/**
* 用户编号
*/
@Id(generatorType = KeyGeneratorTypeEnum.UUID)
@Column(name = "su_id")
private String userId;
/**
* 登录名
*/
@Column(name = "su_login_name")
private String loginName;
/**
* 昵称
*/
@Column(name = "su_nick_name")
private String nickName;
/**
* 密码
*/
@Column(name = "su_password")
private String password;
/**
* 创建时间
*/
@Column(name = "su_create_time")
private String createTime;
/**
* 用户状态
* 1:正常,0:已冻结,-1:已删除
*/
@Column(name = "su_status")
private Integer status;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.EMPTY_LIST;
}
@Override
public String getUsername() {
return this.loginName;
}
@Override
public String getPassword() {
return this.password;
}
/**
* UserDetails提供的方法,用户是否未过期
* 可根据自己用户数据表内的字段进行扩展,这里为了演示配置为true
*
* @return
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* UserDetails提供的方法,用户是否未锁定
* 可根据自己用户数据表内的字段进行扩展,这里为了演示配置为true
*
* @return
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* UserDetails提供的方法,凭证是否未过期
* 可根据自己用户数据表内的字段进行扩展,这里为了演示配置为true
*
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* UserDetails提供的方法,是否启用
*
* @return
*/
@Override
public boolean isEnabled() {
return this.status == 1;
}
}
具体的注解使用详见ApiBoot MyBatis Enhance文档
这里还一点需要注意的是,SystemUser实现了UserDetails接口
如果使用过Spring Security的同学应该都知道这是Spring Security提供的用户详情接口定义
我们如果自定义查询用户就应该让我们自定义的用户实体(注:这是的自定义用户实体也就是SystemUser实体)实现这个接口并全部实现UserDetails接口内提供的方法。
创建用户数据接口
package com.djy.shxt.nemt.mapper;
import com.djy.shxt.nemt.enity.SystemUser;
import com.gitee.hengboy.mybatis.enhance.mapper.EnhanceMapper;
import org.apache.ibatis.annotations.Param;
/**
* @author daijiyong
*/
public interface ISystemUserMapper extends EnhanceMapper<SystemUser, String> {
/**
* 根据用户登录名查询用户信息
*
* @param loginName {@link SystemUser#getLoginName()}
* @return {@link SystemUser}
*/
SystemUser findByLoginName(@Param("loginName") String loginName);
}
该接口继承了EnhanceMapper接口,可以自动被扫描到创建代理的实例后并且加入IOC,这样我们在项目其他的地方可以直接注入使用。
注意:findByXxx方法是ApiBoot MyBatis Enhance提供的方法命名规则查询,多个查询条件可以使用And或者Or追加,会自动根据方法的规则生成对应的SQL。
实现ApiBootStoreDelegate接口
ApiBoot Security提供了一个接口ApiBootStoreDelegate,这个接口主要是用来查询登录用户的具体信息的作用
当我们通过grant_type=password&username=xxx的方式进行获取AccessToken时,ApiBoot Security会直接把username的参数值传递给ApiBootStoreDelegate#loadUserByUsername的方法内
这样我们就可以根据username进行查询用户并返回给ApiBoot Security做后续的认证操作。
package com.djy.shxt.nemt.service;
import com.alibaba.fastjson.JSON;
import com.djy.shxt.nemt.mapper.ISystemUserMapper;
import org.apache.commons.lang3.ObjectUtils;
import org.minbox.framework.api.boot.plugin.security.delegate.ApiBootStoreDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class SystemUserService implements ApiBootStoreDelegate {
/**
* logger instance
*/
static Logger logger = LoggerFactory.getLogger(SystemUserService.class);
/**
* 用户数据接口
*/
@Autowired
private ISystemUserMapper mapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails userDetails = mapper.findByLoginName(username);
if (ObjectUtils.isEmpty(userDetails)) {
throw new UsernameNotFoundException("用户:" + username + ",不存在.");
}
logger.info("登录用户的信息:{}", JSON.toJSONString(userDetails));
return userDetails;
}
}
3.6 运行项目,测试接口 测试token生成
使用postman进行post请求方式测试
跟三个参数grant_type、username、password
分别对应application配置文件中的api.boot.oauth.clients[0].grant-types=password,refresh_token
以及数据表中的登录名和密码
以下配置对应application配置文件中的 api.boot.oauth.clients[0].client-id=daijiyong api.boot.oauth.clients[0].client-secret=nemt
点击send就能顺利获取到token和refreshToken,以及他们的有效期
更新token使用下面的方式
测试接口,如果没有输入token,提示未认证
点击接口右边的锁形按钮
将获取到的这两个参数,拼接一下 "tokenType": "bearer", "value": "a84e887b-9b51-4180-acbc-e281e7d322e6"
点击Authorize按钮
再点击execute,成功,有数据返回了
4.框架完成,可以快(ku)乐(bi)的写代码了
文/戴先生@2020年6月15日
---end---