使用 Springboot + Nginx 的 http_secure_link_module 實(shí)現(xiàn)圖片防盜鏈在 Vue 展示
使用 Springboot + Nginx 的 http_secure_link_module 實(shí)現(xiàn)圖片防盜鏈并在 Vue 中展示功能
在當(dāng)今互聯(lián)網(wǎng)環(huán)境中,圖片資源的安全性和有效管理至關(guān)重要。為了防止未經(jīng)授權(quán)的訪問(wèn)和濫用圖片資源,我們采用 Nginx 的 http_secure_link_module 模塊來(lái)實(shí)現(xiàn)圖片防盜鏈功能。這不僅能保護(hù)圖片的合法使用,還能有效控制資源的訪問(wèn)權(quán)限,提升系統(tǒng)的安全性和穩(wěn)定性。
在本次配置中,我們對(duì) secure_link 模塊進(jìn)行了定制化的設(shè)置,以滿足特定的業(yè)務(wù)需求。通過(guò)引入 $remote_addr 變量,我們能夠更精確地控制訪問(wèn)來(lái)源,進(jìn)一步增強(qiáng)了防盜鏈的效果。同時(shí),對(duì)密鑰的配置和 token 、過(guò)期時(shí)間的生成算法進(jìn)行優(yōu)化,確保了只有在合法條件下才能訪問(wèn)圖片資源。
Nginx 配置(nginx.conf)
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
# 啟用 secure_link 模塊
server {
listen 80;
server_name your_domain;
# 私有密鑰配置
secure_link_secret your_private_secret; # 此處的密鑰是用于生成和驗(yàn)證 token 的關(guān)鍵元素,需要妥善保管和保密。它參與了 MD5 哈希計(jì)算,確保 token 的唯一性和安全性。
location /images {
secure_link $arg_token,$arg_expires,$remote_addr;
secure_link_md5 "$secure_link_secret$uri$arg_expires$remote_addr"; # 這個(gè)表達(dá)式詳細(xì)說(shuō)明了如何根據(jù)密鑰、圖片的 URI、過(guò)期時(shí)間和客戶端的 IP 地址生成 MD5 哈希值,用于驗(yàn)證請(qǐng)求的合法性。
if ($secure_link = "") { # 如果 token 為空,直接拒絕訪問(wèn),返回 403 禁止訪問(wèn)狀態(tài)碼。
return 403;
}
if ($secure_link = "0") { # 如果 token 驗(yàn)證失敗,返回 410 資源已不存在的狀態(tài)碼。
return 410;
}
root /path/to/images; # 指定圖片的實(shí)際存儲(chǔ)路徑,確保 Nginx 能夠正確找到并提供服務(wù)。
}
}
}
Pom.xml 依賴配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>image-security</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Yaml 配置文件(application.yml)
image:
domain: http://your_domain
nginx:
secure-link-secret: your_secure_link_secret
ImageController 類(ImageController.java)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.yaml.snakeyaml.Yaml;
import java.io.InputStream;
import java.util.Map;
@Controller
public class ImageController {
@Autowired
private Environment environment;
public String generateImageUrl(String imageName) {
String imageDomain = environment.getProperty("image.domain");
// 生成過(guò)期時(shí)間(假設(shè) 1 小時(shí)后過(guò)期)
LocalDateTime expirationTime = LocalDateTime.now().plusHours(1);
long expiresInSeconds = expirationTime.toEpochSecond(ZoneOffset.UTC);
// 生成 token
String token = generateToken(imageName, expiresInSeconds);
return imageDomain + "/images/" + imageName + "?token=" + token + "&expires=" + expiresInSeconds;
}
private String generateToken(String uri, long expires) {
String secret = environment.getProperty("nginx.secure-link-secret");
String data = uri + expires + secret;
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] hash = digest.digest(data.getBytes());
return Base64.getUrlEncoder().encodeToString(hash);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
Spring Boot 控制器生成圖片列表
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/image")
public class ImageController {
@GetMapping("/list")
public List<String> getImageList() {
List<String> imageUrls = new ArrayList<>();
imageUrls.add(generateImageUrl("image1.jpg"));
imageUrls.add(generateImageUrl("image2.jpg"));
// 此處根據(jù)實(shí)際邏輯生成帶有 token 和 expires 參數(shù)的圖片鏈接
return imageUrls;
}
}
Vue 中展示圖片列表
<template>
<div>
<ul>
<li v-for="imageUrl in imageList" :key="imageUrl">
<img :src="imageUrl" />
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
imageList: []
};
},
mounted() {
// 發(fā)送請(qǐng)求獲取圖片列表
this.$http.get('/image/list')
.then(response => {
const imageDomain = '${process.env.VUE_APP_IMAGE_DOMAIN}';
this.imageList = response.data.map(url => imageDomain + url);
})
.catch(error => {
console.error('獲取圖片列表失敗', error);
});
}
};
</script>
總結(jié)
本次方案通過(guò)引入 Nginx 的 http_secure_link_module 模塊實(shí)現(xiàn)了圖片防盜鏈功能,增強(qiáng)了圖片資源的安全性。在配置方面,我們使用 Yaml 文件來(lái)管理關(guān)鍵配置信息,包括圖片域名和 Nginx 的安全鏈接密鑰。通過(guò)在 ImageController 類中讀取這些配置,生成帶有令牌和過(guò)期時(shí)間的圖片 URL。在 Vue 端,我們根據(jù)配置的域名來(lái)完整地構(gòu)建圖片的訪問(wèn)地址。整個(gè)方案具有良好的靈活性和可擴(kuò)展性,能夠根據(jù)實(shí)際業(yè)務(wù)需求進(jìn)行調(diào)整和優(yōu)化,有效保護(hù)圖片資源的合法訪問(wèn)和使用。