项目介绍

在我们开发项目的时候各个项目之间总有一些可共用的代码或者配置,如果我们每新建一个项目就把代码复制粘贴再修改就显得很没有必要。于是我就做了一个 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.SecurityStorecom.muggle.poseidon.service.TokenServiceposeidon.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来实现,那么怎么去匹配设计方案就很多了,这里提供几个思路:

  1. 当配置 spring.profiles.include=refresh 的时候会去获取项目中的所有url和相关的swagger注释。交给 TokenService.processUrl(List<AuthUrlPathDO> list) 去处理,你可以保存到数据库,为后续鉴权提供依据。
  2. 你可以制定一套url的命名规则,当鉴权的时候和 GrantedAuthority 进行直接匹配,通过规则我们就能直接判断哪些用户是有权限访问的了。
  3. 前端发请求的时候,在url末尾带上一个参数来指定哪些角色可访问(不安全,可通过伪造请求跳过鉴权)。

TokenServiceSecurityStore 中还有其他相关的方法,如登入登出等,这里不做介绍了,请参看源码注释。

统一异常处理

统一异常处理相关的类是 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脚手架的更多相关文章

  1. 看了 Spring 官网脚手架真香,也撸一个 SpringBoot DDD 微服务的脚手架!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 为什么我们要去造轮子? 造轮子的核心目的,是为了解决通用共性问题的凝练和复用. 虽然 ...

  2. 基于IDEA Plugin插件开发,撸一个DDD脚手架

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 最近很感兴趣结合 IDEA Plugin 开发能力,扩展各项功能.也基于此使用不同的案例,探索 ...

  3. Eclipse 创建第一个 springboot 应用

    1.前言 一直想把笔记整理出来,分享一下 springboot 的搭建: 因为私下 idea 用的比较多,使用比较方便,但恰逢小伙伴问起 eclipse 怎么搭建的问题, 顾整理以记之. 2.spri ...

  4. 整理代码,将一些曾经用过的功能整合进一个spring-boot

    一 由于本人的码云太多太乱了,于是决定一个一个的整合到一个springboot项目里面. 附上自己的项目地址https://github.com/247292980/spring-boot 功能 1. ...

  5. springboot脚手架liugh-parent源码研究参考

    1. liugh-parent源码研究参考 1.1. 前言 这也是个开源的springboot脚手架项目,这里研究记录一些该框架写的比较好的代码段和功能 脚手架地址 1.2. 功能 1.2.1. 当前 ...

  6. 完成一个springboot项目的完整总结一

    一. 项目的基础环境的搭建 1.javaJDK的安装和配置环境变量 2.mysql 3.eclipse 二.项目高级环境的搭建 使用maven前,一定要先安装JDK 1) 解压maven到briup目 ...

  7. 手撸一个SpringBoot的Starter,简单易上手

    前言:今天介绍一SpringBoot的Starter,并手写一个自己的Starter,在SpringBoot项目中,有各种的Starter提供给开发者使用,Starter则提供各种API,这样使开发S ...

  8. 5分钟快速搭建一个springboot的项目

      现在开发中90%的人都在使用springboot进行开发,你有没有这样的苦恼,如果让你新建一个springboot开发环境的项目,总是很苦恼,需要花费很长时间去调试.今天来分享下如何快速搭建. 一 ...

  9. 分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)

    分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...

随机推荐

  1. vscode格式化Vue出现的问题:单引号变双引号 格式化去掉分号

    学习vue框架时,发现在使用vscode格式化vue代码时,出现单引号变成了双引号问题(导致和EsLint要求不一致),从而导致报错!!!!好坑啊!!! 解决方法如下 在文件根目录下创建 .prett ...

  2. MySQL事务锁等待超时 Lock wait timeout exceeded; try restarting transaction

    工作中处理定时任务分发消息时出现的问题,在查找并解决问题的时候,将相关的问题博客收集整理,在此记录下,以便之后再遇到相同的问题,方便查阅. 问题场景 问题出现的场景: 在消息队列处理消息时,同一事务内 ...

  3. protected和private的区别

    1. protected和private在没有继承关系的类A和类B之间其作用都可以视为式一样的--表示私有--每个类中的protected字段/属性都不能被访问到: 2. 当类与类之间存在继承关系时候 ...

  4. Java——MVC模式

    MVC:Model View Controller 一般用于动态程序设计,实现了业务逻辑和表示层分离 Model:掌控数据源-->程序员编写程序或者实现算法,数据库人员进行数据库操作等:响应用户 ...

  5. React Router简单Demo

    简介 react router是使用react的时候首选的一个路由工具. 安装 react router包含react-router,react-router-dom和react-router-nat ...

  6. python的map,reduce函数与pandas的apply,filter函数

    1. python自带的apply.filter.map函数.reduce函数,很多情况下可以代替for循环: map(func,list),对list的每个元素分别执行func函数操作,显然func ...

  7. Excel常用小方法

    Excel快捷键 Excel中处理工作表的快捷键 插入新工作表 Shift+F11或Alt+Shift+F1 移动到工作簿中的下一张工作表 Ctrl+PageDown 移动到工作簿中的上一张工作表 C ...

  8. ASP.NET Core 依赖注入最佳实践与技巧

    ASP.NET Core 依赖注入最佳实践与技巧 原文地址:https://medium.com/volosoft/asp-net-core-dependency-injection-best-pra ...

  9. Spring Boot笔记(五) SpringBoot 集成Lombok 插件

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 为了减少代码量,为当前项目添加 lombok 来优雅编码 Lombok 插件安装: a . 添加依赖: ...

  10. Java并发编程 (二) 并发基础

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.CPU多级缓存-缓存一致性 1.CPU多级缓存 ​ 上图展示的是CPU高级缓存的配置,数据的读取和存 ...