分享一个springboot脚手架
项目介绍
在我们开发项目的时候各个项目之间总有一些可共用的代码或者配置,如果我们每新建一个项目就把代码复制粘贴再修改就显得很没有必要。于是我就做了一个 poseidon-boot-starter 该项目是基于 spring-boot的 starter 功能开发的,因此只适用于 spring-boot 项目。该项目集成了如下功能:
- 异常通知
- 权限配置
- 幂等锁
- 日志配置
- 用户操作日志记录
- 查询接口通用化
项目地址:https://github.com/muggle0/poseidon-boot-starter
下面介绍该组件如何在我们的 spring-boot 项目中使用。
首先我们需要下载下来这个项目:
git clone https://github.com/muggle0/poseidon-boot-starter.git
然后安装到我们的本地仓库或者私有云:
cd poseidon-boot-starter
mvn install
安装完成之后在spring boot 项目中引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
然后进行一些基础的配置:
poseidon.auto=true
poseidon.static-path=/**/*.*
poseidon.ignore-path=/**
logging.config=classpath:poseidon-logback.xml
log.dir=logs
logging.level.com.muggle=debug
spring.profiles.include=refresh
自动化配置默认是不开启的,我们需要使用 poseidon.auto=true 来启用相关功能,当开启自动化配置之后,我们必须要实现两个接口并注入到spring容器—— com.muggle.poseidon.store.SecurityStore 和 com.muggle.poseidon.service.TokenService 。poseidon.static-path 是 ant 匹配的静态资源路径,符合该规则的url不会被权限过滤器拦截,poseidon.ignore-path 是鉴权忽略规则,符合该规则的url不会参与鉴权,直接放行。logging.config=classpath:poseidon-logback.xml 则是采用 poseidon-boot-starter 中的logback配置策略(五彩斑斓的黑),如果采用该配置则必须指定 log.dir 日志文件输出路径。logging.level.com.muggle=debug 是指定包名以debug的级别输出,方便看一些日志调试。spring.profiles.include=refresh 当指定这个 profile 的时候,会去获取当前项目的所有url并交给 tokenService去处理。还有其他默认不开启的功能,在源码解读中介绍。
源码解读
前文我们提到过,该项目是基于 springboot 的 starter 功能开发的,其原理就是一个 springboot 定制版的 spi 这里不做太多介绍,这里我主要介绍如何在项目中使用的。
首先在 META-INF/spring.factories,中指定了要注入的类有哪些:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.muggle.poseidon.auto.ExpansibilityConfig,\
com.muggle.poseidon.auto.SecurityAutoConfig,\
com.muggle.poseidon.handler.web.WebUrlHandler,\
com.muggle.poseidon.handler.web.WebResultHandler
ExpansibilityConfig 是预留的配置类,实际未使用,SecurityAutoConfig 是整合 spring-security 相关的配置。WebUrlHandler 是处理一些特殊的url的。WebResultHandler 是统一异常处理配置。这几个类都通过 @ConditionalOnProperty(prefix = "poseidon", name = "auto", havingValue = "true", matchIfMissing = false) 来控制是否自动配置。配置类具体的源码细节这里就不介绍了。下面对各个功能的源码进行解读。
security
项目集成了security,并重写了处理器和鉴权相关的类,改造成了纯返回json,并从请求头中获取token的方式。首先我们看重写了哪些处理器:
com.muggle.poseidon.handler.security.PoseidonAccessDeniedHandler鉴权失败处理器;com.muggle.poseidon.handler.security.PoseidonAuthenticationFailureHandler登录失败处理器;com.muggle.poseidon.handler.security.PoseidonAuthenticationSuccessHandler登录成功处理器;com.muggle.poseidon.handler.security.PoseidonLoginUrlAuthenticationEntryPoint未登录处理器;com.muggle.poseidon.handler.security.PoseidonLogoutSuccessHandler登出成功处理器。
以上几个处理器都是返回json的数据,如果需要修改json格式或者需要改成重定向的方式,需要手动去找到相关处理器去修改;因为这部分相关工作(比如重定向或者提示信息)都可以在前端解决,所以这里未做扩展处理。
然后是 token过滤器 com.muggle.poseidon.filter.SecurityTokenFilter,该过滤器会首先从请求头中获取token,如果获取失败则会从cookie 中获取token,key都是 token,获取到token后调用 securityStore.getUserdetail(String token) 得到一个 UserDetails ,因此,怎么通过token获取用户信息需要使用者自己去扩展,你可以直接从数据库中读,或者从缓存中读,或者直接就像jwt那样,通过解析token生成。在接下来的鉴权流程中。会从该 UserDetails 中获取 GrantedAuthority 集合 和 url 一并传递给 rooleMatch(Collection<? extends GrantedAuthority> authorities, String path) 去鉴权(如果匹配为 IgnorePath 则不鉴权直接通过)。这里的鉴权方案也是需要使用者去自己实现,鉴权方案肯定是通过匹配url来实现,那么怎么去匹配设计方案就很多了,这里提供几个思路:
- 当配置
spring.profiles.include=refresh的时候会去获取项目中的所有url和相关的swagger注释。交给TokenService.processUrl(List<AuthUrlPathDO> list)去处理,你可以保存到数据库,为后续鉴权提供依据。 - 你可以制定一套url的命名规则,当鉴权的时候和
GrantedAuthority进行直接匹配,通过规则我们就能直接判断哪些用户是有权限访问的了。 - 前端发请求的时候,在url末尾带上一个参数来指定哪些角色可访问(不安全,可通过伪造请求跳过鉴权)。
在 TokenService 和 SecurityStore 中还有其他相关的方法,如登入登出等,这里不做介绍了,请参看源码注释。
统一异常处理
统一异常处理相关的类是 WebResultHandler 它定义了一些异常信息的处理策略。如果你不想要这些策略可以直接删掉它,或者自己重新注入一个异常处理器,如果你想扩展它,那么你可以参考项目中readme.md文档中的案例:
@RestControllerAdvice
@Configuration
public class MyWebResultHandler extends WebResultHandler {
private static final Logger log = LoggerFactory.getLogger(OAwebResultHandler.class);
@ExceptionHandler({ConstraintViolationException.class})
public ResultBean methodArgumentNotValidException(ConstraintViolationException e, HttpServletRequest req) {
log.error("参数未通过校验", e);
ResultBean error = ResultBean.error(e.getConstraintViolations().iterator().next().getMessage());
return error;
}
}
需要注意的一个地方,如果我们项目中出现了未知的异常,应该要引起重视,因此当发生未知异常的时候会抛出一个事件。使用者可以注册监听器来监听这个事件,当发生未知的异常的时候可以及时的通知到开发人员,示例:
@Component
public class ExceptionListener implements ApplicationListener<ExceptionEvent> {
@Override
public void onApplicationEvent(ExceptionEvent event) {
String message = event.getMessage();
// TODO 将异常信息投递到邮箱等,通知开发人员系统异常,尽快处理。
}
}
请求日志及幂等锁
想要使用请求日志的功能需要实现 DistributedLocker 接口并注册到spring容器中以激活日志切面。然后再需要拦截的方法上加上 @InterfaceAction 当我们请求这个方法时就会以info级别将请求参数输入到日志中,目前日志格式是写死的,格式形如:
INFO com.muggle.poseidon.aop.RequestAspect - 》》》》》》 请求日志 用户名:用户未登录 url=/user/regester.jsonmethod=POSTip=127.0.0.1host=127.0.0.1port=57180classMethod=com.muggle.poseidon.oa.controller.UserController.regesterparamters [ (OaUserVO(gender=1, username=muggle, password=xxxxxx, email=null, imgUrl=null)) ]
如果想做幂等拦截 则需要在注解上添加参数 @InterfaceAction(Idempotent = true,message = "请求太频繁,请稍后再试") ,Idempotent 是否开启幂等拦截,
message 是 被拦截后的提示信息,expertime 是幂等锁时长 。开启拦截后会 拼接一个 key String key = "lock:idempotent:" + request.getRequestURI() + ":" + username + ":" + RequestUtils.getIP(request); 然后调用 DistributedLocker.trylock(String key, Long express) 方法进行上锁,express 参数就是注解上配置 expertime,上锁方式需要使用者自己实现,你可以用redis,zookeeper,或者缓存来上锁。
部分使用者可能希望能把请求相关的信息存储到数据库,我也提供了扩展接口:RequestLogProcessor 只要实现该接口并注册到 spring 你就能在recordBefore 方法中拿到 请求相关信息 ,在recordAfterReturning 方法中拿到返回值,注意如果方法抛出异常,是不会拿到返回值的,需要自己去修改源码添加异常切面方法,异常切面方法的注解是 @AfterThrowing。
日志配置
日志配置主要是两个地方,一个是 banner.txt另外一个是 poseidon-logback.xml 如果小伙伴不喜欢这个banner想去掉,只需要在自己的项目中添加一个 banner.txt 进行覆盖就行了。
poseidon-logback.xml 是对日志格式等的配置,通过 logging.config=classpath:poseidon-logback.xml 来启用该配置,同时需要指定日志文件输出路径 log.dir=/temp/xxx,启用该配置后你就可以在控制台上看到五彩斑斓的黑,如果小伙伴不喜欢这个配色,可以根据配置文件中的注释去修改。
查询配置
做出查询配置这个功能是为了减少平时开发写查询接口的开发成本,这个功能本身是结合 mybatis 的 pagehelper 插件使用的,如果你没有用这个插件,那就享受不到这个福利了。
由于各个公司或者的查询要求不尽相同,所以这里我也只做了一个顶层抽象。具体查询策略还是需要开发者去实现,将扩展性预留了出来。下面介绍这个功能的思路。
查询bean的 顶层抽象为 com.muggle.poseidon.base.BaseQuery,这里面定义查询的一些通用属性。然后在 com.muggle.poseidon.aop.QueryAspect 中拦截查询方法,拦截规则是类名必须要以 Controller 结尾,入参必须是 BaseQuery 的子类。
这个切面是没有注册的,需要手动注册一下:
@Bean
QueryAspect getQueryAspect(){
return new QueryAspect();
}
在切面的 doBefore(JoinPoint joinPoint) 中 对查询参数进行转化,在doAfterReturning(JoinPoint joinPoint, Object result)
对查询的返回值进行再次处理。实际使用中小伙伴就根据项目需求进行扩展吧。
一些基础类的封装
com.muggle.poseidon.util 收集了一些工具类,小伙伴们请按需增删。com.muggle.poseidon.base包下的 com.muggle.poseidon.base.ResultBean是对 controller 层的返回值的bean的封装。exception 包下是自定义异常的顶层抽象类。
结语
目前项目只发布了 BETA 版,后续不会再在这个版本上加新功能,当版本稳定后,我会在这个版本基础上发布一个 REALSE 版本。如果小伙伴发现bug,或者有改进意见,或者对这个项目有新的需求请务必联系我,撸码不易,点个star支持一下吧,球球了。
分享一个springboot脚手架的更多相关文章
- 看了 Spring 官网脚手架真香,也撸一个 SpringBoot DDD 微服务的脚手架!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 为什么我们要去造轮子? 造轮子的核心目的,是为了解决通用共性问题的凝练和复用. 虽然 ...
- 基于IDEA Plugin插件开发,撸一个DDD脚手架
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 最近很感兴趣结合 IDEA Plugin 开发能力,扩展各项功能.也基于此使用不同的案例,探索 ...
- Eclipse 创建第一个 springboot 应用
1.前言 一直想把笔记整理出来,分享一下 springboot 的搭建: 因为私下 idea 用的比较多,使用比较方便,但恰逢小伙伴问起 eclipse 怎么搭建的问题, 顾整理以记之. 2.spri ...
- 整理代码,将一些曾经用过的功能整合进一个spring-boot
一 由于本人的码云太多太乱了,于是决定一个一个的整合到一个springboot项目里面. 附上自己的项目地址https://github.com/247292980/spring-boot 功能 1. ...
- springboot脚手架liugh-parent源码研究参考
1. liugh-parent源码研究参考 1.1. 前言 这也是个开源的springboot脚手架项目,这里研究记录一些该框架写的比较好的代码段和功能 脚手架地址 1.2. 功能 1.2.1. 当前 ...
- 完成一个springboot项目的完整总结一
一. 项目的基础环境的搭建 1.javaJDK的安装和配置环境变量 2.mysql 3.eclipse 二.项目高级环境的搭建 使用maven前,一定要先安装JDK 1) 解压maven到briup目 ...
- 手撸一个SpringBoot的Starter,简单易上手
前言:今天介绍一SpringBoot的Starter,并手写一个自己的Starter,在SpringBoot项目中,有各种的Starter提供给开发者使用,Starter则提供各种API,这样使开发S ...
- 5分钟快速搭建一个springboot的项目
现在开发中90%的人都在使用springboot进行开发,你有没有这样的苦恼,如果让你新建一个springboot开发环境的项目,总是很苦恼,需要花费很长时间去调试.今天来分享下如何快速搭建. 一 ...
- 分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)
分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...
随机推荐
- 博客营销(Blog Marketing)
一.什么是博客营销 博客营销(Blog Marketing)的概念可以说并没有严格的定义,简单来说,就是利用博客这种网络应用形式开展网络营销.要说明什么是博客营销,首先要从什么是博客说起. 博客(Bl ...
- 苏浪浪 201771010120《面向对象程序设计(java)》第六章学习总结
第五章 主要学习OOP另一个部分----继承,继承使程序员可以使用现有的类,并根据需要进行修改.这是Java程序设计中的一个基础设计. 1.类.超类和子类: (1) 已有类称为:超类(supercla ...
- 王玉兰201771010128《面向对象与程序设计(Java)》第十一周学习总结
一:理论知识部分: (1)集合:集合(Collection或称为容器)是一种包含多个元素并提供对所包含元素操作方法的类,其包含的元素可以由同一类型的对象组成,也可以由不同类型的对象组成. A:集合类的 ...
- 强连通 反向建图 hdu3639
Hawk-and-Chicken Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- PHP 获取当前目录下的所有文件
我们有时候会想拿到当前目录下的所有文件名,以下就是我写的一个方法,请大家参考 // 获取当前文件的上级目录 $con = dirname(__FILE__); // 扫描$con目录下的所有文件 $f ...
- SPL数据结构
数据结构是计算机存储.组织数据的方式. SPL提供了双向链表.堆栈.队列.堆.降序堆.升序堆.优先级队列.定长数组.对象容器. 基本概念Bottom:节点,第一个节点称Bottom:Top:最后添加的 ...
- 【C++】运算符优先级和结合性
从上表可以大致总结这样一个规律: 优先级 运算符 高 初等运算符 | 单目运算符 | 算术运算符(先乘除,后加减) | 关系运算符 | 逻辑运算符(不包括!) | 条件运算符 ↓ 赋值运算符 低 逗号 ...
- [安卓基础] 004.运行app
运行你的app 这篇课程会教你: 1.如何在设备上运行你的app. 2.如何在模拟器上运行你的app. 当然,在学习之前,你还需要知道: 1.如何使用设备. 2.如何使用模拟器. 3.管理你的项目. ...
- parrot os的一些坑
burpsuite 破解版的运行环境需要jdk8,jdk11无法运行 选择jdk环境 update-alternatives --config java 截图工具 sudo apt install f ...
- 内部服务器错误Internal server error解决方法
这里说的内部服务器错误是网站前台能正常访问,而后台程序在执行某项任务/功能时所出现的内部服务器错误解决方法: 此错误通常是超时导致的,像程序在执行采集.静态页面生成时所耗时间太长导致达到超时限制的: ...