分享一個springboot腳手架

muggle1發表於2020-06-13

專案介紹

在我們開發專案的時候各個專案之間總有一些可共用的程式碼或者配置,如果我們每新建一個專案就把程式碼複製貼上再修改就顯得很沒有必要。於是我就做了一個 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支援一下吧,球球了。

點選關注我的部落格

相關文章