SpringBoot集成FastDFS+Nginx整合基于Token的防盜鏈的方法
為什么要用SpringBoot?
SpringBoot是由Pivotal團(tuán)隊(duì)提供的全新框架,其設(shè)計(jì)目的是用來(lái)簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開發(fā)過程。該框架使用了特定的方式來(lái)進(jìn)行配置,從而使開發(fā)人員不再需要定義樣板化的配置。通過這種方式,Spring Boot致力于在蓬勃發(fā)展的快速應(yīng)用開發(fā)領(lǐng)域(rapid application development)成為領(lǐng)導(dǎo)者。
- 創(chuàng)建獨(dú)立的Spring應(yīng)用程序
- 嵌入的Tomcat,無(wú)需部署WAR文件
- 簡(jiǎn)化Maven配置
- 自動(dòng)配置Spring
- 提供生產(chǎn)就緒型功能,如指標(biāo),健康檢查和外部配置
- 絕對(duì)沒有代碼生成并且對(duì)XML也沒有配置要求
為什么要用Nginx?
概述
Nginx(engine x)是一個(gè)開源的,支持高并發(fā)的www服務(wù)和代理服務(wù)軟件。Nginx是俄羅斯人Igor Sysoev開發(fā)的,最初被應(yīng)用到俄羅斯的大型網(wǎng)站(www.rambler.ru)上。后來(lái)作者將源代碼以類BSD許可證的形式開源出來(lái)供全球使用。在功能應(yīng)用方面,Nginx不僅是一個(gè)優(yōu)秀的Web服務(wù)軟件,還具有反向代理負(fù)載均衡和緩存的功能。在反向代理負(fù)載均衡方面類似于LVS負(fù)載均衡及HAProxy等你專業(yè)代理軟件。Nginx部署起來(lái)更加方便簡(jiǎn)單,在緩存服務(wù)功能方面,有類似于Squid等專業(yè)的緩存服務(wù)軟件。Nginx可以運(yùn)行在UNIX、Linux、MS Windows Server、Mac OS X Server、Solaris等操作系統(tǒng)中。
Nginx的重要特性
- 可以針對(duì)靜態(tài)資源高速節(jié)點(diǎn)并發(fā)訪問及緩存。
- 可以使用反向代理加速,并且可以進(jìn)行數(shù)據(jù)緩存。
- 具有簡(jiǎn)單負(fù)載均衡,節(jié)點(diǎn)健康檢查和容錯(cuò)功能。
- 支持遠(yuǎn)程Fast CGI服務(wù)的緩存加速。
- 支持Fast CGI、Uwsgi、SCGI、Memcached Server的加速和緩存。
- 支持SSL、TLS、SNI。
- 具有模塊化的架構(gòu)。
- 過濾器包括gzip壓縮、ranges支持、chunked響應(yīng)、XSLT、SSL和圖像縮放等功能。
- 在SSL過濾器中,包含多個(gè)SSL頁(yè)面,如果經(jīng)由Fast CGI或反向代理處理,可以并行處理。
Nginx所具備的WWW服務(wù)特性
- 支持基于域名、端口和IP的虛擬主機(jī)配置。
- 支持KeepAlived和piplined連接。
- 可進(jìn)行簡(jiǎn)單、方便、靈活的配置和管理。
- 支持修改Nginx配置,并且在代碼上線時(shí),可平滑重啟,不中斷業(yè)務(wù)訪問。
- 可自定義訪問日志格式,臨時(shí)緩沖寫日志操作,快速日志輪詢及通過rsyslog處理日志。
- 可利用信號(hào)控制Nginx進(jìn)程。
- 支持3xx-5xxHTTP狀態(tài)碼重定向。
- 支持rewrite模塊,支持URI重寫及正則表達(dá)式匹配。
- 支持基于客戶端IP地址和HTTP基本認(rèn)證的訪問控制。
- 支持PUT、DELETE、MKCOL、COPY、MOVE等特殊的HTTP請(qǐng)求方法。
- 支持FLV流和MP4流技術(shù)產(chǎn)品應(yīng)用。
- 支持HTTP響應(yīng)速率限制。
- 支持同一IP地址的并發(fā)連接或請(qǐng)求限制。
- 支持郵件服務(wù)代理。
- 支持高并發(fā),可以支持幾百萬(wàn)并發(fā)連接。
- 資源消耗少,在3萬(wàn)并發(fā)連接下,可以開啟10個(gè)nginx的線程消耗的內(nèi)存不到200MB。
- 可以做HTTP反向代理及加速緩存,及負(fù)載均衡功能,內(nèi)置對(duì)RS節(jié)點(diǎn)服務(wù)器健康檢查功能,折現(xiàn)但能夠與專業(yè)的HAProxy或LVS的功能。
- 具備Squid等專業(yè)緩存軟件等的緩存功能。
- 支持異步網(wǎng)絡(luò)I/O事件模型epoll(Linux2.6+)。
Nginx軟件主要企業(yè)應(yīng)用
- 作為Web服務(wù)軟件。
- 使用Nginx運(yùn)行HTML、JS、CSS、小圖片等靜態(tài)數(shù)據(jù)(類似于Lighttpd)。
- 結(jié)合Fast CGI運(yùn)行PHP等動(dòng)態(tài)程序(例如使用fastcgi_pass方式)。
- Nginx結(jié)合Tomcat/Resin等支持Java動(dòng)態(tài)程序(常用proxy_pass)。
- 反向代理或負(fù)載均衡服務(wù)(Nginx從1.9.0開始就開始支持TCP的代理了)。
- 前端業(yè)務(wù)數(shù)據(jù)緩存服務(wù)。
Web服務(wù)應(yīng)用產(chǎn)品性能對(duì)比
- 靜態(tài)數(shù)據(jù)的訪問上:處理小文件(小于1MB)時(shí),Nginx和Lighttpd比Apache更有優(yōu)勢(shì),Nginx處理小文件的優(yōu)勢(shì)明顯,Lighttpd綜合最強(qiáng)。
- 動(dòng)態(tài)數(shù)據(jù)的訪問上:三者差距不大,Apache更有優(yōu)勢(shì),因?yàn)樘幚韯?dòng)態(tài)數(shù)據(jù)的能力在于PHP(Java)和后端數(shù)據(jù)庫(kù)的服務(wù)能力,也就是說(shuō)瓶頸不在Web服務(wù)器上。
- 一般情況下普通PHP引擎支持的并發(fā)連接參考值3001000。Java引擎和數(shù)據(jù)庫(kù)的并發(fā)連接參考值3001500。
為什么Nginx比Apache的性能高?
- Nginx使用最新版的eepoll(Linux 2.6內(nèi)核)和kqueue(FreeBSD)異步網(wǎng)絡(luò)I/O模型,而Apache使用的是傳統(tǒng)的select模型。
- 目前Linux下能夠承受高并發(fā)訪問的Squid、Memcached軟件采用都是epoll模型。
- 處理大量的連接的讀寫時(shí),Apache所采用的select網(wǎng)絡(luò)I/O模型比較低。
如何正確采用Web服務(wù)器?
- 靜態(tài)業(yè)務(wù):如果是高并發(fā)場(chǎng)景,盡量采用Nginx或Lighttpd,二者首選Nginx。
- 動(dòng)態(tài)業(yè)務(wù):理論上采用Nginx和Apache均可,建議使用Nginx,為了避免相同業(yè)務(wù)服務(wù)的軟件多樣化,增加維護(hù)成本,動(dòng)態(tài)業(yè)務(wù)可以使用Nginx兼做前端代理,再根據(jù)頁(yè)面的元素或目錄轉(zhuǎn)發(fā)到其他的服務(wù)器進(jìn)行處理。
- 既有動(dòng)態(tài)業(yè)務(wù)又有靜態(tài)業(yè)務(wù),就用Nginx。
關(guān)于部署,就不在重復(fù)了,如果需要請(qǐng)移步《Java高級(jí)架構(gòu)之FastDFS分布式文件集群》:
使用IDEA場(chǎng)景啟動(dòng)器創(chuàng)建工程
創(chuàng)建Maven工程,修改POM.xml文件添加如下依賴:
<dependencies> <!-- SpringBoot的自動(dòng)配置相關(guān)依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>1.5.20.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>1.5.20.RELEASE</version> </dependency> <!-- 日志相關(guān)的依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> <version>1.5.20.RELEASE</version> </dependency> <!-- 對(duì)象池相關(guān)的依賴 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.0</version> </dependency> </dependencies>
創(chuàng)建必要的包
- annotation:存放相關(guān)的注解
- autoconfiguation: 存儲(chǔ)自動(dòng)配置類
- factory: 存放工廠類
- properties: 存放配置參數(shù)類
- service: 存放服務(wù)類
一般情況下,SpringBoot都會(huì)提供相應(yīng)的@EnableXxx注解標(biāo)注在應(yīng)用的主啟動(dòng)類上開啟某個(gè)功能:
// EnableFastdfsClient.java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(FastdfsAutoConfiguration.class) @Documented public @interface EnableFastdfsClient { }
下面是相關(guān)的自動(dòng)配置類:
// FastdfsAutoConfiguration.java @Configuration @EnableConfigurationProperties(FastdfsProperties.class) public class FastdfsAutoConfiguration { @Autowired private FastdfsProperties fastdfsProperties; @Bean @ConditionalOnMissingBean(FastdfsClientService.class) public FastdfsClientService fastdfsClientService() throws Exception { return new FastdfsClientService(fastdfsProperties); } }
創(chuàng)建相關(guān)的工廠類:
// StorageClientFactory.java // 用于創(chuàng)建連接對(duì)象的工廠類 public class StorageClientFactory implements PooledObjectFactory<StorageClient> { @Override public PooledObject<StorageClient> makeObject() throws Exception { TrackerClient client = new TrackerClient(); TrackerServer server = client.getConnection(); return new DefaultPooledObject<>(new StorageClient(server, null)); } @Override public void destroyObject(PooledObject<StorageClient> p) throws Exception { p.getObject().getTrackerServer().close(); } @Override public boolean validateObject(PooledObject<StorageClient> p) { return false; } @Override public void activateObject(PooledObject<StorageClient> p) throws Exception { } @Override public void passivateObject(PooledObject<StorageClient> p) throws Exception { } }
Properties類用來(lái)映射application.properties或者application.yml配置文件:
// FastdfsProperties.java @ConfigurationProperties(prefix = "fastdfs") public class FastdfsProperties { // 連接超時(shí)時(shí)間 // 網(wǎng)絡(luò)超時(shí)時(shí)間 // 字符集編碼 // 是否使用Token // Token加密密鑰 // 跟蹤器IP地址,多個(gè)使用分號(hào)隔開 // 連接池的連接對(duì)象最大個(gè)數(shù) // 連接池的最大空閑對(duì)象個(gè)數(shù) // 連接池的最小空閑對(duì)象個(gè)數(shù) // Nginx服務(wù)器IP,多個(gè)使用分號(hào)分割 // 獲取連接對(duì)象時(shí)可忍受的等待時(shí)長(zhǎng)(毫秒) private String connectTimeout = "5"; private String networkTimeout = "30"; private String charset = "UTF-8"; private String httpAntiStealToken = "false"; private String httpSecretKey = ""; private String httpTrackerHttpPort = ""; private String trackerServers = ""; private String connectionPoolMaxTotal = "18"; private String connectionPoolMaxIdle = "18"; private String connectionPoolMinIdle = "2"; private String nginxServers = ""; // 需要?jiǎng)?chuàng)建相關(guān)的Setter和Getter方法 }
在Service類中封裝方法, 下面僅展示3個(gè)常用的方法:
// FastdfsClientSerivce.java public class FastdfsClientService { // SpringBoot加載的配置文件 // 連接池配置項(xiàng) // 轉(zhuǎn)換后的配置條目 // 連接池 // Nginx服務(wù)器地址 private FastdfsProperties fdfsProp; private GenericObjectPoolConfig config; private Properties prop; private GenericObjectPool<StorageClient> pool; private String[] nginxServers; private Logger logger; public FastdfsClientService(FastdfsProperties fdfsProp) throws Exception { this.fdfsProp = fdfsProp; this.logger = LoggerFactory.getLogger(getClass()); init(); create(); info(); } /** * 初始化全局客戶端 */ private void init() throws Exception { this.prop = new Properties(); this.logger.info("FastDFS: reading config file..."); this.logger.info("FastDFS: fastdfs.connect_timeout_in_seconds=" + this.fdfsProp.getConnectTimeout()); this.logger.info("FastDFS: fastdfs.network_timeout_in_seconds=" + this.fdfsProp.getNetworkTimeout()); this.logger.info("FastDFS: fastdfs.charset=" + this.fdfsProp.getCharset()); this.logger.info("FastDFS: fastdfs.http_anti_steal_token=" + this.fdfsProp.getHttpAntiStealToken()); this.logger.info("FastDFS: fastdfs.http_secret_key=" + this.fdfsProp.getHttpSecretKey()); this.logger.info("FastDFS: fastdfs.http_tracker_http_port=" + this.fdfsProp.getHttpTrackerHttpPort()); this.logger.info("FastDFS: fastdfs.tracker_servers=" + this.fdfsProp.getTrackerServers()); this.logger.info("FastDFS: fastdfs.connection_pool_max_total=" + this.fdfsProp.getConnectionPoolMaxTotal()); this.logger.info("FastDFS: fastdfs.connection_pool_max_idle=" + this.fdfsProp.getConnectionPoolMaxIdle()); this.logger.info("FastDFS: fastdfs.connection_pool_min_idle=" + this.fdfsProp.getConnectionPoolMinIdle()); this.logger.info("FastDFS: fastdfs.nginx_servers=" + this.fdfsProp.getNginxServers()); this.prop.put("fastdfs.connect_timeout_in_seconds", this.fdfsProp.getConnectTimeout()); this.prop.put("fastdfs.network_timeout_in_seconds", this.fdfsProp.getNetworkTimeout()); this.prop.put("fastdfs.charset", this.fdfsProp.getCharset()); this.prop.put("fastdfs.http_anti_steal_token", this.fdfsProp.getHttpAntiStealToken()); this.prop.put("fastdfs.http_secret_key", this.fdfsProp.getHttpSecretKey()); this.prop.put("fastdfs.http_tracker_http_port", this.fdfsProp.getHttpTrackerHttpPort()); this.prop.put("fastdfs.tracker_servers", this.fdfsProp.getTrackerServers()); ClientGlobal.initByProperties(this.prop); } /** * 顯示初始化信息 */ private void info() { this.logger.info("FastDFS parameter: ConnectionPoolMaxTotal ==> " + this.pool.getMaxTotal()); this.logger.info("FastDFS parameter: ConnectionPoolMaxIdle ==> " + this.pool.getMaxIdle()); this.logger.info("FastDFS parameter: ConnectionPoolMinIdle ==> " + this.pool.getMinIdle()); this.logger.info("FastDFS parameter: NginxServer ==> " + Arrays.toString(this.nginxServers)); this.logger.info(ClientGlobal.configInfo()); } /** * 創(chuàng)建連接池 */ private void create() { this.config = new GenericObjectPoolConfig(); this.logger.info("FastDFS Client: Creating connection pool..."); this.config.setMaxTotal(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxTotal())); this.config.setMaxIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMaxIdle())); this.config.setMinIdle(Integer.parseInt(this.fdfsProp.getConnectionPoolMinIdle())); StorageClientFactory factory = new StorageClientFactory(); this.pool = new GenericObjectPool<StorageClient>(factory, this.config); this.nginxServers = this.fdfsProp.getNginxServers().split(","); } /** * Nginx服務(wù)器負(fù)載均衡算法 * * @param servers 服務(wù)器地址 * @param address 客戶端IP地址 * @return 可用的服務(wù)器地址 */ private String getNginxServer(String[] servers, String address) { int size = servers.length; int i = address.hashCode(); int index = abs(i % size); return servers[index]; } /** * 帶有防盜鏈的下載 * * @param fileGroup 文件組名 * @param remoteFileName 遠(yuǎn)程文件名稱 * @param clientIpAddress 客戶端IP地址 * @return 完整的URL地址 */ public String autoDownloadWithToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception { int ts = (int) (System.currentTimeMillis() / 1000); String token = ProtoCommon.getToken(remoteFileName, ts, ClientGlobal.getG_secret_key()); String nginx = this.getNginxServer(this.nginxServers, clientIpAddress); return "http://" + nginx + "/" + fileGroup + "/" + remoteFileName + "?token=" + token + "&ts=" + ts; } /** * 上傳文件,適合上傳圖片 * * @param buffer 字節(jié)數(shù)組 * @param ext 擴(kuò)展名 * @return 文件組名和ID */ public String[] autoUpload(byte[] buffer, String ext) throws Exception { String[] upload = this.upload(buffer, ext, null); return upload; } /** * 不帶防盜鏈的下載,如果開啟防盜鏈會(huì)導(dǎo)致該方法拋出異常 * * @param fileGroup 文件組名 * @param remoteFileName 遠(yuǎn)程文件ID * @param clientIpAddress 客戶端IP地址,根據(jù)客戶端IP來(lái)分配Nginx服務(wù)器 * @return 完整的URL地址 */ public String autoDownloadWithoutToken(String fileGroup, String remoteFileName, String clientIpAddress) throws Exception { if (ClientGlobal.getG_anti_steal_token()) { this.logger.error("FastDFS Client: You've turned on Token authentication."); throw new Exception("You've turned on Token authentication."); } String nginx = this.getNginxServer(this.nginxServers, clientIpAddress); return "http://" + nginx + fileGroup + "/" + remoteFileName; } // 后面還有好多方法,就不一一展示了 }
為了在IDEA中使用便捷的配置提示功能,我們需要?jiǎng)?chuàng)建元數(shù)據(jù)文件(resources/spring-configuration-metadata.json):
{ "groups": [ { "name": "fastdfs", "type": "com.bluemiaomiao.properties.FastdfsProperties", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" } ], "properties": [ { "name": "connectTimeout", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "5" }, { "name": "networkTimeout", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "30" }, { "name": "charset", "type": "java.lang.String", "defaultValue": "UTF-8" }, { "name": "httpAntiStealToken", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "false" }, { "name": "httpSecretKey", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" }, { "name": "httpTrackerHttpPort", "type": "java.lang.Integer", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" }, { "name": "trackerServers", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" }, { "name": "connectionPoolMaxTotal", "type": "java.lang.Integer", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "18" }, { "name": "connectionPoolMaxIdle", "type": "java.lang.Integer", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "18" }, { "name": "connectionPoolMinIdle", "type": "java.lang.Integer", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties", "defaultValue": "2" }, { "name": "nginxServers", "type": "java.lang.String", "sourceType": "com.bluemiaomiao.properties.FastdfsProperties" } ], "hints": [ { "name": "http_anti_steal_token", "values": [ { "value": "false" }, { "value": "true" } ] } ] }
應(yīng)用到項(xiàng)目中
創(chuàng)建SpringBoot項(xiàng)目,勾選Web選項(xiàng),版本選擇1.5.20
進(jìn)入場(chǎng)景啟動(dòng)器的項(xiàng)目目錄執(zhí)行mvn clean install 將其安裝到本地
在POM.xml文件中添加依賴:
<dependency> <groupId>com.bluemiaomiao</groupId> <artifactId>fastdfs-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
記得開啟IDEA的自動(dòng)導(dǎo)入功能
創(chuàng)建配置文件application.properties
fastdfs.nginx-servers=192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000 fastdfs.tracker-servers=192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122 fastdfs.http-secret-key=2scPwMPctXhbLVOYB0jyuyQzytOofmFCBIYe65n56PPYVWrntxzLIDbPdvDDLJM8QHhKxSGWTcr+9VdG3yptkw fastdfs.http-anti-steal-token=true fastdfs.http-tracker-http-port=8080 fastdfs.network-timeout=30 fastdfs.connect-timeout=5 fastdfs.connection-pool-max-idle=18 fastdfs.connection-pool-min-idle=2 fastdfs.connection-pool-max-total=18 fastdfs.charset=UTF-8
或者使用application.yml
fastdfs: charset: UTF-8 connect-timeout: 5 http-secret-key: 2scPwMPctXhbLVOYB0jyuyQzytOofmFCBIYe65n56PPYVWrntxzLIDbPdvDDLJM8QHhKxSGWTcr+9VdG3yptkw network-timeout: 30 http-anti-steal-token: true http-tracker-http-port: 8080 connection-pool-max-idle: 20 connection-pool-max-total: 20 connection-pool-min-idle: 2 nginx-servers: 192.168.80.2:8000,192.168.80.3:8000,192.168.80.4:8000 tracker-servers: 192.168.80.2:22122,192.168.80.3:22122,192.168.80.4:22122
創(chuàng)建控制器類測(cè)試方法
// controllers.DownloadController.java @Controller @RequestMapping(value = "/download") public class DownloadController { @Autowired private FastdfsClientService service; @ResponseBody @RequestMapping(value = "/image") public String image() throws Exception { // 之前上傳過的數(shù)據(jù),實(shí)際應(yīng)用場(chǎng)景應(yīng)該使用SQL數(shù)據(jù)庫(kù)來(lái)存儲(chǔ) return service.autoDownloadWithToken("group1", "M00/00/00/wKhQA1ysjSGAPjXbAAVFOL7FJU4.tar.gz", "192.168.80.1"); } }
項(xiàng)目主頁(yè):https://github.com/bluemiaomiao/fastdfs-spring-boot-starter
國(guó)內(nèi)項(xiàng)目主頁(yè):https://gitee.com/bluemiaomiao/fastdfs-spring-boot-starter
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java NumberFormat 類的詳解及實(shí)例
這篇文章主要介紹了Java NumberFormat 類的詳解及實(shí)例的相關(guān)資料,數(shù)字格式化類按照本地風(fēng)格習(xí)慣進(jìn)行的數(shù)字顯示,需要的朋友可以參考下2017-08-08javax.net.ssl.SSLException: java.lang.RuntimeException: Coul
這篇文章主要介紹了javax.net.ssl.SSLException: java.lang.RuntimeException: Could not generate DH keypair 解決方法,有需要的朋友們可以學(xué)習(xí)下。2019-08-08@Valid注解的作用及@Valid注解與@Validated的區(qū)別
這篇文章主要介紹了@Valid注解的作用及@Valid注解與@Validated的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08java中List集合及其實(shí)現(xiàn)類的方法詳解
本篇文章給大家?guī)?lái)的內(nèi)容是關(guān)于java中List集合及其實(shí)現(xiàn)類的方法介紹(附代碼),有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對(duì)你有所幫助。下面我們就來(lái)學(xué)習(xí)一下吧2019-06-06SpringBoot整合Keycloak實(shí)現(xiàn)單點(diǎn)登錄的示例代碼
本文主要介紹了SpringBoot整合Keycloak實(shí)現(xiàn)單點(diǎn)登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03聊聊RabbitMQ發(fā)布確認(rèn)高級(jí)問題
這篇文章主要介紹了RabbitMQ發(fā)布確認(rèn)高級(jí)問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01java隨機(jī)生成字符串(字符隨機(jī)生成類 生成隨機(jī)字符組合)
java隨機(jī)生成字符串,字符組合多樣,可以大小字組合、大+小字符+數(shù)字等方式,大家參考使用吧2013-12-12springboot使用yml文件配置多環(huán)境方式(dev、test、prod)
這篇文章主要介紹了springboot使用yml文件配置多環(huán)境方式(dev、test、prod),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Java Spring MVC獲取請(qǐng)求數(shù)據(jù)詳解操作
Spring MVC 是 Spring 提供的一個(gè)基于 MVC 設(shè)計(jì)模式的輕量級(jí) Web 開發(fā)框架,本質(zhì)上相當(dāng)于 Servlet,Spring MVC 角色劃分清晰,分工明細(xì)。由于 Spring MVC 本身就是 Spring 框架的一部分,可以說(shuō)和 Spring 框架是無(wú)縫集成2021-11-11