mirror of
https://gitee.com/kekingcn/file-online-preview.git
synced 2025-08-20 09:26:40 +08:00
处理fastdfs文件下载token认证问题
This commit is contained in:
parent
52ed462632
commit
3d84a79df8
63
server/src/main/java/cn/keking/service/TokenService.java
Normal file
63
server/src/main/java/cn/keking/service/TokenService.java
Normal file
@ -0,0 +1,63 @@
|
||||
package cn.keking.service;
|
||||
|
||||
import cn.keking.service.cache.impl.CacheServiceRedisImpl;
|
||||
import cn.keking.service.impl.OtherFilePreviewImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 处理 token
|
||||
*/
|
||||
@Component
|
||||
public class TokenService {
|
||||
|
||||
public static final String TOKEN_KEY = "accessToken_cache";
|
||||
|
||||
@Autowired
|
||||
private OtherFilePreviewImpl otherFilePreview;
|
||||
|
||||
@Autowired
|
||||
private CacheServiceRedisImpl cacheServiceRedisImpl;
|
||||
|
||||
/**
|
||||
* 截取带有token的url,并刷新token,返回url
|
||||
* @param url
|
||||
* @param model
|
||||
* @return
|
||||
*/
|
||||
public String extractTokenFromUrl(String url, Model model, HttpServletRequest req) {
|
||||
|
||||
// 例如:http://172.168.27.251:26066/fastdfs/service/fastdfs/download?fileUrl=group1/M00/01/E9/rKg_r2ZO-x-ppWtKACHGUMnMM9U42.docx&fullfilename=group1/M00/01/E9/rKg_r2ZO-x-ppWtKACHGUMnMM9U42.docx
|
||||
|
||||
String[] urlParts = url.split("&accessToken=");
|
||||
if (urlParts.length < 2) {
|
||||
return otherFilePreview.notSupportedFile(model, "非法路径,不允许访问, “&accessToken=” 需拼接到url参数后,例如:http://172.168.27.251:26066/fastdfs/service/fastdfs/download?fileUrl=test.docx&fullfilename=test.docx&accessToken=xxxx");
|
||||
}
|
||||
String accessToken = urlParts[1];
|
||||
if (!StringUtils.hasText(accessToken)) {
|
||||
return otherFilePreview.notSupportedFile(model, "非法路径,不允许访问,请指定accessToken");
|
||||
}
|
||||
refreshToken(accessToken);
|
||||
return urlParts[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取刷新后的token
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
* String
|
||||
* @Date 2025年2月14日11:31:58
|
||||
* @author wangshanzhen2
|
||||
*/
|
||||
public String refreshToken(String token) {
|
||||
// 将accessToken存储到redis,如果不同则更新redis的accessToken
|
||||
return cacheServiceRedisImpl.getAccessTokenCacheAndSetValue(TOKEN_KEY, token);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -9,6 +9,7 @@ import java.util.Map;
|
||||
*/
|
||||
public interface CacheService {
|
||||
|
||||
String FILE_DOWNLOAD_TOKEN_KEY = "fastdfs-file-download-accessToken";
|
||||
String FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file";
|
||||
String FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合
|
||||
String FILE_PREVIEW_PDF_IMGS_KEY = "converted-preview-pdfimgs-file";
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cn.keking.service.cache.impl;
|
||||
|
||||
import cn.keking.service.cache.CacheService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RBlockingQueue;
|
||||
import org.redisson.api.RMapCache;
|
||||
@ -70,7 +71,11 @@ public class CacheServiceRedisImpl implements CacheService {
|
||||
@Override
|
||||
public List<String> getImgCache(String key) {
|
||||
RMapCache<String, List<String>> convertedList = redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
|
||||
return convertedList.get(key);
|
||||
if (key != null) {
|
||||
// 只在 key 非 null 时进行操作
|
||||
return convertedList.get(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -141,4 +146,31 @@ public class CacheServiceRedisImpl implements CacheService {
|
||||
RMapCache<String, Integer> mediaConvertCache = redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
|
||||
mediaConvertCache.clear();
|
||||
}
|
||||
|
||||
|
||||
public String getAccessTokenCacheAndSetValue(String key, String defaultValue) {
|
||||
// redis 缓存中获取accessToken,如果不存在则设置为默认值,并返回默认值
|
||||
RMapCache<String,String> convertedList = redissonClient.getMapCache(FILE_DOWNLOAD_TOKEN_KEY);
|
||||
|
||||
if(StringUtils.isNoneBlank(key)){
|
||||
String token = convertedList.get(key);
|
||||
|
||||
if(StringUtils.isBlank(token) && StringUtils.isNoneBlank(defaultValue)) {
|
||||
convertedList.fastPut(key, defaultValue);
|
||||
}else if (StringUtils.isNoneBlank(token)
|
||||
&& StringUtils.isNoneBlank(defaultValue)
|
||||
&& !token.equals(defaultValue)) {
|
||||
convertedList.fastPut(key, defaultValue);
|
||||
}else {
|
||||
return token;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
throw new IllegalArgumentException("获取token失败. key or defaultValue is blank, please check your input");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
package cn.keking.service.impl;
|
||||
|
||||
import cn.keking.config.SchedulerCleanConfig;
|
||||
import cn.keking.model.FileAttribute;
|
||||
import cn.keking.model.ReturnResponse;
|
||||
import cn.keking.service.FileHandlerService;
|
||||
import cn.keking.service.FilePreview;
|
||||
import cn.keking.utils.DownloadUtils;
|
||||
import cn.keking.utils.KkFileUtils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.ui.Model;
|
||||
@ -20,9 +25,14 @@ import java.util.List;
|
||||
*/
|
||||
@Component("commonPreview")
|
||||
public class CommonPreviewImpl implements FilePreview {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(CommonPreviewImpl.class);
|
||||
private final FileHandlerService fileHandlerService;
|
||||
private final OtherFilePreviewImpl otherFilePreview;
|
||||
/**
|
||||
* 是否从远程下载文件
|
||||
*/
|
||||
@Value("${origin.download:false}")
|
||||
private Boolean isOriginDownload;
|
||||
|
||||
public CommonPreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
|
||||
this.fileHandlerService = fileHandlerService;
|
||||
@ -31,7 +41,9 @@ public class CommonPreviewImpl implements FilePreview {
|
||||
|
||||
@Override
|
||||
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
||||
// 不是http开头,浏览器不能直接访问,需下载到本地
|
||||
|
||||
|
||||
// 不是http开头,浏览器不能直接访问,需下载到本地
|
||||
if (url != null && !url.toLowerCase().startsWith("http")) {
|
||||
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
|
||||
if (response.isFailure()) {
|
||||
@ -42,7 +54,24 @@ public class CommonPreviewImpl implements FilePreview {
|
||||
}
|
||||
} else {
|
||||
model.addAttribute("currentUrl", url);
|
||||
model.addAttribute("isOriginDownload", false);
|
||||
}
|
||||
|
||||
// 是否从远程服务器下载
|
||||
if(isOriginDownload) {
|
||||
model.addAttribute("isOriginDownload", isOriginDownload);
|
||||
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
|
||||
if (response.isFailure()) {
|
||||
//从fastdfs下载到本地
|
||||
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
|
||||
} else {
|
||||
String file = fileHandlerService.getRelativePath(response.getContent());
|
||||
logger.info("img path={}",response.getContent());
|
||||
// 前端通过isOriginDownload 属性区分访问地址,默认 http://127.0.0.1:8012/ + file
|
||||
model.addAttribute("currentUrl", file);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package cn.keking.utils;
|
||||
import cn.keking.config.ConfigConstants;
|
||||
import cn.keking.model.FileAttribute;
|
||||
import cn.keking.model.ReturnResponse;
|
||||
import cn.keking.service.TokenService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.mola.galimatias.GalimatiasParseException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
@ -14,6 +15,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RequestCallback;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@ -23,6 +25,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -42,7 +45,8 @@ public class DownloadUtils {
|
||||
private static final RestTemplate restTemplate = new RestTemplate();
|
||||
private static final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
// 获取TokenService实例对象
|
||||
private static final TokenService tokenService = SpringContextUtils.getBean(TokenService.class);
|
||||
|
||||
/**
|
||||
* @param fileAttribute fileAttribute
|
||||
@ -97,6 +101,21 @@ public class DownloadUtils {
|
||||
factory.setHttpClient(httpClient); //加入重定向方法
|
||||
restTemplate.setRequestFactory(factory);
|
||||
RequestCallback requestCallback = request -> {
|
||||
|
||||
// 设置认证头
|
||||
List<String> authorizationInfo = request.getHeaders().get("Authorization");
|
||||
|
||||
if(CollectionUtils.isEmpty(authorizationInfo)) {
|
||||
// String accessToken = "45d556e7-e0a3-485c-b82f-704f295401b2";
|
||||
String accessToken = tokenService.refreshToken(null);
|
||||
request.getHeaders().set("Authorization", "Bearer " + accessToken);
|
||||
logger.info("设置Authorization = {}",request.getHeaders().get("Authorization"));
|
||||
|
||||
}else {
|
||||
logger.info("Authorization = {}",authorizationInfo);
|
||||
}
|
||||
|
||||
|
||||
request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
|
||||
String proxyAuthorization = fileAttribute.getKkProxyAuthorization();
|
||||
if(StringUtils.hasText(proxyAuthorization)){
|
||||
|
||||
51
server/src/main/java/cn/keking/utils/SpringContextUtils.java
Normal file
51
server/src/main/java/cn/keking/utils/SpringContextUtils.java
Normal file
@ -0,0 +1,51 @@
|
||||
package cn.keking.utils;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
@Component
|
||||
@Lazy(false)
|
||||
public class SpringContextUtils implements ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* 上下文对象实例
|
||||
*/
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
SpringContextUtils.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过class获取Bean
|
||||
*
|
||||
* @param clazz
|
||||
* class
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
return getApplicationContext().getBean(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过name和class获取Bean
|
||||
*
|
||||
* @param name
|
||||
* Bean名称
|
||||
* @param clazz
|
||||
* class
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T getBean(String name, Class<T> clazz) {
|
||||
return getApplicationContext().getBean(name, clazz);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import cn.keking.model.FileAttribute;
|
||||
import cn.keking.service.FileHandlerService;
|
||||
import cn.keking.service.FilePreview;
|
||||
import cn.keking.service.FilePreviewFactory;
|
||||
import cn.keking.service.TokenService;
|
||||
import cn.keking.service.cache.CacheService;
|
||||
import cn.keking.service.impl.OtherFilePreviewImpl;
|
||||
import cn.keking.utils.KkFileUtils;
|
||||
@ -24,6 +25,8 @@ import org.springframework.ui.Model;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.client.RequestCallback;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@ -52,20 +55,25 @@ public class OnlinePreviewController {
|
||||
private final CacheService cacheService;
|
||||
private final FileHandlerService fileHandlerService;
|
||||
private final OtherFilePreviewImpl otherFilePreview;
|
||||
private final TokenService tokenService;
|
||||
private static final RestTemplate restTemplate = new RestTemplate();
|
||||
private static final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
|
||||
private static final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, OtherFilePreviewImpl otherFilePreview) {
|
||||
this.previewFactory = filePreviewFactory;
|
||||
public OnlinePreviewController(TokenService tokenService,FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, OtherFilePreviewImpl otherFilePreview) {
|
||||
this.tokenService = tokenService;
|
||||
this.previewFactory = filePreviewFactory;
|
||||
this.fileHandlerService = fileHandlerService;
|
||||
this.cacheService = cacheService;
|
||||
this.otherFilePreview = otherFilePreview;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping( "/onlinePreview")
|
||||
public String onlinePreview(String url, Model model, HttpServletRequest req) {
|
||||
|
||||
// 使用参考https://kkview.cn/zh-cn/docs/usage.html
|
||||
logger.info("预览URL ==> {}",url);
|
||||
String fileUrl;
|
||||
try {
|
||||
fileUrl = WebUtils.decodeUrl(url);
|
||||
@ -73,6 +81,12 @@ public class OnlinePreviewController {
|
||||
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
|
||||
return otherFilePreview.notSupportedFile(model, errorMsg);
|
||||
}
|
||||
if (!fileUrl.contains("accessToken=")) {
|
||||
return otherFilePreview.notSupportedFile(model, "非法路径,不允许访问, “&accessToken=” 需拼接到url参数后,例如:http://172.168.27.251:26066/fastdfs/service/fastdfs/download?fileUrl=test.docx&fullfilename=test.docx&accessToken=xxxx");
|
||||
}
|
||||
fileUrl = tokenService.extractTokenFromUrl(fileUrl, model, req);
|
||||
|
||||
logger.info("解码后URL ==> {}",fileUrl);
|
||||
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req); //这里不在进行URL 处理了
|
||||
model.addAttribute("file", fileAttribute);
|
||||
FilePreview filePreview = previewFactory.get(fileAttribute);
|
||||
|
||||
@ -19,12 +19,18 @@
|
||||
|
||||
<ul id="image">
|
||||
<#list imgUrls as img>
|
||||
<#if img?contains("http://") || img?contains("https://")>
|
||||
<#assign img="${img}">
|
||||
<#if isOriginDownload>
|
||||
<!-- 如果isOriginDownload为true,则执行这里的代码 -->
|
||||
<li><img id="${currentUrl}" src="${baseUrl}${currentUrl}" style="display: none" alt="Protected Image"></li>
|
||||
<#else>
|
||||
<#assign img="${baseUrl}${img}">
|
||||
<!-- 如果isOriginDownload为false,则执行这里的代码 -->
|
||||
<#if img?contains("http://") || img?contains("https://")>
|
||||
<#assign img="${img}">
|
||||
<#else>
|
||||
<#assign img="${baseUrl}${img}">
|
||||
</#if>
|
||||
<li><img id="${img}" url="${img}" src="${img}" style="display: none"></li>
|
||||
</#if>
|
||||
<li><img id="${img}" url="${img}" src="${img}" style="display: none"></li>
|
||||
</#list>
|
||||
</ul>
|
||||
|
||||
@ -36,6 +42,7 @@
|
||||
backdrop: false,
|
||||
loop : true
|
||||
});
|
||||
// 设置认证token
|
||||
document.getElementById("${currentUrl}").click();
|
||||
|
||||
/*初始化水印*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user