目录
一、环境
二、需求
三、原来访问文件的方式
四、使用nginx+springmvc做文件鉴权服务器
五、代码实现
1、访问系统路径时跳转到nginx
2、配置nginx
3、系统鉴权方法
六、其他问题
springmvc开发的javaweb系统。
需要鉴权后才能访问上传的文件(指定文件夹中的文件)。
1、使用tomcat做文件服务器,通过tomcat的server.xml配置文件的访问路径。
2、特点:不需要鉴权就能访问文件
3、访问路径:http:localhost:8080/download/test.png
注意:http:localhost:8080为系统访问路径,我这里的文件服务器和系统服务器用的同一个tomcat
1、为了不改变系统原有的功能,访问路径不变:http:localhost:8080/download/test.png
2、问题:在访问路径不变的情况下需要访问nginx,鉴权的功能需要交给系统后台校验,校验完成后发送给nginx做文件的访问。
3、对应问题可以知道文件的整个访问过程猜想:
①:访问系统路径时跳转到nginx
②:nginx跳转到系统进行鉴权
③:系统鉴权完成后跳转到nginx
④:nginx访问文件
①:通过spring的过滤器,过滤包含/download/的路径。在web.xml添加过滤器
downloadFileFilter
cn.o.DownloadFileFilter
downloadFileFilter
/download/*
②:DownloadFileFilter代码
package cn.o;
import cn.o.account.LoginAccount;
import cn.o.utils.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class DownloadFileFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取ip,应对使用ip或者域名进行访问
URI uri = URI.create(request.getRequestURL().toString());
String ip = null ;
try {
ip = (new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null)).getHost();
} catch (URISyntaxException e) {
e.printStackTrace();
}
if(StringUtils.isNotBlank(ip)){
//文件nginx服务器端口
String fileNginxPort = "8081";
//我这里项目登录会把账号信息存储到session中
//这里先简单校验是否有账号信息,后面会具体校验账号有效性
LoginAccount loginAccount = (LoginAccount) request.getSession().getAttribute("login_account");
if(loginAccount != null){
String url = request.getRequestURL().toString();
if(url.indexOf("/download/") >= 0){
String redirectUrl = "http://"+ip+":"+fileNginxPort+"/"+url.substring(url.indexOf("/download"),url.length());
//重定向跳转到nginx服务器
response.sendRedirect(redirectUrl);
}
}
}
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
①:配置nginx.conf,注意:每个server的server_name和proxy_pass的ip要一致。
②:rewrite 配置的就是需要跳转的鉴权方法地址。
③:alias 配置的是鉴权成功后文件的物理地址。
到这一步,我们上面猜想的②、③、④就由nginx帮我们实现交互了,接下来就是鉴权代码的实现了。
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# 这个是内网ip的server, 如果有多个ip映射访问,就配置多个server
server {
# nginx服务器端口
listen 8081;
server_name 192.168.1.1;
location / {
root html;
index index.html index.htm;
}
# 文件服务
location ^~ /download {
# 内部请求(即一次请求的Nginx内部请求),禁止外部访问,重要。
internal;
# 文件的物理路径
alias E:/demo/xinfengxian/download/;
limit_rate 200k;
# 浏览器访问返回200,然后转由后台处理
error_page 404 =200 @backend;
}
# 文件鉴权
location @backend {
# 去掉访问路径中的 /download/,然后定义需要跳转的系统鉴权方法。
rewrite ^/download/(.*)$ /authentication/loadFile/$1 break;
# 这里是系统服务器地址
proxy_pass http://192.168.1.1:8080;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
# 这个是本地ip的server
server {
listen 8081;
server_name 127.0.0.1;
location / {
root html;
index index.html index.htm;
}
# 文件下载服务
location ^~ /download {
# 内部请求(即一次请求的Nginx内部请求),禁止外部访问,重要。
internal;
# 文件的物理路径
alias E:/demo/xinfengxian/download/;
limit_rate 200k;
# 浏览器访问返回200,然后转由后台处理
error_page 404 =200 @backend;
}
# 文件下载鉴权
location @backend {
# 去掉访问路径中的 /download/,然后定义需要跳转的系统鉴权方法。
rewrite ^/download/(.*)$ /attachPermissions/downAttach/$1 break;
# 这里是系统服务器地址
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
# 这个是域名的server
server {
listen 8081;
server_name mytest.test.com;
location / {
root html;
index index.html index.htm;
}
# 文件下载服务
location ^~ /download {
# 内部请求(即一次请求的Nginx内部请求),禁止外部访问,重要。
internal;
# 文件的物理路径
alias E:/demo/xinfengxian/download/;
limit_rate 200k;
# 浏览器访问返回200,然后转由后台处理
error_page 404 =200 @backend;
}
# 文件下载鉴权
location @backend {
# 去掉访问路径中的 /download/,然后定义需要跳转的系统鉴权方法。
rewrite ^/download/(.*)$ /attachPermissions/downAttach/$1 break;
# 这里是系统服务器地址
proxy_pass http://mytest.test.com:8080;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
package cn.o.authentication;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
@RequestMapping("/authentication")
@Scope("prototype")
public class AttachPermissionsController {
@RequestMapping(value = "/loadFile/**/{fileName:.+}",method = RequestMethod.GET)
public void loadFile(HttpServletRequest request, HttpServletResponse response,@PathVariable String fileName) throws Exception{
LoginAccount loginAccount = (LoginAccount) request.getSession().getAttribute("login_account");
String loginToken = "";// 登录的token
String loginName = "";// 登录的用户名
boolean flag = false;
if(loginAccount != null){
loginName = loginAccount.getName();
//根据用户名获取token,我这里是将登录的token存放到session缓存中,适用于简单的校验,最好还是使用redis等缓存机制
loginToken = (String) request.getSession().getAttribute(loginName+"_login_token");
if(StringUtils.isNotBlank(loginToken)){
//我这里使用的是JWTUtil工具类生成token的,所以我这里是用JWTUtil工具类校验token的有效性的
//这里就不贴具体校验方法了,
String pass = "通过";
if ("通过".equals(pass)) {// 如果通过,说明此token还有效,否则视为无效
flag = true;
} else {
flag = false;
}
}else{
flag = false;
}
}else{
flag = false;
}
//如果上面的鉴权通过,则允许文件访问
if(flag){
String requestUri = request.getRequestURI();
requestUri = requestUri.substring(requestUri.indexOf("/")+1);
requestUri = requestUri.substring(requestUri.indexOf("/")+1);
requestUri = requestUri.substring(requestUri.indexOf("/")+1);
// 已被授权访问
// 文件下载
response.setHeader("Content-Disposition", "attachment; filename=\"" + new String(fileName.getBytes("GBK"), "iso-8859-1") + "\"");
// 文件以二进制流传输
response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
// 返回真实文件路径交由 Nginx 处理,保证前端无法看到真实的文件路径。
// 这里的 "/download" 为 Nginx 中配置的下载服务名
response.setHeader("X-Accel-Redirect", "/download/"+requestUri);
// 限速,单位字节,默认不限
// response.setHeader("X-Accel-Limit-Rate","1024");
// 是否使用Nginx缓存,默认yes
// response.setHeader("X-Accel-Buffering","yes");
response.setHeader("X-Accel-Charset", "utf-8");
// 禁止浏览器缓存
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "No-cache");
response.setHeader("Expires", "0");
}else{
response.setStatus(404);
return;
}
}
}
还有个大家要注意的,如果同时使用tomcat和nginx配置文件服务器,会只进入到tomcat中,所以如果要使用nginx鉴权,要将tomcat的文件配置删除。
在开发的过程中,web.xml原本配置了不同类型的静态资源,导致了使用nginx服务器后访问不了配置了静态资源的文件。
在网上找了解决方法:将需要通过nginx服务器访问的类型的静态资源配置删掉,并且在spring配置文件中添加
default
*.png
到这里就全部完成了,代码是原有的项目修改后贴出来的,表达能力不太好,有些地方写得不好,大家将就着看吧。配置或者代码有比较好优化可以交流交流