springMvc+nginx文件鉴权(校验权限)服务器

目录

一、环境

二、需求

三、原来访问文件的方式

四、使用nginx+springmvc做文件鉴权服务器

五、代码实现

        1、访问系统路径时跳转到nginx

        2、配置nginx

        3、系统鉴权方法

六、其他问题


一、环境

        springmvc开发的javaweb系统。

二、需求

        需要鉴权后才能访问上传的文件(指定文件夹中的文件)。

三、原来访问文件的方式

        1、使用tomcat做文件服务器,通过tomcat的server.xml配置文件的访问路径。

        springMvc+nginx文件鉴权(校验权限)服务器_第1张图片

        2、特点:不需要鉴权就能访问文件

        3、访问路径:http:localhost:8080/download/test.png

        注意:http:localhost:8080为系统访问路径,我这里的文件服务器和系统服务器用的同一个tomcat

四、使用nginx+springmvc做文件鉴权服务器

        1、为了不改变系统原有的功能,访问路径不变:http:localhost:8080/download/test.png

        2、问题:在访问路径不变的情况下需要访问nginx,鉴权的功能需要交给系统后台校验,校验完成后发送给nginx做文件的访问。

        3、对应问题可以知道文件的整个访问过程猜想:

                ①:访问系统路径时跳转到nginx

                ②:nginx跳转到系统进行鉴权

                ③:系统鉴权完成后跳转到nginx

                ④:nginx访问文件

五、代码实现

        1、访问系统路径时跳转到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() {

	}
}

        2、配置nginx

                ①:配置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;
        }
    }
}

        3、系统鉴权方法

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配置文件中添加,具体在哪里添加和原因自行百度一下springMvc+nginx文件鉴权(校验权限)服务器_第2张图片

  
  
        default
        *.png
    

到这里就全部完成了,代码是原有的项目修改后贴出来的,表达能力不太好,有些地方写得不好,大家将就着看吧。配置或者代码有比较好优化可以交流交流

springMvc+nginx文件鉴权(校验权限)服务器_第3张图片

你可能感兴趣的:(java+nginx,spring,nginx,java)