背景:
从oss下载文件需要做鉴权,直接从oss获取文件流,再返回前端性能不行,所以要实现请求转发,并且能够在转发前进行鉴权,调整路径等操作,使用了smiley-http-proxy-servlet,废话不多说,直接开始:
maven依赖
添加maven依赖:
<dependency>
<groupId>org.mitre.dsmiley.httpproxy</groupId>
<artifactId>smiley-http-proxy-servlet</artifactId>
<version>1.11</version>
</dependency>
代理
编写自定义代理,重写代理转发方法:如果不需要修改转发的逻辑,比如调整路径等,则直接在下面注册代理bean使用ProxyServlet即可,因为我自身平台的请求格式和oss请求格式不匹配,所以需要自定义代理,进行转发路径调整:
package com.caih.ep.cms.config;
import com.cms.attachment.entity.AttachmentDetailResVO;
import com.cms.attachment.manage.CmsAttachmentMng;
import com.common.file.GetByIdsReqVO;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.mitre.dsmiley.httpproxy.ProxyServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* @Description
* @Author Zephyr
* @Date 2024/11/7
**/
@Slf4j
@AllArgsConstructor
public class AttachmentProxyServlet extends ProxyServlet {
//这里不能直接注入,需要在注册代理的时候通过构造器传进来
private final CmsAttachmentMng cmsAttachmentMng;
/**
* @description: 重写代理请求地址 /ep/cms/attachment/{fileId}/{fileName}
* ——> /ep/cms/attachment/ossKey?response-content-disposition=attachment; filename=fileName
* @date: 2024/11/8
* @param:
* @param: servletRequest
* @return: String
**/
@SneakyThrows
@Override
protected String rewritePathInfoFromRequest(HttpServletRequest servletRequest) {
String pathInfo = servletRequest.getPathInfo();
//提取文件id
String fileId = pathInfo.substring(1, pathInfo.lastIndexOf("/"));
// 提取文件名
String fileName = pathInfo.substring(pathInfo.lastIndexOf("/") + 1);
//查询附件详情,获取ossKey
AttachmentDetailResVO attachmentDetailResVO = cmsAttachmentMng.getDetailByFileId(fileId);
String ossKey = attachmentDetailResVO.getMongoFileId();
// 编码文件名,防止特殊字符导致的问题
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
// 修改路径,移除 / 后缀,并添加查询参数
String targetPath = "/" + ossKey + "?response-content-disposition=attachment; filename=" + encodedFileName;
// log.info("oss文件请求地址:" + pathInfo + " ——> " + targetPath);
return targetPath;
}
}
注册代理bean:
package com.caih.ep.cms.config;
import com.cms.attachment.manage.CmsAttachmentMng;
import com.google.common.collect.ImmutableMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* @Description
* @Author Zephyr
* @Date 2024/11/7
**/
@Configuration
public class ProxyConfig {
//oss的域名
@Value("${oss.config.domain}")
private String ossDomain;
//业务处理service
@Autowired
private CmsAttachmentMng cmsAttachmentMng;
/**
* @description: 附件下载代理
* @date: 2024/11/8
* @param:
* @return: ServletRegistrationBean
**/
@Bean
public ServletRegistrationBean proxyServletRegistration(){
//需要代理的转发路径前缀
String servletPath = "/ep/cms/attachment/*";
//需要转发到的目标地址
String targetUri = ossDomain;
//这里构造器new 的是一个自己自定义的ProxyServlet,如果不需要在转发的时候做转发逻辑,则这里可以直接new ProxyServlet()即可
ServletRegistrationBean registrationBean= new ServletRegistrationBean(new AttachmentProxyServlet(cmsAttachmentMng), servletPath);
//这个setName有多个的时候,名字需要不一样
registrationBean.setName("attachmentProxy");
//设置网址以及参数
Map<String, String> params = ImmutableMap.of(
"targetUri", targetUri,
"log", "true");
registrationBean.setInitParameters(params);
return registrationBean;
}
}
这里实现了从/ep/cms/attachment/**转发到ossDomain/**:
eg:/ep/cms/attachment/aaa/bbb.pdf ==> ossDomain/aaa/bbb.pdf
到这里转发的代码就写完了,想要在转发前做鉴权怎么做呢?其实在自定义的代理Servlet里面是可以做的,但是一般不推荐,转发代理只处理代理的逻辑。我们这里使用过滤器实现。
注意:不能使用拦截器,因为请求是先到过滤器—>代理—>拦截器 ,请求还未到拦截器就已经转发到文件服务器了
过滤器 鉴权
创建过滤器:
package com.cms.filters;
import com.attachment.manage.CmsAttachmentMng;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import java.io.IOException;
/**
* @Description 文件鉴权过滤器
* @Author Zephyr
* @Date 2024/11/7
**/
@Slf4j
@AllArgsConstructor
public class FileFilter implements Filter {
private final CmsAttachmentMng cmsAttachmentMng;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
//文件鉴权逻辑
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
鉴权逻辑就不展示了,在doFilter方法里面进行鉴权,不通过可以直接抛出异常,直接return
注册过滤器
package com.caih.ep.cms.filters;
import com.cms.attachment.manage.CmsAttachmentMng;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
public class ServerFilterConfig {
@Autowired
private CmsAttachmentMng cmsAttachmentMng;
@Bean
public FilterRegistrationBean authFilterRegistration(){
FilterRegistrationBean registration = new FilterRegistrationBean();
//添加过滤器
registration.setFilter(new FileFilter(cmsAttachmentMng));
//设置过滤路径
registration.addUrlPatterns("/ep/cms/attachment/*");
registration.setName("fileFilter");
//设置优先级
registration.setOrder(0);
return registration;
}
}
至此,大功告成!