讓ERP的服務更開放! ——用微服務架構搭建的一套基於EBS的API服務系統

1. 源碼下載地址

源碼鏈接: https://github.com/samt007/xygerp-api-demo

這是用Spring Cloud微服務架構搭建的一套基於EBS的API服務系統
如對本文有任何的疑問,請聯繫我:[email protected]

2. Introduction介紹

這是一篇傳統ERP系統和基於Java的微服務架構有效結合的技術文檔。

傳統ERP關注的是企業內部的信息化管理。當ERP系統能將其服務發佈出去之後(結合微服務架構),就可以很好實現與第三方系統的無縫對接,同時也可以實現擴展ERP本身的功能。
目標是:讓ERP的服務更開放!

2.1 它有什麼用?

簡單來說,就是:

相當於做一箇中間服務平臺,把ERP的功能做成Web Service與其它系統集成。

下面具體說明它的作用。

1. 如果沒有它…

如果沒有一個統一的API對接平臺,那麼ERP和第三方系統做對接,那會是下圖所示的結構:
這裏寫圖片描述

從上圖可以看出,各個第三方系統分別和ERP做對接,無論是DBLINK還是通過自己的Web Service, 都是雜亂的,沒能統一管理的。簡單來說就是各自爲政,爲對接而需要做的事情都是零碎的。

當第三方系統越來越多的時候,那對於日常的運維將會是一個災難的問題。舉個例子,就一個簡單的運維:密碼修改,需要調整的地方就會很多,也很容易遺漏。

特別指出的是,接口功能的複用方面也是個難題。假設一個查詢庫存的接口,CRM系統和在線下單系統都可以用的,需要開發2次。

2. 自從有了它…

有了這套統一的API系統之後,ERP系統和別的系統之間的對接就變成了這個結構:

這裏寫圖片描述

所以,有了它,相當於ERP的API都可以通過這服務平臺給開發出去,基本上所有的接口可以完成的業務,都可以通過這套服務平臺來完成。

可以實現:

  • 對外服務的統一
  • API服務之間可以實現互相調用,並且實現服務取數和處理的邏輯的統一
  • 代碼的統一,提高開發效率。特別是comm代碼的部分。
  • 提高與第三方系統對接的穩定性:只需要關注該微服務系統的運行穩定性即可。

3. 有哪些實例?

舉個例子:

1) 成品進出條碼管理系統:

大概這個需求:
成品入庫的時候,直接可以用條碼槍掃條碼或者二維碼就可以入庫;銷售出庫的時候,也可以通過刷成品的條碼直接進行出庫。JIT管理。

通過這個系統的實現邏輯是:
通過EBS的用戶名和密碼可以登入條碼槍上的APP系統。然後,刷條碼的時候,通過該Web Service可以產生對應的事務處理!例如完工入庫,處理物料搬運單等。

下面是該系統的一些截圖

注意:該功能後臺API由該微服務提供,前臺是安卓的APP

這裏寫圖片描述

2) 與各個第三方系統的集成:

每個企業內部都有各種第三方系統,這些系統或多或少都需要和EBS進行集成。傳統的集成方法是通過DBLINK。

但是有這套Web Service系統之後,就可以統一通過該Web Service作爲中介,和EBS進行數據的集成交互!

2.2 什麼是微服務?

關於它的解析,網上資料很多。
這裏引用某位大神的總結(引用:http://blog.51cto.com/ityouknow/1974080),解析得比較到位:

微服務的概念源於2014年3月Martin Fowler所寫的一篇文章“Microservices”。

微服務架構是一種架構模式,它提倡將單一應用程序劃分成一組小的服務,服務之間互相協調、互相配合,爲用戶提供最終價值。每個服務運行在其獨立的進程中,服務與服務間採用輕量級的通信機制互相溝通(通常是基於HTTP的RESTful
API)。每個服務都圍繞着具體業務進行構建,並且能夠被獨立地部署到生產環境、類生產環境等。另外,應儘量避免統一的、集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言、工具對其進行構建。

微服務是一種架構風格,一個大型複雜軟件應用由一個或多個微服務組成。系統中的各個微服務可被獨立部署,各個微服務之間是鬆耦合的。每個微服務僅關注於完成一件任務並很好地完成該任務。在所有情況下,每個任務代表着一個小的業務能力。

微服務架構優勢

複雜度可控:在將應用分解的同時,規避了原本複雜度無止境的積累。每一個微服務專注於單一功能,並通過定義良好的接口清晰表述服務邊界。由於體積小、複雜度低,每個微服務可由一個小規模開發團隊完全掌控,易於保持高可維護性和開發效率。

獨立部署:由於微服務具備獨立的運行進程,所以每個微服務也可以獨立部署。當某個微服務發生變更時無需編譯、部署整個應用。由微服務組成的應用相當於具備一系列可並行的發佈流程,使得發佈更加高效,同時降低對生產環境所造成的風險,最終縮短應用交付週期。

技術選型靈活:微服務架構下,技術選型是去中心化的。每個團隊可以根據自身服務的需求和行業發展的現狀,自由選擇最適合的技術棧。由於每個微服務相對簡單,故需要對技術棧進行升級時所面臨的風險就較低,甚至完全重構一個微服務也是可行的。

容錯:當某一組建發生故障時,在單一進程的傳統架構下,故障很有可能在進程內擴散,形成應用全局性的不可用。在微服務架構下,故障會被隔離在單個服務中。若設計良好,其他服務可通過重試、平穩退化等機制實現應用層面的容錯。

擴展:單塊架構應用也可以實現橫向擴展,就是將整個應用完整的複製到不同的節點。當應用的不同組件在擴展需求上存在差異時,微服務架構便體現出其靈活性,因爲每個服務可以根據實際需求獨立進行擴展。

2.3 ERP API微服務系統架構說明

2.3.1 系統開發邏輯說明

從上面的解析得知:微服務是一種技術架構,將一個龐大的服務體系拆分爲若干個子服務執行。
問題來了,應該如何拆分呢?就是服務的拆分原則是什麼。
這個問題就像是一個大表如何進行分區一樣,其實我覺得是具體問題具體分析。
由於我開發的是基於EBS的微服務系統,正常來說,比較合理的劃分規則應該是以EBS的模塊來分。

相當於每個模塊都劃分爲一個單獨的微服務。例如FND模塊,INV模塊,WIP模塊等等。

有時候,爲了某個目的,可能有些功能是定製的,需要提取幾個模塊的數據來用,而且被別的模塊重用的概率很低。所以,實際上也可以以定製的功能來劃分微服務。

目前來說,該系統包括2個子服務:

  • xygerp-ald服務:ald模塊
    這個是整個微服務API的核心ald模塊。這個模塊的主要功能是驗證用戶的登錄,爲所有的api模塊提供統一的token認證。相當於ebs的FND模塊。

  • xygerp-albc服務:albc子模塊
    這個項目是屬於微服務的API模塊之一:條碼管理系統提供數據以及數據處理的API。
    主要是爲條形碼傳輸系統用。

當然,未來可以添加若干個服務。微服務架構的優勢是有很好的橫向擴展能力!

2.3.2 微服務系統架構圖

該系統的架構圖如下所示。
這裏寫圖片描述

注意:
1.Spring Cloud模塊中,實際上Spring Security並不是單獨的一個模塊,而是融入到每一個業務微服務模塊中! 每個微服務都必須要有token認證才允許訪問API,它非常重要! 所以我將它給列到Spring Cloud模塊中。

2.圖中有些模塊目前還沒有實現。
目前實現了架構整體,包括以下的服務(下一個章節會具體說明每個模塊的用途):
xygerp-ald
xygerp-albc
xygerp-server-eureka
xygerp-server-zuul

注意: 以後會按需添加別的模塊。

3 系統開發流程

接下來是一步一步來開發一套這個基於Spring Cloud的微服務系統。

3.1 必須掌握的基礎開發技術知識點

開發系統都必須要打好基礎。所以,這裏列出了開發基於Spring Cloud的微服務系統需要掌握的開發技術。

下面我不會具體解說每一個開發技術如何學習,因爲這並不是本文的重點。工慾善其事必先利其器,基礎還是必須要打好。

1)Java語言

必須要熟悉java,否則基本不用看文檔了。先打好基礎吧!

2)Maven項目管理工具

系統開發的項目都是以maven做項目管理的,所以必須要先安裝並掌握maven工具。

3)Oracle數據庫+PLSQL+SQL語言

數據庫端的開發技術。這裏選用Oracle數據庫,因爲EBS就是基於Oracle數據庫的ERP系統。

3.2 需要熟悉的java框架

Java技術發展到現在,已經出現了許多非常優秀的開源框架,我們可以藉助這些框架來快速開發系統。

3.2.1 Spring框架技術棧(全家桶)

Spring是目前開源的主流的技術包。
該系統主要用到的技術棧是:Spring boot,Spring Security以及Spring Cloud。

  1. Spring Boot
    系統基於SpringBoot快速開發。選擇目前熱度很高的SpringBoot,最大限度地降低配置複雜度,把大量的精力投入到業務開發中來。

  2. Spring MVC
    利用Spring MVC框架處理所有的url請求,簡單易用。

  3. Spring Security
    Spring security是一個強大的和高度可定製的身份驗證和訪問控制框架。它是確保基於Spring的應用程序的標準。
    這裏主要是用Spring Security框架處理Token機制。

  4. Spring Cloud
    Spring Cloud是一系列框架的有序集合。
    它利用Spring Boot的開發便利性巧妙地簡化了分佈式系統基礎設施的開發,如服務發現註冊、配置中心、消息總線、負載均衡、斷路器、數據監控等,都可以用Spring Boot的開發風格做到一鍵啓動和部署。
    注意:本系統目前使用Spring Cloud的2個模塊

    • 請求統一通過API網關(Zuul)來訪問內部服務.
    • 網關接收到請求後,從註冊中心(Eureka)獲取可用服務

3.2.2 MyBatis

ORM框架選用MyBatis。
主要是考慮到它能夠很好支持SQL語句:MyBatis是支持普通SQL查詢,存儲過程和高級映射的優秀持久層框架。
另外,還用到了MyBatis的一些提高開發效率的插件,特別是通用Mapper和PageHelper分頁插件!

3.2.3 Alibaba druid

DRUID是阿里巴巴開源平臺上一個數據庫連接池實現。它結合了C3P0、DBCP、PROXOOL等DB池的優點,同時加入了日誌監控,可以很好的監控DB池連接和SQL的執行情況。

3.2.4 Swagger

前端和後端的唯一聯繫,變成了API接口。
API文檔變成了前後端開發人員聯繫的紐帶,變得越來越重要,swagger就是一款讓你更好的書寫API文檔的框架。

3.3 需要準備的軟件工具

3.3.1Redis

目前用Redis的主要作用是存取token,以配合實現Spring Security完成api訪問的安全機制。

以後可以考慮做緩存或者消息隊列等高級功能。

3.3.2Docker

基於Docker的容器化部署。
由於使用了微服務架構後,我們的系統將會由很多子系統構成。
爲了達到多個系統之間環境隔離的目的,我們可以將它們部署在多臺服務器上,可這樣的成本會比較高,而且每臺服務器的性能可能都沒有充分利用起來。
所以我們很自然地想到了虛擬機,在同一臺服務器上運行多個虛擬機,從而實現環境的隔離,每個虛擬機上運行獨立的服務。
然而虛擬機的隔離成本依舊很高,因爲它需要佔用服務器較多的硬件資源和軟件資源。
所以,在微服務結構下,要實現服務環境的隔離,Docker是最佳選擇。它比虛擬機更加輕量級,佔用資源較少,而且能夠實現快速部署。

備註:後面有專題說明這個工具如何安裝使用。由於篇幅原因,本文檔暫時不講解容器化部署。

3.3.3 Jenkins

Jenkins自動化構建工具。

當我們採用了微服務架構後,我們會發現這樣一個問題。整個系統由許許多多的服務構成,這些服務都需要運行在單獨的容器中,那麼每次發佈的複雜度將非常高。
首先你要搞清楚這些服務之間的依賴關係、啓動的先後順序,然後再將多個子系統挨個編譯、打包、發佈。這些操作技術難度低,卻又容易出錯。
那麼有什麼工具能夠幫助我們解決這些問題呢?答案就是——Jenkins。
它是一款自動化構建的工具,簡單的來說,就是我們只需要在它的界面上按一個按鈕,就可以實現上述一系列複雜的過程。

備註:後面有專題說明這個工具如何安裝使用。由於篇幅原因,本文檔暫時不講解自動化構建。

3.4 具體開發流程

現在開始手把手來搭建一套這樣子的系統。

3.4.1 創建Maven項目的組織結構

先創建一個微服務系統的父級項目:xygerp-api

再在這個項目下面分別創建下面幾個子項目:

項目名稱 說明
xygerp-ald ald模塊,端口:8180。這個是整個微服務API的核心ald模塊。這個模塊的主要功能是驗證用戶的登錄,爲所有的api模塊提供統一的token認證。相當於ebs的FND模塊。
xygerp-albc albc子模塊,端口:8181。這個項目是屬於微服務的API模塊之一:條碼管理系統提供數據以及數據處理的API。主要是爲條形碼傳輸系統用。
xygerp-comm comm模塊這個項目是所有API項目的核心依賴項目。說白了就是將API微服務架構的所有項目的公用代碼可以抽取在這裏。
xygerp-basic-support 核心基礎支撐模塊這個項目是所有API項目的基礎數據支撐項目。這裏統一歸集了所有的Entity!因爲對於Entity來說,應該是整個微服務都公用的。
xygerp-server-eureka Spring Cloud的服務與發現的服務中心。端口:8101。這個模塊是Spring cloud的最核心的模塊了,用來處理各個微服務之間的服務調用的。
xygerp-server-zuul Spring Cloud服務網關。端口:8102。在Spring Cloud架構體系內的所有微服務都通過Zuul來對外提供統一的訪問入口,所有需要和微服務架構內部服務進行通訊的請求都走統一網關。
它們的目錄結果是這樣子的:

這裏寫圖片描述

注意:
關於xygerp-basic-support:核心基礎支撐模塊
可能您會有疑問:爲什麼不將entity歸併在它所屬的模塊?其實是這樣的,我主要是考慮到服務之間的互相調用的問題。
微服務雖然客觀上是一個單獨的服務,但是,實際上大部分的功能肯定是互相調用的。舉個例子,銷售訂單模塊調用庫存模塊的功能查詢個庫存是很正常的業務吧?
如果entity不共用的話,相當於銷售模塊得到的庫存模塊的結果無法歸集爲bean來處理,這樣子對於後臺的處理會帶來極大的不便!

3.4.2 構建模塊的依賴關係

接着需要通過pom文件來指定它們之間的依賴關係,依賴關係如下圖所示。

1. 業務服務部分:

這裏寫圖片描述
注意,上面的4個項目是有依賴關係的。
所以,xygerp-ald和xygerp-albc部署方式 改爲war部署 (主要是爲了利用jenkins進行自動化部署)。
而xygerp-comm和xygerp-basic-support只是爲各個微服務提供基礎代碼支撐,所以是jar部署即可。
此外,爲了簡化各個模塊的配置,我們將所有模塊的通用依賴放在Project的pom文件中,然後讓所有模塊作爲Project的子模塊。
這樣子模塊就可以從父模塊中繼承所有的依賴,而不需要自己再配置了。

在父pom中指定子模塊
modules標籤指定了當前模塊的子模塊是誰,但是僅在父模塊的pom文件中指定子模塊還不夠,還需要在子模塊的pom文件中指定父模塊是誰。

    <modules>
        <module>xygerp-comm</module>          <!--核心Comm模塊 -->
        <module>xygerp-basic-support</module>    <!--核心基礎支撐模塊 -->
        <module>xygerp-server-eureka</module>   <!--Eureka服務治理模塊 -->
        <module>xygerp-server-zuul</module>      <!--zuul動態路由模塊 -->
        <module>xygerp-ald</module>             <!--核心ald模塊 -->
        <module>xygerp-albc</module>           <!--子模塊:albc模塊,提供條碼對接API服務 -->
    </modules>

需要在子模塊中指定父模塊

    <parent>
        <artifactId>xygerp</artifactId>
        <groupId>com.xygerp</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
</parent>

備註:具體代碼直接看源碼吧。這裏只是提及了一些重點設置而已。

所以,到此爲止,模塊的依賴關係配置完畢!但要注意模塊打包的順序。

由於所有模塊都依賴於xygerp-comm模塊和xygerp-basic-support模塊,因此在構建模塊時,首先需要編譯、打包、安裝xygerp-comm模塊和xygerp-basic-support模塊,將它打包進本地倉庫中,這樣上層模塊才能引用到。當該模塊安裝完畢後,再構建上層模塊。
否則在構建上層模塊的時候會出現找不到xygerp-comm模塊中類庫的問題。

Tips: 其實,如果是在父級目錄直接用mvn package整體打包的話,那打包構建的順序在父pom中是直接指定了!

2. 微服務架構服務治理部分

xygerp-server-eureka:Spring Cloud服務註冊和發現。就是處理服務之間的治理。
xygerp-server-zuul:Spring Cloud的統一API網關服務。

Tips: 這2個項目是爲了實現微服務架構而用到的核心服務。所以,它們是相對獨立的。不需要依賴父pom。

3.4.3用mvn編譯命令打包代碼

上面的項目都建立好之後,再添加所有項目都需要用到的依賴(具體代碼可以參考我的源碼)。

都沒問題的話,就可以用mvn命令進行打包項目了:

mvn clean install -Dmaven.test.skip=true -P dev

這裏簡單解析一下指令:
mvn:Maven的統一指令。
clean install:表示要構建該項目。
-Dmaven.test.skip=true:表示構建的時候要跳過測試模塊。
-P dev:表示構建的時候,啓用 dev 的Spring boot參數運行系統。

如果一切都OK,那正常的結果如下:

這裏寫圖片描述

3.5 本地電腦測試系統

代碼搞定了,接下來需要考慮的事情應該是如何測試。
畢竟所有的系統都必須要經過測試,特別是這種配置多,涉及範圍廣的系統。

這個就不得不說一下Spring boot的優勢了。Spring boot的打包應用默認內置了tomcat服務。
換句話說,只需要java命令執行一下Spring boot打包的target結果,就可以啓動一個tomcat服務啦!真挺方便測試的!

3.5.1 啓動本地系統的服務

假設我的xygerp-api項目在這裏:D:\JSP_MyEclipse\xygerp-api
然後分別打開4個cmd命令窗口,執行:

D:\JSP_MyEclipse\xygerp-api\xygerp-server-eureka\target>java -jar xygerp-server-eureka-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-server-zuul\target>java -jar xygerp-server-zuul-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-ald\target>java -jar xygerp-ald-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-albc\target>java -jar xygerp-albc-1.0-SNAPSHOT.war

如下圖:
這裏寫圖片描述

3.5.2 本地測試API服務系統

本地測試環境的服務啓動起來了,接着就是進行具體的數據測試。

首先測試Eureka的服務註冊以及發現,確認服務是否都已經註冊到系統中:
這裏寫圖片描述

然後,用swagger測試用戶登錄的功能:
http://127.0.0.1:8102/xygerp/ald/swagger-ui.html
目前是測試是否可以正常產生token。
這裏寫圖片描述
這裏寫圖片描述
說明已經登錄成功,並且產生了本次訪問的token!

將token記錄下來,接着測試。
繼續測試一個查詢的功能:
http://127.0.0.1:8102/xygerp/albc/swagger-ui.html
這裏寫圖片描述

注意,這裏用了Spring Security框架,所以的API請求頭都必須攜帶token信息。否則請求會返回401。

如果測試OK,那說明基本上系統已經成功搭建好了。
下一步就是如何在測試環境或者正式環境部署它,以及如何一鍵構建項目的問題了。

簡單來說,系統的部署是用 docker工具 ,一鍵部署項目用的是 Jenkins工具。後面將會用專題來說明這2個工具的使用。

3.6 該API微服務系統實現的功能難點

3.6.1 解決數據庫Session的環境變量問題,特別是語言環境和用戶環境。

關於這個問題,目前我用的辦法可能不一定是最優的,如果有別的兄臺有更好的解決辦法,請留言給我,十分感謝!

問題來源:

熟悉EBS開發的兄臺都應該知道,登錄ERP之後,我們每次打開Form,系統就會申請一個新的數據庫Session,這時候,EBS系統會 自動幫我們初始化該Session的環境變量 :例如基本的語言環境,用戶環境,業務實體等等。

這時候,我們在包裏面可以直接用FND_GLOBAL.USER_ID之類的函數就可以非常方便獲取環境變量的信息。

但是,在Java Web開發裏面就不一樣了!

在Java訪問數據庫的理念中,Session的申請是一個極耗資源的動作!所以,大部分連接數據庫的Java軟件都提出了一個 數據庫連接池 的概念(例如DRUID數據庫連接池)。簡單來說就是session共用!

Session公用就會帶來一個併發問題:A用戶使用系統,並初始化了該Session的環境變量爲A用戶;當A用戶不用系統的時候,Session會閒置並放回連接池裏面等待別的用戶使用。

這時候如果B的用戶很可能會使用該Session,如果不重新初始化環境變量的話,那B用戶使用系統的Session的環境變量還是A用戶,就會導致數據的bug!
如何處理該問題是開發該系統碰到的一個難題。

問題解決:

我目前的處理辦法是:在Service層,用AOP統一自動監控Service層的這個參數AuthUser user

只要在Service層將參數AuthUser user放在最後,AOP會自動初始化Session的環境變量。(需要注意的是,我這個系統的數據庫Transaction在Service層啓用!)

另外,語言環境變量,登錄ID環境變量等,會一併自動初始化。因爲AuthUser會攜帶該定義!

核心處理代碼如下:

private static final String SQL_GLOBAL_INIT
        = " DECLARE "
        + "    L_session_id NUMBER;L_user_id NUMBER;L_login_id NUMBER;L_LANG VARCHAR2(10); "
        + " BEGIN "
        + "    L_user_id:=:P_USER_ID; L_login_id:=:P_LOGIN_ID; L_LANG:=:P_LANG;"
        + "    APPS.fnd_global.INITIALIZE("
        + "       session_id=>L_session_id, user_id =>L_user_id, resp_id =>NULL, "
        + "       resp_appl_id=>NULL, security_group_id=>NULL, site_id=>NULL, login_id =>L_login_id, "
        + "       conc_login_id=>NULL, prog_appl_id=>NULL, conc_program_id=>NULL, conc_request_id=>NULL, "
        + "       conc_priority_request=>NULL"
        + "     ); "
        + "    IF NVL(L_LANG,'US') <> USERENV('LANG') THEN "
        + "        IF L_LANG='ZHS' THEN "
        + "            APPS.fnd_global.set_nls_context(p_nls_language => 'SIMPLIFIED CHINESE'); "
        + "        ELSE "
        + "            APPS.fnd_global.set_nls_context(p_nls_language => 'AMERICAN'); "
        + "         END IF;"
        + "    END IF;"
        + " END; ";

    /*** 
     * service層調用之前先自動初始化環境變量
     * 需要注意的是,用戶變量必須的參數放在最後!
     * 只要在Service層將參數AuthUser user放在最後,AOP會自動初始化Session的環境變量。
     * @throws Exception 
     */  
    @SuppressWarnings("static-access")
    @Before("execution(* com.jebms.*.service..*.*(..))  && args(..,user)")  
    public void oracleDBInit(JoinPoint joinPoint,AuthUser user) throws Exception{
        Long dbLoginId=devDao.getJdbcTemplate().queryForObject("SELECT FND_GLOBAL.LOGIN_ID FROM DUAL", Long.class);
if(user.getLoginId()!=null&&user.getLoginId()>0&&!user.getLoginId().equals(dbLoginId)){
            Map<String,Object> inParamMap=new HashMap<String,Object>();
            inParamMap.put("P_USER_ID", user.getUserId());
            inParamMap.put("P_LOGIN_ID", user.getLoginId());
            inParamMap.put("P_LANG", user.getLanguage());
            devDao.getDevJdbcTemplate().execute(this.SQL_GLOBAL_INIT, inParamMap);
        }
    }

源代碼在:com.jebms.comm.utils. AopUtil

3.6.2 解決EBS的用戶的登錄問題:統一用EBS系統的帳號密碼登錄API系統。

問題描述:

由於我這個是第三方的API系統,所以,用戶名和密碼信息實際上並不是該API系統需要管理的事情。

相當於說,API系統無法按照正常的流程來驗證用戶名和密碼:輸入用戶名和密碼,系統驗證後臺數據庫的用戶名和密碼,再返回驗證結果。
而是:輸入用戶名和密碼,系統 調用ERP的用戶名密碼驗證包 進行驗證,再返回結果。
簡單來說:需要添加自定義驗證的邏輯。

還好Spring Security框架支持靈活的驗證邏輯。

添加步驟:

首先,寫一個自定義驗證的類:MyAuthenticationProvider
接着,在Spring Security框架的定義中,添加這個自定義的驗證。
AbstractWebSecurityConfig
private MyAuthenticationProvider provider;//自定義驗證
auth.authenticationProvider(provider);

即可以完美實現這個效果

核心代碼:

    /**
     * 自定義驗證方式
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();
        AuthUser user = (AuthUser) userService.loadUserByUsername(username);
        System.out.println("username:"+username+",password:"+password);
        if(user == null){
            throw new BadCredentialsException("Username not found.");
        }

        //加密過程在這裏體現
        if (!sysService.xygErpValidateLogin(username, password)) {
            throw new BadCredentialsException("Wrong password.");
        }

        user.setPassword(passwordEncoder.encode(password));

        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        return new UsernamePasswordAuthenticationToken(user, password, authorities);
    }

3.6.3 統一的開發風格。

1.Entity基類封裝。

封裝了5who欄位,以及類似Form的FND_SET_WHO的方法,可以很方便進行開發。
另外,爲了防止丟失更新,這邊每次更新前實際上會先檢測數據的一致性,對應的動作也有做了封裝。

2.查詢邏輯的封裝。

查詢功能相對來說還是會很多,對於複雜的查詢條件如何傳值是一個難題。

這裏封裝了一個SearchInfo積累,可以統一將所有的查詢條件都放在這個類,然後在java的Controller層定義好查詢條件對應匹配欄位,系統就可以自動產生對應的and條件。

例如:

   @GetMapping(value = "/getPageLocator")
    @ApiOperation(value = "貨位分頁列表接口")
    public ResultEntity<PageInfo<EslipLocatorRE>> getPageLocator(
            @ApiParam(value = "庫存組織ID",required = true) @RequestParam(required = true) int organizationId,
            @ApiParam(value = "庫別代碼",required = true) @RequestParam(required = true) String subinventoryCode,
            @ApiParam(value = "貨位代碼",required = false) @RequestParam(required = false) String locatorCode,
            SearchInfo searchInfo) throws Exception {
        searchInfo.getConditionMap().put("organizationId", organizationId);
        searchInfo.getConditionMap().put("subinventoryCode", subinventoryCode);
        searchInfo.getConditionMap().put("locatorCode", locatorCode);
        searchInfo.setAuthUser(this.authUser);
        searchInfo.initSqlCondition();
        searchInfo.andSqlCondition("MIL.ORGANIZATION_ID","organizationId");
        searchInfo.andSqlCondition("MIL.SUBINVENTORY_CODE","subinventoryCode");
        searchInfo.andSqlCondition("MIL.SEGMENT1","locatorCode");
        return eslipService.selectForPageLocator(searchInfo);
    }

3.統一的處理結果的封裝。

基本上任何一個處理,要不成功,要不失敗(警告其實也算失敗)。

這裏封裝了一個返回結果的基類ResultEntity<T>,可以進行有效的應用端或者java端的交互。
這裏寫圖片描述

@ApiModelProperty(value = "狀態碼,0表示成功 其他表示失敗", example = "0",position = 1)
private String code;

特別需要指出的是,前端獲取或者處理數據,也是統一要用這個處理結果基類的返回。

簡單來說,就是數據處理成功/失敗,會有一個統一的返回結果標識。注意,這個標識和請求的響應結果標識(200)是有所不同的!

請求響應標識只是說明web服務器的響應是正常,但,具體的處理結果可能是處理失敗。
這裏寫圖片描述

下面是一個具體的例子(到時候實際開發處理的接口處理結果也是這樣子):

{
    "code": "0",
    "message": "",
    "description": "",
    "obj": [{
        "createdBy": -1,
        "creationDate": "2017-10-10 09:37:03",
        "lastUpdatedBy": 10,
        "lastUpdateDate": "2017-11-16 14:47:48",
        "lastUpdateLogin": 96,
        "valueUUID": null,
        "id": 2,
        "applId": 1,
        "respCode": "BASIC_SET",
        "menuId": 2,
        "startDate": "2017-10-10 09:37:03",
        "endDate": null,
        "respName": "系統設置職責",
        "description": "系統設置職責",
        "menuCode": "SYSTEM_SET",
        "menuName": "系統設置菜單",
        "enabled": true
    }],
    "param1": null,
    "param2": null,
    "param3": null,
    "param4": null,
    "param5": null,
    "ok": true
}

文檔參考鏈接:
https://juejin.im/entry/5a7812906fb9a0635014f19a
http://blog.51cto.com/ityouknow/1974080

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章