您的位置:首页 > 其它

润乾报表单独部署url安全之加解密函数

2017-05-30 11:30 309 查看
润乾报表与用户的系统集成,一般有两种方案。一是集成到用户系统中。二是将润乾报表单独部署到一个应用下。

两种方案各有利弊:第一种方案对于安全性等方面可以统一管理,但是报表本身如果数据量大并发大造成的压力会直接影响用户自己的系统。第二种方案在报表服务器承受压力过大数据量过大的时候,不会影响到用户本身的系统,也就是说就算报表服务器压力饱和进入等待状态,用户的系统也可以正常的使用,众所周知润乾报表的调用一般直接通过url的方式进行调用,那么第二种方案中的url就无法被用户系统所控制,造成了一定的安全隐患。

下面就介绍几种解决此类问题的方案之中的一种:对url进行加解密

案例场景:

通过用户系统访问一张报表时,当然会带着一些用户信息或是其他参数传递过来。如果这时这个url的明文被其他人记住,比如url拼参数username=zhangsan,这时李四记住了这个url并且与自己访问该资源的url进行对比发现传入参数为用户姓名拼音,由于采用的是第二种方案,用户系统并不能控制,这样就可以肆无忌惮的去查看其他人的信息,导致信息的泄漏。

解决思路:

这时我们可以采取对url中的明文进行加解密的操作,通过用户系统传递参数之前进行加密,然后传递到报表服务器端,在jsp中或者报表中进行解密。(下文介绍是在报表中解密)

实现方法:

一、首先确定加解密规则,这里使用的是AES加解密方法,密钥为16位英文,由于是说明将密钥定义死在相应类中。

二、通过测试类将username=zhangsan加密为:

880CB3013AB0D0F3A91B1C36B323F6E40294D3BE6EEFAA38135690CA3DCC61A2模拟用户系统传递参数到报表服务器的url为:

http://127.0.0.1:6001/demo/reportJsp/showReport.jsp?aes=880CB3013AB0D0F3A91B1C36B323F6E40294D3BE6EEFAA38135690CA3DCC61A2&raq=demo.raq

三、编写AES加解密的自定义函数,接受参数时只用到解密函数,加密函数是在报表中超链接等位置再次使用时可以调用加密函数。
      1、实现AES加解密的类

public
class AES {
/** 
* 加密 
*  
* @param content 需要加密的内容 
* @param password  加密密码 
* @return 
*/  
public static byte[] encrypt(String content, String password) {  

        try {             

                KeyGenerator kgen = KeyGenerator.getInstance("AES");  

                kgen.init(128, new SecureRandom(password.getBytes()));  

                SecretKey secretKey = kgen.generateKey();  

                byte[] enCodeFo
4000
rmat = secretKey.getEncoded();  

                SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");  

                Cipher cipher = Cipher.getInstance("AES");// 创建密码器  

                byte[] byteContent = content.getBytes("utf-8");  

                cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化  

                byte[] result = cipher.doFinal(byteContent);  

                return result; // 加密  

        } catch (NoSuchAlgorithmException e) {  

                e.printStackTrace();  

        } catch (NoSuchPaddingException e) {  

                e.printStackTrace();  

        } catch (InvalidKeyException e) {  

                e.printStackTrace();  

        } catch (UnsupportedEncodingException e) {  

                e.printStackTrace();  

        } catch (IllegalBlockSizeException e) {  

                e.printStackTrace();  

        } catch (BadPaddingException e) {  

                e.printStackTrace();  

        }  

        return null;  

}  
/**解密 
* @param content  待解密内容 
* @param password 解密密钥 
* @return 
*/  
public static byte[] decrypt(byte[] content, String password) {  
       try {  
                KeyGenerator kgen = KeyGenerator.getInstance("AES");  
                kgen.init(128, new SecureRandom(password.getBytes()));  
                SecretKey secretKey = kgen.generateKey();  
                byte[] enCodeFormat = secretKey.getEncoded();  
                SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");              
                Cipher cipher = Cipher.getInstance("AES");// 创建密码器  
               cipher.init(Cipher.DECRYPT_MODE, key);// 初始化  
               byte[] result = cipher.doFinal(content);  
               return result; // 加密  
       } catch (NoSuchAlgorithmException e) {  
               e.printStackTrace();  
       } catch (NoSuchPaddingException e) {  
               e.printStackTrace();  
       } catch (InvalidKeyException e) {  
               e.printStackTrace();  
       } catch (IllegalBlockSizeException e) {  
               e.printStackTrace();  
       } catch (BadPaddingException e) {  
               e.printStackTrace();  
       }  
       return null;  
}  
/**将二进制转换成16进制 
* @param buf 
* @return 
*/  
public static String parseByte2HexStr(byte buf[]) {  
       StringBuffer sb = new StringBuffer();  
       for (int i = 0; i < buf.length; i++) {  
               String hex = Integer.toHexString(buf[i] & 0xFF);  
               if (hex.length() == 1) {  
                       hex = '0' + hex;  
               }  
               sb.append(hex.toUpperCase());  
       }  
       return sb.toString();  
}  
/**将16进制转换为二进制 
* @param hexStr 
* @return 
*/  
public static byte[] parseHexStr2Byte(String hexStr) {  
       if (hexStr.length() < 1)  
               return null;  
       byte[] result = new byte[hexStr.length()/2];  
       for (int i = 0;i< hexStr.length()/2; i++) {  
               int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);  
               int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);  
               result[i] = (byte) (high * 16 + low);  
       }  
       return result;  
}  

2、润乾自定义加解密函数中调用AES类

加密自定义函数:
//判断参数个数
if (this.param == null || this.param.getSubSize() !=0) {
MessageManager mm = EngineMessage.get();
throw new ReportError("encrypt:" + mm.getMessage("function.missingParam"));
}
//取得第一个参数,默认为表达式,需要把该表达式算出来,结果才是函数的参数值
Expression param1=(Expression)this. param.getLeafExpression();
if (param1 == null) { //判断参数是否为空
MessageManager mm = EngineMessage.get();
throw new ReportError("encrypt:" + mm.getMessage("function.invalidParam"));
}
//算出第一个参数值
Object result1 = Variant2.getValue(param1.calculate(ctx), false);
//判断第一个参数值是否为空
if (result1 == null) {
return null;
}
//判断第一个参数值的数据类型
if (! (result1 instanceof String)) {
MessageManager mm = EngineMessage.get();
throw new ReportError("encrypt:" + mm.getMessage("function.paramTypeError"));
}
// 算出第一个参数值
Object result2 = Variant2.getValue(param1.calculate(ctx),false);
// 判断第一个参数值是否为空
if (result2 == null) {
return null;
}  
AES aes = new AES();
byte[] value = aes.encrypt(result2.toString(),"aaaaaaaaaaaaaaaa");
String encryptResultStr = aes.parseByte2HexStr(value); 
return encryptResultStr;

解密自定义函数:

//判断参数个数
if (this.param == null || this.param.getSubSize() !=0) {
MessageManager mm = EngineMessage.get();
throw new ReportError("encrypt:" + mm.getMessage("function.missingParam"));
}
//取得第一个参数,默认为表达式,需要把该表达式算出来,结果才是函数的参数值
Expression param1=(Expression)this. param.getLeafExpression();
if (param1 == null) { //判断参数是否为空
MessageManager mm = EngineMessage.get();
throw new ReportError("encrypt:" + mm.getMessage("function.invalidParam"));
}
//算出第一个参数值
Object result1 = Variant2.getValue(param1.calculate(ctx), false);
//判断第一个参数值是否为空
if (result1 == null) {
return null;
}
//判断第一个参数值的数据类型
if (! (result1 instanceof String)) {
MessageManager mm = EngineMessage.get();
throw new ReportError("encrypt:" + mm.getMessage("function.paramTypeError"));
}
// 算出第一个参数值
Object result2 = Variant2.getValue(param1.calculate(ctx),false);
// 判断第一个参数值是否为空
if (result2 == null) {
return null;
}  
AES aes = new AES();
byte[] decryptFrom = aes.parseHexStr2Byte(result2.toString());  
byte[] decryptResult = aes.decrypt(decryptFrom,"aaaaaaaaaaaaaaaa"); 
return new String(decryptResult);

四、 自定义函数注册

1,将编译后的class文件复制到

\report5\web\webapps\demo\WEB-INF\classes

2,在

\report5\web\webapps\demo\WEB-INF\classes\config\customFunctions.properties 文件加入以下内容.

encode=0,api.MyEncode

decode=0,api.MyDecode

3,如果\WEB-INF\classes目前下没有config目录,则可以自己创建.

如果已经存在,在该目录下执行上述步骤2.

 重新启动设计器服务器。

 在设计器中测试内容:

=encode(“ceshi”);

=decode(“555DF8AB73DAB1FF191A2B977430E02B”);

 出现结果为正确. 

五、报表中设定参数aes接受url中传递过来的值,通过动态参数调用解密函数将真实值取到进行报表中的运算。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息