1. 描述
使用防盗链,来预防报表工程里的报表被直接访问或者被其他网站链接访问。
注1:FineReport 11.0.12 版本新增 Referer 校验功能,可参考 Referer校验 进行配置,操作简单,无需处理复杂代码。
注2:需先将 webroot 目录部署在外置 Tomcat 下才可生效(内置的设计器的 Tomcat 现在不读 web.xml 了)。
注3:移动端访问也能起到防盗链作用。
2. 原理
浏览器中直接输入报表 URL 的时候,它的头文件是空的,因此,可以在访问的时候做两个判断:头文件是否为空以及以什么页面进行跳转,如果不符合跳到错误页面即可。
1)什么是 Referer?
这里的 Referer 指的是 HTTP 头部的一个字段,也称为 HTTP 来源地址(HTTP Referer),用来表示从哪儿链接到目前的网页,采用的格式是 URL。换句话说,借着 HTTP Referer 头部网页可以检查访客从哪里而来,这也常被用来对付伪造的跨网站请求。
2)什么是空 Referer,什么时候会出现空 Referer?
首先,我们对空 Referer 的定义为,Referer 头部的内容为空,或者,一个 HTTP 请求中根本不包含 Referer 头部。
那么什么时候 HTTP 请求会不包含 Referer 字段呢?根据 Referer 的定义,它的作用是指示一个请求是从哪里链接过来,那么当一个请求并不是由链接触发产生的,那么自然也就不需要指定这个请求的链接来源。
比如,直接在浏览器的地址栏中输入一个资源的 URL 地址,那么这种请求是不会包含 Referer 字段的,因为这是一个“凭空产生”的 HTTP 请求,并不是从一个地方链接过去的。
那么在防盗链设置中,允许空 Referer 和不允许空 Referer 有什么区别?
在防盗链中,如果允许包含空的 Referer,那么通过浏览器地址栏直接访问该资源 URL 是可以访问到的;
但如果不允许包含空的 Referer,那么通过浏览器直接访问也是被禁止的。
3. 操作步骤
3.1 添加 class 文件
编写一个类文件,用来判断头文件是否为空,代码如下:
package com.fr.test;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class Dodo implements Filter {
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String referer = req.getHeader("referer");
//下面的IP地址是正常页面请求
if(null != referer && (referer.trim().startsWith("http://localhost:8080")||referer.trim().startsWith("http://dev.fanruan.com/detail.html"))){
System.out.println("正常页面请求"+referer);
chain.doFilter(req, resp);
//下面的就是出现不是正常页面请求的时候跳转
}else{
System.out.println("盗链"+referer);
req.getRequestDispatcher("/LdapLogin.jsp").forward(req, resp);
}
}
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
注:条件语句说明如下
null != referer 表示 referer 不为空。
referer.trim().startsWith("http://localhost:8080") 指被访问的服务器地址,这句判断一定要有,因为正常访问状态下允许访问的链接跳转过来访问该链接开头的地址时,有两条申请,一个就是对该服务器的申请,如下图所示。此句表示允许访问以 http://localhost:8080 开头的链接。
referer.trim().startsWith("http://dev.fanruan.com/detail.html") 此处网址 IP 或端口号不能与被访问的服务器地址相同,表示允许该链接跳转到以 localhost:8080 开头的链接地址来进行访问。
将 Dodo.java 编译成 class 文件,并放在%TOMCAT_HOME%\webroot\WEB-INF\classes\com\fr\test目录下。
3.2 修改 web.xml 文件
打开%TOMCAT_HOME%\webapps\webroot\WEB-INF下新建 web.xml 文件,配置一个过滤 filter,在出现 decision 的时候执行过滤,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Template WebApp</display-name>
<mime-mapping>
<extension>msi</extension>
<mime-type>application/x-msi</mime-type>
</mime-mapping>
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>com.fr.test.Dodo</filter-class>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/decision/*</url-pattern>
</filter-mapping>
</web-app>
两步就可以搞定了,如果属于盗链,则跳转至上述的 LDAPLogin 错误页面,这里没有 LDAPLoign 页面,所以直接跳转 404。如果还想实现数据权限,则可以通过单点登录或者 Session 注入的方式。
4. 效果测试
准备两个 HTML 文件,放在外网 http://dev.fanruan.com/ 服务器文件夹下。
注:在 IE 浏览器下,自带超链 FR.doHyperlinkByGet() 和 window.open() 方法跳转的链接没有 referer 属性,会造成了防盗链失败,可以使用表单 post 提交或者 a 标签来完成跳转。
假设 http://dev.fanruan.com/detail.html 是正确的网址
<html>
<body>
<a href='http://localhost:8080/webroot/decision/view/report?viewlet=test.cpt'>防盗链测试,正确的链接地址</p>地址:http://localhost:8080/webroot/decision/view/report?viewlet=test.cpt</a>
</body>
<html>
假设 http://dev.fanruan.com 是盗链的网址
<html>
<body><a href='http://localhost:8080/webroot/decision/view/report?viewlet=test.cpt'>防盗链测试,错误的链接地址</p>地址:http://localhost:8080/webroot/decision/view/report?viewlet=test.cpt</a>
</body>
</html>
4.1 情况一
通过 http://dev.fanruan.com/detail.html 跳转,跳转链接正确,即 referer 不为空且正确
4.2 情况二
通过 http://dev.fanruan.com 跳转,跳转链接错误,即 referer 不为空且错误
4.3 情况三
直接访问 URL 地址,即 referer 为空
5. 注意事项
在 IE 浏览器下,自带超链 FR.doHyperlinkByGet() 和 window.open() 方法跳转的链接没有 referer 属性,会造成了防盗链失败,可以使用表单 post 提交或者 a 标签来完成跳转。
上述是以 a 标签跳转链接为例,准备的两个 html 文件,放在外网 www.finereporthelp.com 服务器文件夹下的。
这里提供 post 提交方法,两个 HTML 文件内容为:
<html>
<head>
<title>FineReport Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<script type="text/javascript">
function post(URL, PARAMS,target) { var temp_form = document.createElement("form");
temp_form .action = URL;
temp_form .target = target;
temp_form .method = "post";
temp_form .style.display = "none"; for (var x in PARAMS) { var opt = document.createElement("textarea");
opt.name = x;
opt.value = PARAMS[x];
temp_form .appendChild(opt);
}
document.body.appendChild(temp_form);
temp_form .submit();
}
</script>
</head>
<body>
<p>测试</p>
<input type="button" name="show" value="查询" onclick="post('/webroot/decision/view/report',{reportlet:'test.cpt',a:'111'},'_blank')"/>
</body>
</html>
同样可以实现防盗链的效果。