模板权限的控制示例
1. 问题描述
如下图集成报表后,希望不同的用户登录,所能看到的报表是不同的,没有权限的用户查看报表时提示没有权限:
2. 解决方案
通过数字签名验证的方式来验证发送的报表请求,需要带有与FR约定的数字签名方式签名之后的信息,FR验证签名信息合法才允许访问报表,否则返回没有权限。
注:本方案使用 MD5+RSA数字签名方式。
3. 适用情况
前面3个章节的权限控制方式是针对那些比较简单的系统而言的,那么对那些很复杂的系统,使用前面3中权限配置方式就行不通了,这个时候就可以用数字签名的方式进行权限配置。
4. 实现步骤
4.1 前提准备
8075;
如直接使用内置Jetty服务器中的报表工程WebReport,端口为8080;
如用户工程(在PFDemo目录)发布于Tomcat服务器,端口为PFDemo下有文件login.html(用户的登陆界面)、index.jsp(登陆后的主界面)、report.jsp(对报表请求进行签名,将签名信息加入请求中转发给报表工程)。
注:下面有对应文件的完整代码。
如该例我们使用数据集验证,使用服务器数据集存放用户和密码,点击服务器>服务器数据集定义如下:
登陆平台系统http://localhost:8075/WebReport/ReportServer?op=fs,点击管理系统>用户管理>设置,认证方式使用默认的平台内置认证,配置用户同步数据集,如下图:
修改用户登录界面login.html,实现单点登录,代码如下:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
function loginFR() {
var username =document.getElementById("username").value;
var password =document.getElementById("password").value;
var scr = document.createElement("iframe");
scr.src = "http://localhost:8075/WebReport/ReportServer?op=fs_load&cmd=sso&fr_username=" + username + "&__redirect__=false&fr_password=" + password + "&_t=" + (Math.random() * 10000) ;
if (scr.attachEvent){
scr.attachEvent("onload", function(){
var f = document.getElementById("loginForm");
f.submit();
});
} else {
scr.onload = function(){
var f = document.getElementById("loginForm");
f.submit();
};
}
document.getElementsByTagName("head")[0].appendChild(scr);
}
</script>
<body>
<form id="loginForm" name="loginForm" method="post" action="/PFDemo/index.jsp">
<table>
<tbody>
<tr class="prop">
<td class="name"><label>
Username</label></td>
<td class="value"><input id="username" type="text" name="username" value="A" /></td>
</tr>
<tr class="prop">
<td class="name"><label>
Password</label></td>
<td class="value"><input id="password" type="password" name="password" value="" /></td>
</tr>
</tbody>
</table>
</div></div></div>
<div class="actionButtons">
<input id="lalala" type="button" name="lalala" onclick="loginFR();" value ="登录"/>
<input id="sub" type="submit" name="sub" value="" style="display:none;"/>
</div>
</form>
</body>
</html>
单点登录的其他实现实现方式,详细情况请参照单点登录。
4.2 拷贝jar包
下载fr-pfh-java-7.0.jar包,选择权限集成-JAVA资源>fr-pfh-java-7.0.jar,拷贝至报表工程WebReport\WEB-INF\lib及用户系统PFDemo\WEB-INF\lib下(注意:报表工程与用户系统下都要有);
将报表工程WebReport\WEB-INF\lib下的fr-third-7.0.jar包拷贝至用户系统PFDemo\WEB-INF\lib下。
4.3 添加一个web接口给报表工程提供公钥
这个接口是一个地址,比如在用户系统中注册一个servlet,访问这个servlet来获得公钥,http://localhost:8080/PFDemo/DemoServer?cmd=getkey:
在用户系统的PFDemo\WEB-INF\web.xml中添加一个servlet:
<web-app>
<display-name>Demo WebApp</display-name>
<servlet>
<servlet-name>DemoServer</servlet-name>
<servlet-class>demo.DemoServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DemoServer</servlet-name>
<url-pattern>/DemoServer</url-pattern>
</servlet-mapping>
</web-app>
DemoServer这个servlet所对应的类为demo.DemoServlet,代码如下:
package demo;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fr.pf.java.FRPrivilegeFilterHelper;
public class DemoServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
doPost(req, res);
}
/**
* 这里只是示例,就简单处理,不做分层了
*/
public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
String cmd = req.getParameter("cmd");
if ("getkey".equals(cmd)) {
dealWithKey(req, res);
}
}
private void dealWithKey(HttpServletRequest req, HttpServletResponse res) throws IOException {
PrintWriter writer = new PrintWriter(res.getOutputStream());
try {
// 获得公钥的两个参数,传回
BigInteger modulus = FRPrivilegeFilterHelper.getPublicKeyModulus();
BigInteger exponent = FRPrivilegeFilterHelper.getPublicKeyExponent();
writer.write(modulus.toString() + "&" + exponent.toString());
} catch (Exception e) {
e.printStackTrace();
writer.write("error");
}
writer.flush();
writer.close();
}
}
编译后的类保存在PFDemo\WEB-INF\classes\demo\DemoServlet.class。
4.4 报表工程中获取用户系统的公钥
登陆平台系统http://localhost:8075/WebReport/ReportServer?op=fs,选择
;数字签名密钥地址为http://localhost:8080/PFDemo/DemoServer?cmd=getkey,点击提交:
4.5 用户系统发送报表请求加入数字签名信息
如最上图中登陆后的主界面为index.jsp,代码如下:
<%@page contentType="text/html;charset=gbk" pageEncoding="gbk"%>
<%
String username = request.getParameter("username");
String password = request.getParameter("password");
// 下面是用户系统对登陆用户名和密码的验证,以下用最简单的代码做示例
if ("A".equals(username) && "123".equals(password)) {
request.getSession().setAttribute("username", "A");
} else if ("B".equals(username) && "123".equals(password)) {
request.getSession().setAttribute("username", "B");
} else {
request.getSession().setAttribute("username", "");
}
%>
<html>
<head>
<title>Privilege Demo</title>
<script type="text/javascript">
function viewReport(report) {
var f = document.getElementById("frame");
f.src = "/PFDemo/report.jsp?report=" + report;
}
</script>
</head>
<body bgcolor="#FFFFFF">
<table style="width:1018px;" border="2">
<colgroup>
<col width="150px;"/>
<col/>
</colgroup>
<tr style="height:30px;">
<td style="font-size:24pt;background-color:blue;color:#EFEFEF;" colSpan="2">
Privilege Demo
<span style="margin-left:500px;">Current user:</span>
<%=username%>
</td>
</tr>
<tr style="height:730px;">
<td style="vertical-align:top;">
<% if ("A".equals(request.getSession().getAttribute("username"))) {
%>
<ul>
<li style="cursor:pointer;color:blue;" onclick="javascript:viewReport('GettingStarted.cpt');">
Report1(A,B)
</li>
<li style="cursor:pointer;color:blue;" onclick="javascript:viewReport('doc/Primary/CrossReport/Cross.cpt');">
Report2(A)
</li>
</ul>
<%
} else if ("B".equals(request.getSession().getAttribute("username"))) {
%>
<ul>
<li style="cursor:pointer;" onclick="javascript:viewReport('GettingStarted.cpt');">
Report1(A,B)
</li>
</ul>
<%
}
%>
</td>
<td>
<iframe id="frame" name="frame" width="100%" height="100%"/>
</td>
</tr>
</table>
</body>
</html>
当点击左边报表节点时,触发viewReport这个js方法,通过iframe执行report.jsp文件,并传递请求查看的报表名字。
在report.jsp中利用fr-pfh-java-7.0.jar提供的接口,对需要访问的报表路径、报表浏览形式(op)、当前用户名与当前系统时间进行数字签名,得到签名信息,并将签名信息加入请求中转发给报表工程,代码如下:
<%@page contentType="text/html;charset=gbk" import="com.fr.pf.java.*" pageEncoding="gbk"%>
<%
String reportPath = request.getParameter("report"); // 获得需要访问的报表路径,即我方reportlet参数
String username = (String)request.getSession().getAttribute("username"); // 获取当前用户名
String op = "page"; // 默认分页预览时,null即可
long curtime = System.currentTimeMillis(); // 获取当前时间
String signInfo = FRPrivilegeFilterHelper.sign(reportPath, op, username, curtime); // 将上述获得的四个要素传入,获得相关的数字签名信息
String path = "http://localhost:8075/WebReport/ReportServer?reportlet=" + reportPath + "&op=" + op
+ "&" + FRPrivilegeFilterHelper.FR_DIGITALSIGNATURE_CURRENT_TIME + "=" + curtime
+ "&" + FRPrivilegeFilterHelper.FR_DIGITALSIGNATURE_INFO + "=" + signInfo; // 模拟拼接url,其实就是原有正常请求之后添加上签名的当前时间,和签名信息
%>
<html>
<head>
<title>Privilege Demo</title>
</head>
<body bgcolor="#FFFFFF" style="padding: 0pt; border: 0pt none; margin: 0pt; overflow: hidden;">
<iframe id="frame" src="<%=path%>" name="frame" width="100%" height="100%"/>
</body>
</html>
报表工程得到报表请求后进行验证,对reportlet参数值、op参数值、报表系统中当前用户名、发送来的系统时间进行数字签名验证是否正确。
且若用户系统发送来的签名时系统时间,与当前时间差超过90秒,将视为超时。如果都验证通过,则可以访问报表,否则提示没有权限查看。
4.6 重启服务器
重启用户系统服务器及报表服务器(注:必须先启动用户系统再启动报表系统),设置即可生效;
在浏览器中输入地址 http://localhost:8080/PFDemo/login.html,使用A用户和B用户分别登录,效果如上图。
在浏览器中输入地址 http://localhost:8080/PFDemo/login.html,使用A用户和B用户分别登录,效果如上图。
相关demo详细请前往权限集成java版demo页面下载,选择 即可。
5. 相关问题
5.1 何谓数字签名?
目前的数字签名是建立在公共密钥体制基础上,它是公用密钥加密技术的另一类应用。它的主要方式是:报文的发送方从报文文本中生成一个128位的散列值(或报文摘要)。
发送方用自己的私人密钥对这个散列值进行加密来形成发送方的数字签名。这个数字签名将作为报文的附件和报文一起发送给报文的接收方。
报文的接收方首先从接收到的原始报文中计算出128位的散列值(或报文摘要),再用发送方的公用密钥来对报文附加的数字签名进行解密。
如果两个散列值相同、那么接收方就能确认该数字签名是发送方的。通过数字签名能够实现对原始报文的鉴别。
附件列表
主题: 部署集成
标签:
已验证