FastDFS分布式文件系统之防盗链权限控制
问题所在
FastDFS采用的是tracker和storage的模式。
tracker负责负载均衡
storage负责文件存储、文件同步、提供文件访问接口(也就是说用户要从storage直接获取文件)
其实,storage的http端口是开着的,外界只要知道端口和文件路径信息就可以直接访问到文件。这种操作,对于文件而言是不安全的。
解决方式
在storage上配置访问规则,打开防盗链功能,首先要确保不能直接访问到文件。其次,限制访问文件时必须带有一个本地应用服务器生成的token,这个token需要有文件id、自己设置的密钥、时间戳三个部分组成。但是只有本地应用服务器保存密钥是不可行的,需要把密钥同时保存到storage上,用于token的解密验证工作。
配置
storage
进入etc/fdfs,编辑http.conf文件
编辑后的内容
Java
resources下新建fdfs_cilent.conf文件
#默认值为30s
connect_timeout = 10
#默认值为30s
network_timeout = 30
charset = UTF-8
#tracker的http端口
http.tracker_http_port = 8888
#服务器
tracker_server = 192.168.1.106:22122
#开启防盗链,防盗链密钥
http.anti_steal_token = true
http.secret_key= 密钥
主要的工具类文件
/**
* 上传下载等文件操作工具类
*/
public class FastDFSClient {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);
/***
* 初始化加载FastDFS的TrackerServer配置
*/
static {
try {
String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
ClientGlobal.init(filePath);
} catch (Exception e) {
logger.error("FastDFS Client Init Fail!",e);
}
}
public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, MyException {
String fileName = "M00/00/00/wKgBa2U4dPSAO7fXAAC5PC_5iKw01.docx";
String groupName = "group1";
String sourceUrl = getSourceUrl(groupName,fileName);
System.out.println(sourceUrl);
}
public static FastDFSFile getFastDFSFile(MultipartFile file,String author) {
InputStream inputStream = null;
//获取文件名称(带有后缀)
String allFileName = file.getOriginalFilename();
//获取文件扩展名
String ext = "";
byte[] content = null;
if (allFileName != null && !"".equals(allFileName)) {
ext = (allFileName.substring(allFileName.lastIndexOf("."))).replaceAll("\\.","");
}
//文件名称(不带后缀)
String fileName = allFileName.replaceAll("." + ext, "");
try {
inputStream = file.getInputStream();
//获取文件所有的字节内容
content = inputStream.readAllBytes();
//一定要关闭输入流,不然会报错
inputStream.close();
} catch (IOException e) {
throw new CustomException("文件上传错误", ResultCode.UPLOAD_ERROR);
}
//创建文件实体类对象
FastDFSFile fastDFSFile = new FastDFSFile();
fastDFSFile.setContent(content);
fastDFSFile.setSize(content.length);
fastDFSFile.setName(fileName);
fastDFSFile.setExt(ext);
fastDFSFile.setAuthor(author);
return fastDFSFile;
}
/***
* 文件上传
* @param file
* @return
*/
public static String[] upload(FastDFSFile file) {
//获取文件基础信息,有多少长度就要是多少,不然会报空指针
NameValuePair[] meta_list = new NameValuePair[4];
meta_list[0] = new NameValuePair("author", file.getAuthor());
meta_list[1] = new NameValuePair("name",file.getName());
meta_list[2] = new NameValuePair("ext",file.getExt());
meta_list[3] = new NameValuePair("size", file.getSize()+"");
//接收返回数据
String[] uploadResults = null;
StorageClient storageClient=null;
try {
//创建StorageClient客户端对象
storageClient = getTrackerClient();
/***
* 文件上传
* 1)文件字节数组
* 2)文件扩展名
* 3)文件作者
*/
uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
} catch (Exception e) {
logger.error("Exception when uploadind the file:" + file.getName(), e);
}
if (uploadResults == null && storageClient!=null) {
logger.error("upload file fail, error code:" + storageClient.getErrorCode());
}
//获取组名
String groupName = uploadResults[0];
//获取文件存储路径
String remoteFileName = uploadResults[1];
return uploadResults;
}
/***
* 获取文件信息
* @param groupName:组名
* @param remoteFileName:文件存储完整名
* @return
*/
public static FileInfo getFile(String groupName, String remoteFileName) {
try {
StorageClient storageClient = getTrackerClient();
return storageClient.get_file_info(groupName, remoteFileName);
} catch (Exception e) {
logger.error("Exception: Get File from Fast DFS failed", e);
}
return null;
}
/***
* 文件下载
* @param groupName
* @param remoteFileName
* @return
*/
public static InputStream downFile(String groupName, String remoteFileName) {
try {
//创建StorageClient
StorageClient storageClient = getTrackerClient();
//下载文件
byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
InputStream ins = new ByteArrayInputStream(fileByte);
return ins;
} catch (Exception e) {
logger.error("Exception: Get File from Fast DFS failed", e);
}
return null;
}
/***
* 文件删除
* @param groupName
* @param remoteFileName
* @throws Exception
*/
public static void deleteFile(String groupName, String remoteFileName)
throws Exception {
//创建StorageClient
StorageClient storageClient = getTrackerClient();
//删除文件
int i = storageClient.delete_file(groupName, remoteFileName);
}
/***
* 获取Storage客户端
* @return
* @throws IOException
*/
private static StorageClient getTrackerClient() throws IOException {
TrackerServer trackerServer = getTrackerServer();
StorageClient storageClient = new StorageClient(trackerServer, null);
return storageClient;
}
/***
* 获取Tracker
* @return
* @throws IOException
*/
private static TrackerServer getTrackerServer() throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerServer;
}
/**
* 生成防盗链token
* @param groupName 文件组名称,group1
* @param fileName 文件路径,不带group:M00/00/00/wKg4C1tFmTWAFPKBAADdeFFxlXA240.png
* @return
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
* @throws MyException
*/
public static String getSourceUrl(String groupName,String fileName) throws UnsupportedEncodingException, NoSuchAlgorithmException, MyException {
//时间戳
int lts = (int)(System.currentTimeMillis() / 1000);
System.out.println(ClientGlobal.getG_secret_key());
System.out.println("ip:"+FastDFSClient.getFile(groupName, fileName).getSourceIpAddr());
//ip自动获取
String ip = FastDFSClient.getFile(groupName, fileName).getSourceIpAddr();
String token = ProtoCommon.getToken(fileName, lts, ClientGlobal.getG_secret_key()); //初始化secret_key
//生成防盗链的文件访问url
return "http://" +ip+ ":8888/" +groupName+"/" + fileName + "?token=" + token + "&ts=" + lts;
}
/***
* 获取Storage组
* @param groupName
* @return
* @throws IOException
*/
public static StorageServer[] getStoreStorages(String groupName)
throws IOException {
//创建TrackerClient
TrackerClient trackerClient = new TrackerClient();
//获取TrackerServer
TrackerServer trackerServer = trackerClient.getConnection();
//获取Storage组
return trackerClient.getStoreStorages(trackerServer, groupName);
}
/***
* 获取Storage信息,IP和端口
* @param groupName
* @param remoteFileName
* @return
* @throws IOException
*/
public static ServerInfo[] getFetchStorages(String groupName,String remoteFileName) throws IOException {
TrackerClient trackerClient = new TrackerClient();
TrackerServer trackerServer = trackerClient.getConnection();
return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
}
/***
* 获取Tracker服务地址
* @return
* @throws IOException
*/
public static String getTrackerUrl() throws IOException {
return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ ClientGlobal.getG_tracker_http_port()+"/";
}
}
附属的文件类
/**
* 文件实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FastDFSFile {
//文件名字
private String name;
//文件内容
private byte[] content;
//文件扩展名
private String ext;
//文件MD5摘要值
private String md5;
//文件创建作者
private String author;
//文件大小
private Integer size;
public FastDFSFile(String name, byte[] content, String ext) {
super();
this.name = name;
this.content = content;
this.ext = ext;
}
}