南京網(wǎng)頁網(wǎng)站制作網(wǎng)站推廣的意義和方法
接上次博客:JavaEE進階(5)Spring IoC&DI:入門、IoC介紹、IoC詳解(兩種主要IoC容器實現(xiàn)、IoC和DI對對象的管理、Bean存儲、方法注解 @Bean)、DI詳解:注入方式、總結-CSDN博客
目錄
配置文件作用
SpringBoot配置文件?
配置文件的格式
properties 配置文件說明
properties 基本語法
讀取配置文件
properties 缺點分析
yml 配置文件說明
yml 基本語法
yml 使用進階
yml 配置不同數(shù)據(jù)類型及 null
yml 配置讀取
配置對象
配置集合
配置Map
yml優(yōu)缺點?
綜合性練習
驗證碼案例
Kaptcha 插件介紹
原理
引入依賴
生成驗證碼
添加配置項?
?Kaptcha詳細配置
需求
準備工作
index.html:
success.html:
參考邏輯
約定前后端交互接口?
實現(xiàn)服務器端代碼?
引入依賴
通過配置創(chuàng)建驗證碼生成器
驗證碼校驗
調(diào)整前端頁面代碼
運行測試
配置文件作用
計算機系統(tǒng)中存在著大量的配置文件,雖然絕大多數(shù)用戶不直接與這些文件交互,但它們在各種軟件和應用中發(fā)揮著關鍵作用。無論是瀏覽器、微信、IDE(集成開發(fā)環(huán)境)還是智能手機,都依賴于配置文件的存在。這些文件以不同的形式分布在計算機上,例如在C:\Users、C:\Windows等文件夾下,以及各種 .config、.xml 文件。
配置文件主要是為了解決硬編碼帶來的問題, 把可能會發(fā)生改變的信息, 放在一個集中的地方,?當我們啟動某個程序時,應用程序從配置?件中讀取數(shù)據(jù),并加載運行。
配置文件的作用和重要性:
解決硬編碼問題: 主要作用之一是解決硬編碼的問題。硬編碼將數(shù)據(jù)直接嵌入到程序源代碼中,導致數(shù)據(jù)固定且不易修改。通過配置文件,可以將可能發(fā)生變化的信息集中存儲,使得修改配置更加方便,而不需要修改源代碼。
硬編碼(Hard Coding)是指直接將數(shù)據(jù)、配置或參數(shù)等值直接嵌入到程序或可執(zhí)行對象的源代碼中,而不是通過外部配置文件或其他可變的機制進行設置。這種做法使得這些值變得固定,不易修改,而且需要修改源代碼才能改變這些值。
以手機字體大小為例,如果采用硬編碼的方式,開發(fā)者會在程序源代碼中明確指定字體大小的數(shù)值。這樣,所有用戶使用該應用時都將看到相同的字體大小,而無法根據(jù)個人偏好進行調(diào)整。如果不同用戶有不同的偏好,他們將無法根據(jù)個人偏好調(diào)整字體大小。
集中管理配置信息: 許多軟件和系統(tǒng)需要配置信息,例如路徑、數(shù)據(jù)庫連接、用戶設置等。配置文件提供了一種集中管理這些信息的方式,使得修改配置信息更加方便,而不需要在整個代碼庫中查找和修改。
提高靈活性: 配置文件使得應用程序的行為能夠在不修改程序代碼的情況下進行調(diào)整。這使得軟件更具靈活性,能夠適應不同的使用場景和需求,而無需重新編譯和部署。
適應不同環(huán)境: 不同的環(huán)境(開發(fā)、測試、生產(chǎn)等)可能需要不同的配置。通過使用不同的配置文件,可以輕松地適應不同的環(huán)境需求,而無需更改源代碼。
用戶和應用程序的交互: 配置文件為用戶提供了調(diào)整應用程序行為的手段,例如更改界面樣式、設置偏好等。用戶可以通過修改配置文件而不是程序代碼來個性化應用。
應用程序間的交互: 在分布式系統(tǒng)中,不同的應用程序可能需要相互通信和協(xié)作。通過配置文件,可以配置不同應用程序之間的交互規(guī)則和參數(shù),實現(xiàn)更好的集成和協(xié)作。
簡化部署和維護: 將配置信息存儲在文件中使得部署和維護過程更加簡化。不同的配置文件可以輕松地用于不同的部署環(huán)境,同時也方便備份和恢復。
綜合而言,配置文件在軟件開發(fā)和計算機系統(tǒng)中扮演了重要的角色,為軟件提供了靈活性、可維護性和用戶友好性。通過合理使用配置文件,可以使得應用程序更易于開發(fā)、維護和升級。
SpringBoot配置文件?
SpringBoot提供了強大的配置文件支持,允許開發(fā)人員靈活地配置和管理應用程序的各種參數(shù)。配置文件的格式得到了SpringBoot的支持和定義,這不僅有助于規(guī)范化項目的配置,同時也為其他框架集成到SpringBoot提供了一種便捷的方式。
在配置文件中,可以設置許多項目或框架的關鍵信息,包括但不限于:
-
項目的啟動端口: 開發(fā)人員可以輕松地指定應用程序監(jiān)聽的端口,確保應用在啟動時使用正確的端口進行通信。
-
數(shù)據(jù)庫的連接信息: 數(shù)據(jù)庫是許多應用程序的核心組成部分,而數(shù)據(jù)庫連接信息通常包括用戶名和密碼等敏感信息。通過配置文件,這些信息可以被安全地存儲和管理。
-
第三方系統(tǒng)的調(diào)用密鑰: 集成到其他系統(tǒng)或服務時,經(jīng)常需要提供訪問權限驗證的密鑰。將這些密鑰放在配置文件中,有助于在應用程序中輕松管理這些敏感信息。
-
用于發(fā)現(xiàn)和定位問題的日志信息: 配置文件中可以定義各種日志參數(shù),包括常規(guī)日志和異常日志。這些日志對于開發(fā)人員在調(diào)試和解決問題時提供了寶貴的信息。
通過使用SpringBoot的配置文件,開發(fā)人員可以集中管理應用程序的配置,而不必硬編碼這些參數(shù),從而增加了靈活性。這種方式還使得配置信息可以根據(jù)不同環(huán)境(如開發(fā)、測試、生產(chǎn))進行調(diào)整,而不必修改源代碼。總體而言,SpringBoot的配置文件功能為開發(fā)人員提供了更方便、可維護性更強的配置管理方式。
在Spring框架中,Spring Boot的配置文件主要是通過application.properties(或者application.yml)來定義項目的配置信息。我們其實已經(jīng)見過一些常見配置項:
項目的啟動端口
Spring Boot內(nèi)置了Tomcat服務器,默認端口號是8080。但由于電腦上8080端口可能被其他應用程序占用,因此Spring Boot允許用戶自定義端口號。通過在application.properties文件中設置如下配置,可以指定項目的啟動端口:
server.port=自定義端口號
這樣,應用程序將使用指定的端口號進行監(jiān)聽,確保不與其他應用程序發(fā)生沖突。
數(shù)據(jù)庫連接信息
為了更方便簡單地訪問數(shù)據(jù)庫,出現(xiàn)了一些持久層框架,它們對JDBC進行了更深層次的封裝。這些框架允許用戶通過簡短的代碼實現(xiàn)數(shù)據(jù)庫訪問。然而,不同的應用程序通常需要訪問不同的數(shù)據(jù)庫,因此這些持久層框架需要支持用戶自定義配置數(shù)據(jù)庫的連接信息。
在application.properties文件中,可以設置數(shù)據(jù)庫連接的相關信息,例如:
spring.datasource.url=jdbc:mysql://localhost:3306/數(shù)據(jù)庫名
spring.datasource.username=數(shù)據(jù)庫用戶名
spring.datasource.password=數(shù)據(jù)庫密碼
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
這樣,開發(fā)人員可以輕松地配置應用程序連接到特定數(shù)據(jù)庫的詳細信息,而不必在代碼中硬編碼這些參數(shù)。
總體而言,通過application.properties文件,Spring Boot提供了一個集中管理和靈活配置項目的途徑,使得我們能夠輕松地自定義端口號和數(shù)據(jù)庫連接信息,以滿足不同項目的需求。
配置文件的格式
在Spring Boot中,配置文件是一種關鍵的組件,用于定義應用程序的各種屬性和設置。
這些配置文件主要有三種格式:
- application.properties
- application.yml
- application.yaml
其中,yml是yaml的縮寫,而在實際開發(fā)中,yml格式的配置文件的使用頻率最高。需要注意的是,yaml和yml在使用方式上是相同的,但在文件擴展名上有所不同,而我們主要介紹yml文件的使用。
當Spring Boot應用程序啟動時,它會自動尋找并加載位于classpath路徑下的application.properties、application.yaml或者application.yml文件。這樣的自動加載機制使得配置文件的管理變得非常方便,同時也為我們提供了更靈活的配置選項。
除了默認的加載路徑外,還可以通過使用spring.config.name屬性來指定配置文件的路徑和名稱。這為項目中的多個配置文件提供了支持,使得我們可以根據(jù)環(huán)境或其他條件選擇合適的配置文件。例如,我們可以通過spring.config.name=custom-config來指定加載名為custom-config.yml或custom-config.properties的配置文件。具體參考官方文檔:Core Features (spring.io)
以下是一個示例application.yml文件,展示了如何配置一些常見的應用屬性:
# application.ymlserver:port: 8080spring:datasource:url: jdbc:mysql://localhost:3306/mydatabaseusername: rootpassword: passwordjpa:hibernate:ddl-auto: updateshow-sql: truemyapp:custom-property: custom-value
在這個例子中,我們通過server配置了服務器的端口,使用了spring配置來定義數(shù)據(jù)庫連接信息,而myapp下則是自定義的應用屬性。通過這種方式,就可以靈活地管理和配置Spring Boot應用程序的各種參數(shù)和設置。
我們把?application.properties里面的代碼注釋掉,然后:
類比商品包裝,Spring Boot的配置文件可以看作是兩種不同的包裝:properties 類型的配置文件就像是“老款包裝”,而yml 則是“新版包裝”。默認情況下,在創(chuàng)建Spring Boot項目時,會采用 properties 格式的配置文件,這主要是為了兼容性和傳統(tǒng)。這種選擇的原因可能是因為在倉庫中還有大量使用老款包裝的庫存,因此作為默認。
然而,yml格式的配置文件被認為是“新版包裝”,更加現(xiàn)代且易讀。如果用戶對情況比較了解,并希望使用更新的配置文件格式,可以直接選擇使用yml。這就好像用戶可以選擇購買商品時,如果了解情況并喜歡新版包裝,那么商家就直接提供新版包裝的產(chǎn)品。
因此,當用戶在創(chuàng)建Spring Boot項目時,如果對配置文件格式有特定需求,可以直接指定要使用的包裝,即選擇 properties 或 yml,就像在購物時選擇商品包裝一樣。這樣,用戶可以更加靈活地根據(jù)個人偏好或項目需求選擇適當?shù)呐渲梦募袷健?/p>
那么如果我們讓兩個文件并存,哪個更優(yōu)先呢?
特殊說明:
- 理論上,.properties 和 .yml 可以同時存在于一個項目中。當這兩種配置文件并存時,兩個配置文件都會被加載。然而,如果配置文件內(nèi)容存在沖突,那么以 .properties 為主,也就是說,.properties 的優(yōu)先級更高。這意味著在存在沖突的情況下,.properties 文件中的配置項會覆蓋相同配置項在 .yml 文件中的設置。
- 同一個配置項在兩個配置文件都存在的情況下,.properties 的優(yōu)先級高于 .yml 文件;不同的配置項,在.properties 和 .yml 文件中配置都會生效。
- 雖然理論上可以讓 .properties 和 .yml 共存,但在實際的業(yè)務中,通常會選擇一種統(tǒng)一的配置文件格式。這樣的做法有助于更好地維護項目,降低故障率。通過采用一致的配置文件格式,開發(fā)團隊能夠更容易理解和管理配置,減少因格式差異而引起的問題。因此,為了項目的一致性和維護性,通常會選擇在一個項目中使用一種主要的配置文件格式。
properties 配置文件說明
properties 配置文件是最早期的配置文件格式,也是創(chuàng)建 Spring Boot 項目時的默認配置文件。這種格式以鍵值對的形式存儲配置信息,每一行表示一個屬性的設置。由于其簡單直觀的語法,properties 配置文件在項目早期得到了廣泛應用。
在 Spring Boot 的早期版本中,使用 properties 格式的配置文件是主流,這也符合許多傳統(tǒng) Java 項目的配置需求。然而,隨著時間的推移和開發(fā)者對更靈活、易讀的配置的需求增加,新一代的配置文件格式逐漸嶄露頭角,其中以 yml 格式為代表。
盡管 properties 配置文件在默認設置中仍然保留,但隨著 Spring Boot 的演進,更多項目和開發(fā)者轉向使用 yml 格式的配置文件。這種趨勢主要因為 yml 具有更為結構化和可讀性強的語法,使得配置信息更清晰、易維護,從而提高了開發(fā)效率。不過,properties 仍然在一些場景中得到應用,特別是在傳統(tǒng)項目或與其他系統(tǒng)集成時。
properties 基本語法
properties 配置文件的基本語法是采用鍵值對的形式,其中鍵和值之間用等號 = 連接。以下是一個簡單的示例,展示了如何使用 properties 文件配置一些常見的項:
# 配置項目端口號
server.port=8080# 配置數(shù)據(jù)庫連接信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding=utf8&
spring.datasource.username=root
spring.datasource.password=root
在這個示例中:
- server.port=8080 表示配置了應用程序的端口號為 8080。
- spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding=utf8& 配置了數(shù)據(jù)庫連接的URL。
- spring.datasource.username=root 和 spring.datasource.password=root 配置了數(shù)據(jù)庫的用戶名和密碼。
此外,可以使用 # 符號在配置文件中添加注釋信息,這對于提供配置項的說明或者添加一些備注非常有用。注釋部分對應于配置文件中的說明而不會影響實際的配置。比如上述示例中的注釋用于解釋每個配置項的用途。
你也可以自定義配置:
我們現(xiàn)在先學習語法和具體的使用,更多配置信息會隨著深入學習進行補充。如果你很感興趣,可以參考:Common Application Properties (spring.io)
這些全是一些默認配置:
你可能會很頭大,這要怎么學……?
不用學,用的時候現(xiàn)去查就行,而且與其在官方文檔查,還不如直接百度。
我整理了幾個application.yml文件的常用的配置項,可以直接復制粘貼:
# 數(shù)據(jù)源配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver# Spring MVC 配置
spring:mvc:favicon:enable: false# 多平臺配置
spring:profiles:active: dev# Mybatis 配置
mybatis:# 設置 Mybatis 的 XML 文件保存路徑mapper-locations: classpath:mapper/*Mapper.xml# Mybatis 配置configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true # 自動將數(shù)據(jù)庫字段轉為駝峰命名# 日志配置
logging:file:name: logs/springboot.log # 指定日志文件位置logback:rollingpolicy:max-file-size: 1KB # 設置日志文件大小file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i # 指定日志文件名格式level:com:example:demo: debug # 設置特定包的日志級別為debug
上述配置包含了常見的數(shù)據(jù)源配置、Spring MVC 配置、多平臺配置、MyBatis 配置以及日志配置。注釋部分提供了對各個配置項的簡要解釋。這樣的配置文件已經(jīng)可以滿足常見的Spring Boot項目的需求,同時你也可以根據(jù)實際情況進行調(diào)整和擴展。
讀取配置文件
在Spring Boot項目中,可以使用@Value注解來主動讀取配置文件中的內(nèi)容。這個注解的使用方式是在類的字段上添加注解,并使用${}的格式來引用配置文件中的屬性值。
我們先創(chuàng)建一個新的Controller:
啟動之后去網(wǎng)頁看看:
現(xiàn)在我們通過@Value注解來拿key1的值:
再多增加一個:
如果把 ${ } 去掉就代表賦值:
它同時也是有一些校驗的,比如現(xiàn)在把key3改成字符串:
在運行就不對了:?
properties 缺點分析
properties 配置文件以 key-value 的形式存儲配置信息,具有簡單直觀的語法。然而,隨著項目的復雜性增加,properties 配置文件也會暴露一些缺點,其中一些包括:
-
缺乏層次結構:properties 配置文件本身不支持層次結構,所有配置項都是扁平的。這意味著在配置復雜的對象結構時,需要使用復雜的命名規(guī)則來模擬層次結構,導致配置文件變得冗長和難以維護。
person.name=John Doe person.age=30 person.address.city=New York person.address.zip=10001
-
冗余的信息:?properties 配置文件中可能包含大量重復的信息,尤其是在鍵的前綴相同的情況下。這可能導致配置文件變得臃腫,并且當需要修改一組相關的配置項時,可能需要在多個地方進行調(diào)整。
-
可讀性差: 隨著配置項的增多,properties 文件可能變得難以閱讀和理解。復雜的結構和大量的鍵值對可能使得配置文件的維護變得繁瑣。
相對于 properties ,yml 配置文件提供了更具層次結構和可讀性的格式。通過使用縮進和冒號的方式,yml 允許在配置文件中創(chuàng)建更復雜的數(shù)據(jù)結構,減少了冗余的信息,并提高了可讀性。因此,在需要處理復雜配置結構和提高配置文件可讀性的情況下,yml 格式通常更受開發(fā)者歡迎。
yml 配置文件說明
yml 是 YAML 是縮寫,YAML(YAML 的原意是 "YAML Ain't Markup Language",這是一種自指的遞歸縮寫。盡管 "Yet Another Markup Language" 也是 YAML 的一種解釋,翻譯成中文就是“另一種標記語言”,但官方的定義是 "YAML Ain't Markup Language"。這個縮寫旨在表達 YAML 不是一種傳統(tǒng)的標記語言(markup language),而是一種數(shù)據(jù)序列化格式,更注重數(shù)據(jù)的表達和易讀性。強調(diào) YAML 的設計目標和特性。)是一種人類可讀的數(shù)據(jù)序列化格式,常被用于配置文件和數(shù)據(jù)交換的場景。
YAML 的跨平臺性體現(xiàn)在它的語法規(guī)則和數(shù)據(jù)結構的表達方式上。YAML 的語法是清晰、簡潔、易讀的,且不依賴于特定編程語言,因此可以在不同的編程語言和平臺之間進行交換和共享。
在實踐中,許多編程語言都有對 YAML 格式的解析和生成支持,使得開發(fā)者可以在不同的平臺上使用 YAML 文件進行配置。這種靈活性和跨平臺性使 YAML 成為許多項目中配置文件的首選格式。
我們先來學習yml文件的基本語法和說明:
yml 基本語法
yml 是樹形結構的配置文件,它的基礎語法是"key: value",key 和 value 之間使用英文冒號加空格的方式組成,空格不可省略!!!
如果一個鍵值對中的值包含多個單詞,通過換行的縮進表示,這種方式通常用于表示復雜的數(shù)據(jù)結構或長的文本塊:
description: |This is a multilinedescription that spansmultiple lines in YAML.
-
基本結構:?yml使用縮進來表示結構,而不是像其他語言一樣使用大括號??s進的空格數(shù)目是有意義的,通常是兩個空格。
key1: value1 key2:subkey1: subvalue1subkey2: subvalue2
-
鍵值對: 鍵值對使用冒號 : 分隔,表示鍵和值的關系。
name: John Doe age: 30
-
列表: 使用連字符?- 表示列表中的每個元素。
fruits:- apple- orange- banana
-
多行文本: 使用 |?符號表示多行文本塊,保留換行符。
description: |This is a multilinetext block.
-
注釋: 使用?# 符號表示注釋。
# This is a comment key: value
-
引用: 使用?& 符號創(chuàng)建錨點(anchor),使用 *?符號引用錨點。
defaults: &defaultsusername: guestpassword: guestuser1: *defaults user2:<<: *defaultspassword: secure_password
這些是.yml文件的基本語法。YAML以人類可讀的方式表示數(shù)據(jù)結構,其簡潔性和可讀性使其成為配置文件和數(shù)據(jù)交換的常用格式。在Spring Boot項目中,.yml文件通常用于配置應用程序的屬性,提供了一種更易讀、更清晰的配置方式。
使用.yml連接數(shù)據(jù)庫:
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/dbname?characterEncoding=utf8&useSSL=falseusername: rootpassword: root
使用.properties連接數(shù)據(jù)庫:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/dbname?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
對比來看,在這兩個例子中,配置的含義是相同的,都是配置了Spring Boot應用程序連接到MySQL數(shù)據(jù)庫的相關信息。主要的區(qū)別在于語法格式:
- .yml文件: 使用縮進和冒號來表示層次結構和鍵值對。
- .properties文件: 使用等號來表示鍵值對,每個配置項在文件中占據(jù)一行。
在實際使用中,選擇.yml還是.properties通常取決于個人或團隊的偏好,以及項目的具體需求。
.yml文件相對更加易讀和清晰,特別適合配置文件較為復雜的情況。.properties文件則更加傳統(tǒng),適用于簡單的配置需求。
yml 使用進階
yml 配置不同數(shù)據(jù)類型及 null
# 字符串
string:value: Hello# 布爾值,true或false
boolean:value: truevalue1: false# 整數(shù)
int:value: 10# 浮點數(shù)
float:value: 3.14159# Null,~代表null
null:value: ~# 空字符串
# 直接后面什么都不加就可以了, 但這種方式不直觀, 更多的表示是使用引號括起來
empty:value: ''
yml 配置讀取
yml 讀取配置的方式和 properties 相同,使用?@Value 注解即可:
?
?
?
所以你會發(fā)現(xiàn),把.properties中的“?. ”改成換行縮進就是.yml文件格式。
注意事項:value 值加單雙引號
在 YAML 文件中,對字符串的表示有一些注意事項,尤其涉及到單引號和雙引號。
字符串默認不用加上單引號或者雙引號,如果加英文的單雙引號可以表示特殊的含義。
嘗試在 application.yml 中配置如下信息:
string:str1: Hello \n Spring Boot.str2: 'Hello \n Spring Boot.'str3: "Hello \n Spring Boot."
?
從上述實例可以得出以下結論:
-
字符串默認不需要加上單引號或者雙引號: 在?str1 中,字符串 "Hello \n Spring Boot." 沒有加上任何引號,它會被解釋為一個普通字符串,特殊字符 \n 會被當作兩個字符。
-
單引號會轉義特殊字符: 在?str2 中,字符串被單引號包裹,導致 \n 失去特殊功能,它會被解釋為普通字符串中的兩個字符,而不是換行符。
-
雙引號不會轉義字符串里面的特殊字符: 在?str3 中,字符串被雙引號包裹,特殊字符 \n 會被保留其本身的含義,即表示換行符。
注意這里的描述,可能我們的第一反應相反。
- 此處的轉義理解起來會有些拗口,\n 本意表示的是換行。
- 使用單引號會轉義,就是說,\n 不再表示換行了,而是表示一個普通的字符串。
- 使用雙引號不會轉義,\n 表示的是它本身的含義,就是換行。
配置對象
我們還可以在 yml 中配置對象,如下配置:?
或者是使用行內(nèi)寫法(與上面的寫法作用?致):
這個時候就不能用?@Value 來讀取配置中的對象了,此時要使用另?個注解 @ConfigurationProperties 來讀取,具體實現(xiàn)如下:?
注意要加上@Component: 如果希望Student類成為Spring容器的一個Bean,通常需要在這個類上添加@Component注解,以便Spring能夠掃描并管理這個Bean。?
調(diào)用類的實現(xiàn)如下:?
配置集合
配置?件也可以配置 list 集合,如下所示:?
注意縮進!!!
Dbtypes:name:- mysql- sqlserver- db2
集合的讀取和對象?樣,也是使用?@ConfigurationProperties 來讀取的,具體實現(xiàn)如下:
訪問集合的實現(xiàn)如下:
空格絕對不可以省略!否則此處將被當作一個對象!
省略空格后沒有報錯,但是含義完全改變!?
?
配置Map
配置文件也可以配置 map,如下所示:
或者是使用行內(nèi)寫法(與上面的寫法作用?致,也要空格):
Map的讀取和對象一樣,也是使? @ConfigurationProperties 來讀取的,具體實現(xiàn)如下:
打印類的實現(xiàn)如下:
?
yml優(yōu)缺點?
優(yōu)點:
-
可讀性高,寫法簡單,易于理解: YAML 使用縮進和簡潔的語法結構,使文件更加易讀,不需要像 XML 或 JSON 那樣使用大量的符號和標記。
-
支持更多的數(shù)據(jù)類型: YAML 支持豐富的數(shù)據(jù)類型,包括對象、數(shù)組、List、Map 等,使其能夠簡單表達各種數(shù)據(jù)形態(tài),適用于多樣化的應用場景。
-
跨編程語言支持: YAML 不僅在 Java 中得到廣泛應用,還能夠在其他編程語言中使用,如 Golang、Python、Ruby、JavaScript 等,提供了更大的靈活性和通用性。
缺點:
-
不適合寫復雜的配置文件: 盡管 YAML 對于簡單的配置文件表達十分方便,但對于復雜配置文件的書寫可能變得困難。與 properties 格式相比,復雜配置的轉換過程可能會花費更多精力,可讀性也會下降。
例如,對于一份復雜的配置,YAML 的可讀性較差:
keycloak:realm: demoresource: fm-cache-cloudcredentials:secret: d4589683-Oce7-4982-bcd3security:- authRoles:- usercollections:- name: ssologinurlpatterns:- /login/*
而properties格式如下:
keycloak.realm = demo keycloak.resource = fm-cache-cloud keycloak.credentials.secret = d4589683-Oce7-4982-bcd3 keycloak.security[0].authRoles[0]= user keycloak.security[0].collections[0].name = ssologinurl keycloak.security[0].collections[0].patterns[0] = /login/*
-
對格式有較強的要求: YAML 對縮進和格式有較強的要求,一個空格的差異可能導致解析錯誤。這可能使得在編輯或處理 YAML 文件時更加容易出錯,需要維護者保持良好的格式規(guī)范。
總的來說,YAML 適用于簡單和中等復雜度的配置文件,但在處理高度復雜的配置時,可能會顯得繁瑣并降低可讀性。在選擇配置文件格式時,需要根據(jù)具體的應用場景和需求權衡其優(yōu)缺點。
綜合性練習
驗證碼案例
隨著對安全性要求的日益提升,目前許多項目都廣泛采用驗證碼作為一種重要的安全驗證手段。驗證碼的形式多種多樣,其中更為復雜的圖形驗證碼和行為驗證碼已經(jīng)成為當前的主流趨勢。隨著技術的不斷發(fā)展,這些高級驗證碼不僅提升了安全性,還為用戶提供了更加可靠和有效的身份驗證方式。
驗證碼的實現(xiàn)方式很多,?網(wǎng)上也有比較多的插件或者工具包可以使用,咱們選擇使用Google的開源項目?Kaptcha來實現(xiàn)。
Kaptcha 插件介紹
Kaptcha是由Google推出的一款高度可配置的實用驗證碼生成工具。該工具以其靈活性和高度定制化而著稱,為開發(fā)者提供了生成驗證碼的便捷解決方案。通過Kaptcha,用戶可以根據(jù)具體需求調(diào)整驗證碼的各種參數(shù),包括但不限于驗證碼的外觀、復雜性和大小等。這使得Kaptcha成為許多項目中首選的驗證碼生成工具之一,為用戶提供了一種可靠而安全的身份驗證機制。
代碼:Google Code Archive - Long-term storage for Google Code Project Hosting.
網(wǎng)上有很多人甚至公司都基于Google的kaptcha進行了二次開發(fā)。
我們選擇?個直接適配SpringBoot的 開源項目。
GitHub - oopsguy/kaptcha-spring-boot: Kaptcha Spring Boot Starter help you use Google Kaptcha with Spring Boot easier. 一個簡單封裝了 Kaptcha 驗證碼庫的 Spring Boot Starter
但是這篇文章比較粗糙……所以,我們下面先簡單解釋一下插件的使用,然后再進行一個驗證碼程序的詳細編寫過程。?
原理
驗證碼的生成可以在客戶端進行,也可以在服務器端進行。
對于普通的字符驗證碼,后端通常分兩個主要步驟。首先,生成驗證碼的內(nèi)容,根據(jù)驗證碼內(nèi)容以及干擾項等因素,生成相應的圖像,并將圖像返回給客戶端。其次,將驗證碼的內(nèi)容存儲起來,以便在校驗時取出進行比對。
在這個流程中,kaptcha插件采取了將驗證碼內(nèi)容存儲在Session中的策略。這意味著生成的驗證碼內(nèi)容會被存儲在服務器端的Session對象中,以確保安全性和一致性。在校驗時,系統(tǒng)會從Session中取出相應的驗證碼內(nèi)容,然后與用戶輸入的驗證碼進行比對,以完成驗證過程。這種方法有效地維護了驗證碼的狀態(tài)和安全性,為用戶提供了可靠的身份驗證機制。
引入依賴
<dependency><groupId>com.oopsguy.kaptcha</groupId><artifactId>kaptcha-spring-boot-starter</artifactId><version>1.0.0-beta-2</version>
</dependency>
生成驗證碼
該插件提供了兩種方式生成驗證碼 :
1、通過代碼來生成:參考文檔:kaptcha-spring-boot/README_zh-CN.md at master · oopsguy/kaptcha-spring-boot · GitHub
2、僅通過配置文件來生成驗證碼(推薦)
我們接下來介紹的方法就是通過配置文件來生成驗證碼:
添加配置項?
# 應用服務 WEB 訪問端口
server:port: 8080kaptcha:items:# home captchahome:path: /home/captchasession:key: HOME_KAPTCHA_SESSION_KEYdate: HOME_KAPTCHA_SESSION_DATE# admin captchaadmin:path: /admin/captchasession:key: ADMIN_KAPTCHA_SESSION_KEYdate: ADMIN_KAPTCHA_SESSION_DATE
?
配置完之后就可以運行一下看看:
?
訪問一下這個URL:
可以發(fā)現(xiàn),驗證碼已經(jīng)出來了,而且隨著我們的刷新會進行更改。
看起來好像很神奇,我們好像沒有做什么工作,但是驗證碼就已經(jīng)出現(xiàn)了。
好吧,總有人替你負重前行。
我們導入的依賴里面的jar包中有人幫你完成了代碼編寫的過程:
感興趣的可以看看源碼,代碼量其實不多:
1、最開始的是一個名為KaptchaConst的Java接口,定義了一個常量AUTO_CONFIG_PREFIX,其值為字符串"kaptcha"。這樣的接口通常用于存放項目中使用的常量,以提高代碼的可維護性和可讀性。在這里,KaptchaConst接口定義了一個用于自動配置的前綴常量,該前綴用于處理Kaptcha驗證碼生成庫的自動配置屬性。在其他部分的代碼中,可以通過引用KaptchaConst.AUTO_CONFIG_PREFIX來獲取這個前綴,以確保一致性和避免硬編碼。
2、ConfigUtils類的目的是提供一組方法,將Kaptcha的配置信息轉換為Properties對象,使得配置信息更易于處理和傳遞。這對于在應用程序中動態(tài)配置Kaptcha生成庫的行為非常有用。
3、接下來是一個名為BaseProperties的抽象類,用于定義Kaptcha驗證碼生成庫的基本屬性。該類包含了多個內(nèi)部靜態(tài)類,每個靜態(tài)類表示不同的配置項,形成了一個層次結構。以下是主要的配置項和它們的含義:
- Border: 邊框配置項,包括是否啟用邊框(enabled)、邊框顏色(color)、邊框厚度(thickness)。
- Noise: 噪點配置項,包括噪點顏色(color)和噪點實現(xiàn)類(impl)。
- Obscurificator: 扭曲器配置項,包括扭曲器實現(xiàn)類(impl)。
- Producer: 生產(chǎn)器配置項,包括生產(chǎn)器實現(xiàn)類(impl)。
- TextProducer: 文本生成器配置項,包括文本生成器實現(xiàn)類(impl)、字符配置項(character)和字體配置項(font)。
- Background: 背景配置項,包括背景實現(xiàn)類(impl)、背景顏色起始值(colorFrom)和背景顏色結束值(colorTo)。
- Word: 單詞配置項,包括單詞實現(xiàn)類(impl)。
- Image: 圖像配置項,包括圖像寬度(width)和圖像高度(height)。
每個配置項都有相應的Getter和Setter方法,用于獲取和設置其屬性值。這種結構使得可以靈活配置Kaptcha驗證碼生成庫的各個方面,使其適應不同的需求和場景。BaseProperties類的實例通常作為其他類的屬性,例如KaptchaProperties和ConfigUtils中的屬性。
4、KaptchaAutoConfigure類沒有直接繼承BaseProperties類。相反,它使用了KaptchaProperties類,并通過@EnableConfigurationProperties({KaptchaProperties.class})注解啟用了對KaptchaProperties類的配置屬性支持。這意味著KaptchaProperties類中的屬性將會被映射到KaptchaAutoConfigure中,以便在自動配置過程中使用。
雖然KaptchaAutoConfigure類沒有直接繼承BaseProperties類,但它通過引用KaptchaProperties類間接地使用了BaseProperties類中定義的屬性,因為KaptchaProperties類繼承了BaseProperties類。
KaptchaAutoConfigure類是一個Spring Boot自動配置類,用于配置和初始化Kaptcha驗證碼生成庫的相關組件。
- @Configuration: 表示這是一個配置類。
- @EnableConfigurationProperties({KaptchaProperties.class}): 啟用對KaptchaProperties類的配置屬性支持,使得可以在配置文件中配置Kaptcha的屬性。
- @Bean(name = {"kaptchaProps"}): 定義了名為kaptchaProps的Bean,該Bean用于將KaptchaProperties對象轉換為Properties對象,以便后續(xù)使用。
- @Bean: 定義了名為defaultKaptcha的Bean,該Bean用于創(chuàng)建并配置DefaultKaptcha對象,作為Kaptcha驗證碼生成的默認實現(xiàn)。這里使用了ConfigUtils類中的方法將Properties對象應用到DefaultKaptcha中。
- @ConditionalOnMissingBean({Producer.class}): 僅當不存在名為Producer的Bean時,才創(chuàng)建defaultKaptcha Bean。這意味著如果應用程序已經(jīng)定義了自己的Producer Bean,則不會覆蓋它。
- @DependsOn({"kaptchaProps"}): 表示defaultKaptcha Bean依賴于kaptchaProps Bean。
- @Bean: 定義了一個名為webConfig的Bean,該Bean是ServletContextInitializer的實例,用于在Web應用程序啟動時注冊Kaptcha的Servlet。這里使用了ServletRegisterInitializer類。
總體而言,這個自動配置類負責將Kaptcha集成到Spring Boot應用程序中。它提供了默認的Kaptcha實現(xiàn)(DefaultKaptcha),并允許用戶在配置文件中靈活地配置Kaptcha的各種屬性。如果用戶已經(jīng)定義了自己的Producer Bean,則默認的Kaptcha實現(xiàn)不會覆蓋用戶的定義。
5、然后是剛剛提到的繼承自BaseProperties的KaptchaProperties類,它用于配置Kaptcha驗證碼生成庫的屬性,逐步解釋一下代碼的結構和含義:
- @ConfigurationProperties(prefix = "kaptcha"): 這是一個Spring Boot注解,表明該類用于處理以"kaptcha"為前綴的配置屬性。
- public class KaptchaProperties extends BaseProperties: KaptchaProperties類擴展了BaseProperties類,說明它繼承了一些基本的屬性。
- private Map<String, SingleKaptchaProperties> items = new HashMap();: 這里定義了一個Map類型的屬性items,用于存儲不同類型的Kaptcha配置。每個配置由一個字符串鍵(key)和一個SingleKaptchaProperties對象值組成。
- public Map<String, SingleKaptchaProperties> getItems(): 提供了獲取items屬性的方法。
- public void setItems(Map<String, SingleKaptchaProperties> items): 提供了設置items屬性的方法。
- public static class SingleKaptchaProperties extends BaseProperties: 定義了一個靜態(tài)內(nèi)部類SingleKaptchaProperties,該類也擴展了BaseProperties類。
- private Session session = new Session();: 在SingleKaptchaProperties類中定義了一個Session類型的屬性session,并初始化為new Session()。
- private String path;: 定義了一個字符串類型的屬性path。
- 對于Session類:
private String key;: 定義了一個字符串類型的屬性key。
private String date;: 定義了一個字符串類型的屬性date。 - 提供了相應的Getter和Setter方法,用于獲取和設置各個屬性的值。
這個配置類的目的是為Kaptcha驗證碼生成庫提供靈活的配置選項,支持多種配置情況。其中,KaptchaProperties類包含了一個Map,每個條目對應一個特定類型的Kaptcha配置,而SingleKaptchaProperties類則包含了該類型的詳細配置信息,包括Session類中的屬性。
6、最后的一個類是一個ServletContextInitializer的實現(xiàn)類,名為ServletRegisterInitializer。它的主要目的是在Servlet容器啟動時,向ServletContext注冊Kaptcha驗證碼生成的Servlet。
讓我解釋一下該類的主要結構和功能:
-
private static final String KAPTCHA_SERVLET_BEAN_NAME_SUBFFIX = "KapthcaServlet": 定義了一個常量,用于生成Kaptcha驗證碼Servlet的Bean名稱后綴。
-
@Resource: 注解用于注入依賴。
-
private KaptchaProperties kaptchaProperties: 注入KaptchaProperties對象,用于獲取Kaptcha的配置信息。
-
@Resource(name = "kaptchaProps"): 注入名為kaptchaProps的Properties對象,該對象通過ConfigUtils類將KaptchaProperties轉換而來。
-
-
public void onStartup(ServletContext servletContext) throws ServletException: 實現(xiàn)了ServletContextInitializer接口的方法,在Servlet容器啟動時執(zhí)行。該方法的主要邏輯如下:
-
獲取Kaptcha的不同配置項(SingleKaptchaProperties)。
-
遍歷配置項,為每個配置項創(chuàng)建相應的Kaptcha驗證碼Servlet,并將其注冊到ServletContext中。
-
使用addServlet方法注冊Servlet,其中Servlet的名稱由配置項的鍵和常量后綴拼接而成。
-
使用addMapping方法為Servlet指定映射路徑,路徑由配置項的path屬性決定。
-
通過setInitParameter方法設置Servlet的初始化參數(shù),這些參數(shù)來自于kaptchaProps和subProps,其中subProps是通過ConfigUtils類生成的。
-
注冊完成后,每個Kaptcha配置項對應的Servlet就可以在指定的路徑上響應請求了。
-
總體而言,ServletRegisterInitializer類負責在Servlet容器啟動時注冊Kaptcha驗證碼生成的Servlet,并根據(jù)配置項提供靈活的配置選項。
?Kaptcha詳細配置
這里提供了每個配置項的說明和默認值:
配置項 | 配置說明 | 默認值 |
---|---|---|
kaptcha.border | 圖片邊框,合法值:yes,no | yes |
kaptcha.border.color | 邊框顏色,合法值:r,g,b (and optional alpha) 或者 white, black, blue | black |
kaptcha.image.width | 圖片寬度 | 200 |
kaptcha.image.height | 圖片高度 | 50 |
kaptcha.producer.impl | 圖片實現(xiàn)類 | com.google.code.kaptcha.impl.DefaultKaptcha |
kaptcha.textproducer.impl | 文本實現(xiàn)類 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
kaptcha.textproducer.char.string | 文本集合,驗證碼值從此集合中獲取 | abcde2345678gfynmnpwx |
kaptcha.textproducer.char.length | 驗證碼長度 | 5 |
kaptcha.textproducer.font.names | 字體 | Arial, Courier |
kaptcha.textproducer.font.size | 字體大小 | 40px |
kaptcha.textproducer.font.color | 字體顏色,合法值:r,g,b 或者 white, black, blue | black |
kaptcha.textproducer.char.space | 文字間隔 | 2 |
kaptcha.noise.impl | 干擾實現(xiàn)類 | com.google.code.kaptcha.impl.DefaultNoise |
kaptcha.noise.color | 干擾顏色,合法值:r,g,b 或者 white, black, blue | black |
kaptcha.obscurificator.impl | 圖片樣式 | com.google.code.kaptcha.impl.WaterRipple, com.google.code.kaptcha.impl.FishEyeGimpy, com.google.code.kaptcha.impl.ShadowGimpy |
kaptcha.background.impl | 背景實現(xiàn)類 | com.google.code.kaptcha.impl.DefaultBackground |
kaptcha.background.clear.from | 背景顏色漸變,開始顏色 | light grey |
kaptcha.background.clear.to | 背景顏色漸變,結束顏色 | white |
kaptcha.word.impl | 文字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
kaptcha.session.key | Session Key | KAPTCHA_SESSION_KEY |
kaptcha.session.date | Session Date | KAPTCHA_SESSION_DATE |
但是這個配置具體還是以代碼為準。?
我們剛剛使用的就是?kaptcha.items 配置多個驗證碼生成器。
kaptcha.items 是一個包含驗證碼生成器配置信息的Map。在這個Map中,每個key代表一個特定的驗證碼生成器的名稱,而對應的value則包括了該生成器的詳細配置。這些配置可能涉及驗證碼的長度、字符集、字體樣式、噪點類型等多個參數(shù),以確保生成的驗證碼符合特定的需求和標準。通過這樣的映射關系,系統(tǒng)能夠靈活地選擇和使用不同的驗證碼生成器,并根據(jù)具體的場景需求進行定制化配置,以提供更加安全和個性化的驗證碼生成服務。這種模塊化的設計使得系統(tǒng)在驗證碼生成方面具有較高的可擴展性和定制性,適應不同應用場景的需求。
如上,我們配置了兩個驗證碼,這兩個驗證碼都是可以應用的。
當然,你也可以配置多個驗證碼。?
為了使用 kaptcha.items 配置多個驗證碼生成器,你可以按照以下方式擴展和完善配置。
在這里,我仍然以兩個驗證碼生成器("home" 和 "admin")為例,提供詳細的配置說明:
kaptcha:items:# Configuration for home captcha generatorhome:path: /home/captcha # URL路徑session:key: HOME_KAPTCHA_SESSION_KEY # Session中驗證碼的鍵date: HOME_KAPTCHA_SESSION_DATE # Session中驗證碼生成時間的鍵producer:impl: com.google.code.kaptcha.impl.DefaultKaptcha # 圖片生成器實現(xiàn)類width: 200 # 圖片寬度height: 50 # 圖片高度textproducer:impl: com.google.code.kaptcha.text.impl.DefaultTextCreator # 文本生成器實現(xiàn)類char.string: abcde2345678gfynmnpwx # 文本集合char.length: 5 # 驗證碼長度font.names: Arial, Courier # 字體font.size: 40 # 字體大小font.color: black # 字體顏色char.space: 2 # 文字間隔noise:impl: com.google.code.kaptcha.impl.DefaultNoise # 干擾實現(xiàn)類color: black # 干擾顏色obscurificator:impl: com.google.code.kaptcha.impl.WaterRipple # 圖片樣式background:impl: com.google.code.kaptcha.impl.DefaultBackground # 背景實現(xiàn)類clear:from: light grey # 背景顏色漸變開始顏色to: white # 背景顏色漸變結束顏色word:impl: com.google.code.kaptcha.text.impl.DefaultWordRenderer # 文字渲染器# Configuration for admin captcha generatoradmin:path: /admin/captchasession:key: ADMIN_KAPTCHA_SESSION_KEYdate: ADMIN_KAPTCHA_SESSION_DATEproducer:impl: com.google.code.kaptcha.impl.DefaultKaptchawidth: 200height: 50textproducer:impl: com.google.code.kaptcha.text.impl.DefaultTextCreatorchar.string: abcde2345678gfynmnpwxchar.length: 5font.names: Arial, Courierfont.size: 40font.color: blackchar.space: 2noise:impl: com.google.code.kaptcha.impl.DefaultNoisecolor: blackobscurificator:impl: com.google.code.kaptcha.impl.WaterRipplebackground:impl: com.google.code.kaptcha.impl.DefaultBackgroundclear:from: light greyto: whiteword:impl: com.google.code.kaptcha.text.impl.DefaultWordRenderer
學習完成之后我們就可以來做一個關于驗證碼的小項目了。
需求
界面如下圖所示:
- 頁面生成驗證碼;
- 輸入驗證碼,點擊提交,驗證用戶輸入驗證碼是否正確,正確則進行頁面跳轉。
準備工作
創(chuàng)建項目,引入SpringMVC的依賴包,?把前端頁面放在項目中:
?
index.html:
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>驗證碼</title><style>#inputCaptcha {height: 30px;vertical-align: middle; }#verificationCodeImg{vertical-align: middle; }#checkCaptcha{height: 40px;width: 100px;}</style>
</head><body><h1>輸入驗證碼</h1><div id="confirm"><input type="text" name="inputCaptcha" id="inputCaptcha"><img id="verificationCodeImg" src="/admin/captcha" style="cursor: pointer;" title="看不清?換一張" /><input type="button" value="提交" id="checkCaptcha"></div><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>$("#verificationCodeImg").click(function(){$(this).hide().attr('src', '/admin/captcha?dt=' + new Date().getTime()).fadeIn();});$("#checkCaptcha").click(function () {alert("驗證碼校驗");});</script>
</body></html>
這前端代碼是一個簡單的驗證碼輸入頁面:
-
HTML結構:
- <!DOCTYPE html>: 定義文檔類型為HTML。
- <html lang="en">: 標明文檔的語言為英語。
- <head>: 包含文檔的元信息,如字符集和頁面標題。
- <body>: 包含頁面的實際內(nèi)容。
-
CSS樣式:設置了一些樣式,如輸入框高度、垂直對齊方式等。
-
頁面內(nèi)容:
- <h1>輸入驗證碼</h1>: 頁面標題,提示用戶當前頁面的目的。
- <div id="confirm">: 包裹驗證碼輸入相關元素的容器。
-
驗證碼相關元素:
- <input type="text" name="inputCaptcha" id="inputCaptcha">: 用于輸入驗證碼的文本框。
- <img id="verificationCodeImg" src="/admin/captcha" style="cursor: pointer;" title="看不清?換一張" />: 顯示驗證碼圖片的img標簽。通過點擊圖片,觸發(fā)jQuery事件,在點擊時隱藏圖片,通過添加時間戳參數(shù)來強制瀏覽器刷新圖片,以獲取新的驗證碼圖片。
<img id="verificationCodeImg">: 創(chuàng)建一個圖片元素,使用id屬性為其指定一個唯一的標識符,方便通過JavaScript或CSS進行操作。src="/admin/captcha": 設置圖片的源路徑為/admin/captcha,這是驗證碼圖片的獲取路徑,它將相對于當前頁面的 URL 進行解析;此處可以是絕對路徑、相對路徑和網(wǎng)絡路徑。
也就是說我們下面配置的URL返回的是一個圖片:style="cursor: pointer;": 添加樣式,將鼠標指針設置為手型,以提示用戶該圖片可以點擊。
title="看不清?換一張": 設置圖片的標題,這將在用戶將鼠標懸停在圖片上時顯示。提示用戶如果驗證碼不清晰,可以點擊圖片來獲取新的驗證碼。
- <input type="button" value="提交" id="checkCaptcha">: 提交按鈕,用于觸發(fā)驗證碼校驗。
- 相關的jQuery事件:
?$("#verificationCodeImg").click(function(){$(this).hide().attr('src', '/admin/captcha?dt=' + new Date().getTime()).fadeIn(); });
$("#verificationCodeImg").click(): 通過jQuery選擇器選中id為verificationCodeImg的圖片元素,然后為其綁定一個點擊事件。
$(this).hide(): 在點擊事件中,首先隱藏當前的驗證碼圖片。
.attr('src', '/admin/captcha?dt=' + new Date().getTime()): 修改圖片的src屬性,通過添加時間戳參數(shù)(?dt=' + new Date().getTime()),以確保瀏覽器認為這是一個新的URL,從而強制刷新驗證碼圖片。
.fadeIn(): 將修改后的圖片以淡入效果顯示,使新的驗證碼圖片在頁面中漸顯出來。
-
jQuery腳本:
- 引入jQuery庫。
- $("#verificationCodeImg").click(): 給驗證碼圖片添加點擊事件,通過隱藏當前圖片、修改src屬性添加時間戳參數(shù)、然后漸顯顯示新圖片,實現(xiàn)更換驗證碼的效果。
- $("#checkCaptcha").click(): 給提交按鈕添加點擊事件,點擊按鈕時彈出一個提示框,顯示"驗證碼校驗"。
總體而言,這段前端代碼實現(xiàn)了一個簡單的驗證碼輸入頁面,其中用戶可以通過點擊驗證碼圖片來更換驗證碼,點擊提交按鈕會觸發(fā)一個提示框。
success.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>驗證成功頁</title><script>console.log("Frontend: success.html loaded."); // 添加調(diào)試信息</script>
</head>
<body><h1>驗證成功</h1>
</body>
</html>
可以直接運行看看:
點擊圖片就可以切換新的驗證碼。
你也可以通過更改配置來調(diào)整界面:
參考邏輯
如果我們想自己實現(xiàn)一個驗證碼的程序,并且希望使用類似Kaptcha的方式,可以參考這個類的實現(xiàn):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.google.code.kaptcha.servlet;import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.util.Config;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class KaptchaServlet extends HttpServlet implements Servlet {private Properties props = new Properties();private Producer kaptchaProducer = null;private String sessionKeyValue = null;private String sessionKeyDateValue = null;public KaptchaServlet() {}public void init(ServletConfig conf) throws ServletException {super.init(conf);ImageIO.setUseCache(false);Enumeration<?> initParams = conf.getInitParameterNames();while(initParams.hasMoreElements()) {String key = (String)initParams.nextElement();String value = conf.getInitParameter(key);this.props.put(key, value);}Config config = new Config(this.props);this.kaptchaProducer = config.getProducerImpl();this.sessionKeyValue = config.getSessionKey();this.sessionKeyDateValue = config.getSessionDate();}public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setDateHeader("Expires", 0L);resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");resp.addHeader("Cache-Control", "post-check=0, pre-check=0");resp.setHeader("Pragma", "no-cache");resp.setContentType("image/jpeg");String capText = this.kaptchaProducer.createText();req.getSession().setAttribute(this.sessionKeyValue, capText);req.getSession().setAttribute(this.sessionKeyDateValue, new Date());BufferedImage bi = this.kaptchaProducer.createImage(capText);ServletOutputStream out = resp.getOutputStream();ImageIO.write(bi, "jpg", out);}
}
這個類是一個實現(xiàn)驗證碼生成的Servlet類,主要用于生成Kaptcha驗證碼圖片。以下是這個類的主要功能:
-
初始化方法?(init):
- 在Servlet初始化時被調(diào)用。
- 設置ImageIO的緩存使用為false,確保驗證碼圖片不會被緩存。
- 通過獲取初始化參數(shù),使用這些參數(shù)初始化 Config 對象。
- 從?Config 對象中獲取驗證碼生成器、會話鍵名和日期鍵名。
-
GET請求處理方法?(doGet):
- 處理HTTP GET請求,用于生成驗證碼圖片。
- 設置HTTP響應頭,禁用緩存,設置響應類型為JPEG。
- 通過驗證碼生成器 (kaptchaProducer) 創(chuàng)建驗證碼文本和圖片。
- 將驗證碼文本和日期信息存儲在會話中。
- 將驗證碼圖片寫入響應輸出流。
-
屬性:
- props: 用于存儲Servlet初始化參數(shù)的 Properties 對象。
- kaptchaProducer: 用于生成驗證碼圖片的 Producer?對象。
- sessionKeyValue: 存儲驗證碼文本的會話鍵名。
- sessionKeyDateValue: 存儲驗證碼日期信息的會話鍵名。
-
構造方法:沒有參數(shù)的構造方法。
這個類的作用是處理GET請求,生成Kaptcha驗證碼圖片,并將驗證碼文本和日期信息存儲在會話中。這是一個常見的用于實現(xiàn)驗證碼功能的Servlet類。
主要邏輯包括:
- 初始化配置:在 init 方法中,通過獲取Servlet初始化參數(shù),創(chuàng)建 Config 對象,從中獲取驗證碼生成器、會話鍵名和日期鍵名。
- 生成驗證碼圖片:在 doGet 方法中,設置HTTP響應頭,生成驗證碼文本和圖片,將驗證碼文本和日期信息存儲在會話中,并將驗證碼圖片寫入響應輸出流。
- 使用ImageIO生成圖片:使用 ImageIO 類來創(chuàng)建驗證碼圖片,并通過 ServletOutputStream 將圖片寫入響應輸出流。
接下來我們只需要完成KaptchaController 類的編寫工作,然后結合導入的前面提到的 KaptchaServlet 類,就可以構成一個簡單的驗證碼程序。
我們的 KaptchaController 類主要用于驗證碼的校驗,而 KaptchaServlet 類則用于生成驗證碼圖片。
關鍵的步驟如下:
-
生成驗證碼:使用 KaptchaServlet 類的 doGet 方法生成驗證碼圖片,并在其中將驗證碼文本和日期信息存儲在會話中。
-
校驗驗證碼:
- 使用 KaptchaController 類的 check 方法,獲取用戶輸入的驗證碼,并從會話中獲取生成的驗證碼文本和日期信息。
- 比對用戶輸入的驗證碼是否與生成的驗證碼一致,并檢查驗證碼是否過期。
-
前端交互:
- 在前端頁面中引用驗證碼圖片的路徑為 /admin/captcha。
- 用戶在頁面上輸入驗證碼,并通過提交按鈕觸發(fā) KaptchaController 的 check 方法進行驗證。
總之,在整個流程中,用戶在頁面上看到的驗證碼圖片和用戶輸入的驗證碼將經(jīng)由我們的前端和后端協(xié)作實現(xiàn)。?
約定前后端交互接口?
需求分析: 在前后端交互的過程中,后端需要提供兩個主要服務,分別是:
- 生成驗證碼,并將生成的驗證碼返回給前端。
- 校驗用戶輸入的驗證碼是否正確。
接口定義:
-
生成驗證碼
- 請求:GET /admin/captcha
- 響應:圖片內(nèi)容
用戶通過瀏覽器發(fā)送一個GET請求至服務器,路徑為/admin/captcha。服務器在收到請求后,生成一個驗證碼圖片,并將該圖片的內(nèi)容作為響應返回給瀏覽器。瀏覽器接收到響應后,在頁面上顯示驗證碼圖片。
-
校驗驗證碼是否正確
- 請求:
- POST /admin/check
- 參數(shù):captcha=xn8d(用戶輸入的驗證碼)
- 響應:true 或 false
用戶在登錄或提交表單時,通過瀏覽器向服務器發(fā)送一個POST請求,路徑為/admin/check,同時攜帶用戶輸入的驗證碼參數(shù)(例如:captcha=xn8d)。服務器收到請求后,根據(jù)用戶輸入的驗證碼進行校驗,如果驗證碼正確,則返回true,表示驗證成功;否則返回false,表示驗證失敗。
- 請求:
通過以上接口定義,前端可以實現(xiàn)驗證碼的生成和校驗功能,增強系統(tǒng)的安全性和用戶驗證機制。
實現(xiàn)服務器端代碼?
引入依賴
如前
通過配置創(chuàng)建驗證碼生成器
如前
啟動項目,訪問http://127.0.0.1:8080/admin/captcha,顯示驗證碼。
驗證碼校驗
package com.example.captchademo;import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpSession;
import java.util.Date;@RequestMapping("/admin")
@RestController
public class KaptchaController {private static final String KAPTCHA_SESSION_KEY = "KAPTCHA_SESSION_KEY";private static final String KAPTCHA_SESSION_DATE = "KAPTCHA_SESSION_DATE";private static final long TIME_OUT = 60*1000;//一分鐘, 毫秒數(shù)/*** 校驗驗證碼是否正確* @param inputCaptcha 用戶輸入的驗證碼* @return*/@RequestMapping("/check")public boolean check(String inputCaptcha, HttpSession session){//1. 判斷輸入的驗證碼是否為空//2. 獲取生成的驗證碼//3. 比對 生成的驗證碼和輸入的驗證碼是否一致//4. 確認驗證碼是否過期if (!StringUtils.hasLength(inputCaptcha)){return false;}//生成的驗證碼(正確的驗證碼)String saveCaptcha = (String)session.getAttribute(KAPTCHA_SESSION_KEY);Date savaCaptchaDate = (Date)session.getAttribute(KAPTCHA_SESSION_DATE);if (inputCaptcha.equalsIgnoreCase(saveCaptcha)){//不區(qū)分大小寫if (savaCaptchaDate!=null || System.currentTimeMillis()-savaCaptchaDate.getTime()<TIME_OUT){return true;}}return false;}
}
比對Session中存儲的驗證碼是否和用戶輸入的?致?
如果?致,并且時間在?分鐘以為就認為成功。?
調(diào)整前端頁面代碼
修改 index.html :
補充ajax代碼,點擊提交按鈕,發(fā)送請求去服務端進行校驗:
$("#checkCaptcha").click(function () {// 發(fā)送Ajax請求校驗驗證碼$.ajax({url: "/admin/check", // 服務端校驗驗證碼的接口路徑type: "post", // 請求類型為POSTdata: { inputCaptcha: $("#inputCaptcha").val() }, // 向服務端發(fā)送的數(shù)據(jù),包括用戶輸入的驗證success: function (result) {// 請求成功的回調(diào)函數(shù)if (result) {// 如果服務端返回true,表示驗證碼校驗成功location.href = "success.html"; // 重定向到success.html頁面} else {// 如果服務端返回false,表示驗證碼校驗失敗alert("驗證碼錯誤"); // 彈出提示框,提示用戶驗證碼錯誤console.log("Frontend: Verification failed."); // 添加調(diào)試信息$("#inputCaptcha").val(""); // 清空用戶輸入的驗證碼}},error: function (xhr, status, error) {console.log("Ajax Error:", xhr, status, error); // 輸出Ajax請求錯誤信息}});
});
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>驗證碼</title><style>#inputCaptcha {height: 30px;vertical-align: middle; }#verificationCodeImg{vertical-align: middle; }#checkCaptcha{height: 40px;width: 100px;}</style>
</head><body><h1>輸入驗證碼</h1><div id="confirm"><input type="text" name="inputCaptcha" id="inputCaptcha"><img id="verificationCodeImg" src="/admin/captcha" style="cursor: pointer;" title="看不清?換一張" /><input type="button" value="提交" id="checkCaptcha"></div><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>$("#verificationCodeImg").click(function(){$(this).hide().attr('src', '/admin/captcha?dt=' + new Date().getTime()).fadeIn();});$("#checkCaptcha").click(function () {// 發(fā)送Ajax請求校驗驗證碼$.ajax({url: "/admin/check", // 服務端校驗驗證碼的接口路徑type: "post", // 請求類型為POSTdata: { inputCaptcha: $("#inputCaptcha").val() }, // 向服務端發(fā)送的數(shù)據(jù),包括用戶輸入的驗證success: function (result) {// 請求成功的回調(diào)函數(shù)if (result) {// 如果服務端返回true,表示驗證碼校驗成功location.href = "success.html"; // 重定向到success.html頁面} else {// 如果服務端返回false,表示驗證碼校驗失敗alert("驗證碼錯誤"); // 彈出提示框,提示用戶驗證碼錯誤console.log("Frontend: Verification failed."); // 添加調(diào)試信息$("#inputCaptcha").val(""); // 清空用戶輸入的驗證碼}},error: function (xhr, status, error) {console.log("Ajax Error:", xhr, status, error); // 輸出Ajax請求錯誤信息}});
});</script>
</body></html>