SpringBoot實(shí)現(xiàn)文章防盜鏈的代碼設(shè)計(jì)
來(lái)今天的正題:springboot實(shí)現(xiàn)圖片防盜鏈。可能看起來(lái)場(chǎng)景比較抽象,這里shigen
給出之前的一個(gè)例子:對(duì)象存儲(chǔ)服務(wù)的流量被盜刷了,當(dāng)時(shí)官方給的解決方案包括我后來(lái)采用的方式就是referer
的限制。
后來(lái)我的對(duì)象存儲(chǔ)服務(wù)的流量就正常了。那今天我也是好奇這個(gè)用springboot怎么實(shí)現(xiàn)。在接下來(lái)的內(nèi)容中,我將會(huì)著重分享我的設(shè)計(jì)。
首先了解一下Referer是什么吧。
什么是Referer
這里告別充滿廣告和垃圾網(wǎng)站的搜索引擎,直接GPT查詢:
Referer(來(lái)源)是HTTP頭部字段之一,用于指示客戶端是從哪個(gè)頁(yè)面跳轉(zhuǎn)或發(fā)起請(qǐng)求的。當(dāng)客戶端(通常是瀏覽器)向服務(wù)器發(fā)送請(qǐng)求時(shí),它會(huì)在HTTP頭部中包含 Referer 字段,告訴服務(wù)器請(qǐng)求的來(lái)源頁(yè)面的URL。這個(gè)字段可以幫助服務(wù)器了解請(qǐng)求的上下文和用戶行為,有助于進(jìn)行數(shù)據(jù)分析、安全檢查等操作。
也就是說(shuō)請(qǐng)求一個(gè)資源的時(shí)候,瀏覽器的請(qǐng)求頭信息中會(huì)帶上Referer
字段標(biāo)示出當(dāng)下的請(qǐng)求的上一個(gè)請(qǐng)求是什么地方來(lái)的。
那基于這個(gè)原理,我們就可以設(shè)計(jì)出自己的防盜鏈。
java代碼的設(shè)計(jì)
基礎(chǔ)版
假設(shè)我們的springboot項(xiàng)目中可以直接通過(guò)http請(qǐng)求訪問(wèn)到某個(gè)路徑下的資源。我們先這樣的嘗試吧。我們的配置肯定要實(shí)現(xiàn)WebMvcConfigurer
這個(gè)接口,實(shí)現(xiàn)資源的映射。那我就直接展示我的代碼:
@Configuration @EnableWebMvc @Slf4j public class MvcConfig implements WebMvcConfigurer { ? /** * 靜態(tài)資源保存目錄 */ public static final String FILE_RESOURCE_PATH = "file:" + System.getProperty("user.dir") + "/files/"; ? @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { log.error("靜態(tài)資源保存目錄:{}", FILE_RESOURCE_PATH); registry.addResourceHandler("/files/**") .addResourceLocations(FILE_RESOURCE_PATH); } }
其實(shí)我們最終實(shí)現(xiàn)的文件路徑就是項(xiàng)目根路徑/files/
文件夾的全部文件。
這樣我們就可以通過(guò)http請(qǐng)求訪問(wèn)了。
但是,明顯的我們的資源不是很安全。因?yàn)槿我鈦?lái)源、任何人都可以訪問(wèn)到它。那我們限制來(lái)源的話,這個(gè)時(shí)候Referer
就可以派上用場(chǎng)了。
升級(jí)版
升級(jí)版本,我們就需要統(tǒng)一攔截一下請(qǐng)求,看看請(qǐng)求頭中是否包含Referer
信息,且是我們約定的Referer
。這樣才能判定是正常的請(qǐng)求,進(jìn)行流量的放行,否則的話就是要去攔截。
接下來(lái)先去設(shè)計(jì)一個(gè)攔截器:
@Slf4j @Component public class ResourceInterceptor extends HandlerInterceptorAdapter { ? @Resource private ReferConfig referConfig; ? /** * 匹配的文件種類 */ private static final String FILE_REGEX = "\.(html|css|js|jpg|jpeg|png|gif|bmp|svg|pdf|doc|docx|xls|xlsx|ppt|pptx|mp4|mov)$"; private static final Pattern FILE_REGEX_PATTERN = Pattern.compile(FILE_REGEX, Pattern.CASE_INSENSITIVE); ? ? @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 獲取請(qǐng)求的 URL String requestUrl = request.getRequestURL().toString(); log.info("requestUrl:{}", requestUrl); // 檢查是否是靜態(tài)資源請(qǐng)求 if (referConfig.isEnabled() && isStaticResource(requestUrl)) { // 檢查防盜鏈策略 if (!isValidReferer(request)) { response.setStatus(HttpServletResponse.SC_FORBIDDEN); return false; } } return true; } ? /** * 正則驗(yàn)證請(qǐng)求的資源 * * @param url 請(qǐng)求資源 * @return 是否匹配 */ private boolean isStaticResource(String url) { return FILE_REGEX_PATTERN.matcher(url).find(); } ? /** * 檢查 Referer 頭,判斷請(qǐng)求是否合法 * * @param request 請(qǐng)求 * @return 是否是合法請(qǐng)求 */ private boolean isValidReferer(HttpServletRequest request) { String referer = request.getHeader("Referer"); return CollectionUtil.contains(referConfig.getAllowedOrigins(), referer); } }
具體的業(yè)務(wù)邏輯的驗(yàn)證都在注釋里,這里需要注意:
- 關(guān)于
Referer
的配置最好寫成動(dòng)態(tài)的,便于后期的拓展 - 對(duì)于url的請(qǐng)求判斷最好使用正則表達(dá)式,因?yàn)閡rl本身請(qǐng)求的就是靜態(tài)資源,但是后邊帶了其他的參數(shù)可能導(dǎo)致直接繞過(guò)
對(duì)于自定的配置類,shigen
是這樣的設(shè)計(jì):
@Configuration @ConfigurationProperties(prefix = "refer") @Data public class ReferConfig { /** * 是否開啟防盜鏈攔截 */ private boolean enabled; /** * 允許的Referer請(qǐng)求 */ private List<String> allowedOrigins; ? }
接下來(lái)就是配置到攔截器上。
@Configuration @EnableWebMvc @Slf4j public class MvcConfig implements WebMvcConfigurer { ? @Resource private UserArgumentResolver userArgumentResolver; ? @Resource private ResourceInterceptor resourceInterceptor; ? /** * 靜態(tài)資源保存目錄 */ public static final String FILE_RESOURCE_PATH = "file:" + System.getProperty("user.dir") + "/files/"; ? @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(userArgumentResolver); } ? @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(resourceInterceptor).addPathPatterns("/**"); } ? @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { log.error("靜態(tài)資源保存目錄:{}", FILE_RESOURCE_PATH); registry.addResourceHandler("/files/**") .addResourceLocations(FILE_RESOURCE_PATH); } }
待一切完成,我們這里再檢查一下配置文件就可以正常的啟動(dòng)服務(wù)進(jìn)行測(cè)試了。
refer: enabled: true allowed-origins: - http://www.shigen.com
此時(shí),我們?cè)俅卧跒g覽器中直接訪問(wèn):
這時(shí)我們想要正常的訪問(wèn),就得借助于接口測(cè)試工具了。
以上就是SpringBoot實(shí)現(xiàn)文章防盜鏈的代碼設(shè)計(jì)的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot文章防盜鏈的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Boot應(yīng)用發(fā)布到Docker的實(shí)現(xiàn)
這篇文章主要介紹了Spring Boot應(yīng)用發(fā)布到Docker的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Java設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解適配器模式
適配器模式(Adapter?Pattern)是作為兩個(gè)不兼容的接口之間的橋梁。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它結(jié)合了兩個(gè)獨(dú)立接口的功能2022-04-04linux系統(tǒng)下java項(xiàng)目在后臺(tái)啟動(dòng)的4種方式總結(jié)
Linux是集多種功能于一身的操作系統(tǒng),它可以讓用戶查看和管理當(dāng)下正在運(yùn)行的進(jìn)程,包括Java程序,這篇文章主要給大家總結(jié)介紹了關(guān)于linux系統(tǒng)下java項(xiàng)目在后臺(tái)啟動(dòng)的4種方式,需要的朋友可以參考下2023-10-10Java基礎(chǔ)學(xué)習(xí)筆記之?dāng)?shù)組詳解
這篇文章主要介紹了Java基礎(chǔ)學(xué)習(xí)筆記之?dāng)?shù)組,結(jié)合實(shí)例形式詳細(xì)分析了java的基本概念、定義、迭代、輸出、反轉(zhuǎn)、排序等常用操作技巧,需要的朋友可以參考下2019-08-08java的Jackson將json字符串轉(zhuǎn)換成泛型List
這篇文章主要介紹了java的Jackson將json字符串轉(zhuǎn)換成泛型List ,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。2017-02-02