大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
; Java程序的源代码很容易被别人偷看 只要有一个反编译器 任何人都可以分析别人的代码 本文讨论如何在不修改原有程序的情况下 通过加密技术保护源代码
成都创新互联公司于2013年成立,先为米脂等服务建站,米脂等地企业,进行企业商务咨询服务。为米脂企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
一 为什么要加密?
对于传统的C或C++之类的语言来说 要在Web上保护源代码是很容易的 只要不发布它就可以 遗憾的是 Java程序的源代码很容易被别人偷看 只要有一个反编译器 任何人都可以分析别人的代码 Java的灵活性使得源代码很容易被窃取 但与此同时 它也使通过加密保护代码变得相对容易 我们唯一需要了解的就是Java的ClassLoader对象 当然 在加密过程中 有关Java Cryptography Extension(JCE)的知识也是必不可少的
有几种技术可以 模糊 Java类文件 使得反编译器处理类文件的效果大打折扣 然而 修改反编译器使之能够处理这些经过模糊处理的类文件并不是什么难事 所以不能简单地依赖模糊技术来保证源代码的安全
我们可以用流行的加密工具加密应用 比如PGP(Pretty Good Privacy)或GPG(GNU Privacy Guard) 这时 最终用户在运行应用之前必须先进行解密 但解密之后 最终用户就有了一份不加密的类文件 这和事先不进行加密没有什么差别
Java运行时装入字节码的机制隐含地意味着可以对字节码进行修改 JVM每次装入类文件时都需要一个称为ClassLoader的对象 这个对象负责把新的类装入正在运行的JVM JVM给ClassLoader一个包含了待装入类(比如java lang Object)名字的字符串 然后由ClassLoader负责找到类文件 装入原始数据 并把它转换成一个Class对象
我们可以通过定制ClassLoader 在类文件执行之前修改它 这种技术的应用非常广泛??在这里 它的用途是在类文件装入之时进行解密 因此可以看成是一种即时解密器 由于解密后的字节码文件永远不会保存到文件系统 所以窃密者很难得到解密后的代码
由于把原始字节码转换成Class对象的过程完全由系统负责 所以创建定制ClassLoader对象其实并不困难 只需先获得原始数据 接着就可以进行包含解密在内的任何转换
Java 在一定程度上简化了定制ClassLoader的构建 在Java 中 loadClass的缺省实现仍旧负责处理所有必需的步骤 但为了顾及各种定制的类装入过程 它还调用一个新的findClass方法
这为我们编写定制的ClassLoader提供了一条捷径 减少了麻烦 只需覆盖findClass 而不是覆盖loadClass 这种方法避免了重复所有装入器必需执行的公共步骤 因为这一切由loadClass负责
不过 本文的定制ClassLoader并不使用这种方法 原因很简单 如果由默认的ClassLoader先寻找经过加密的类文件 它可以找到;但由于类文件已经加密 所以它不会认可这个类文件 装入过程将失败 因此 我们必须自己实现loadClass 稍微增加了一些工作量
二 定制类装入器
每一个运行着的JVM已经拥有一个ClassLoader 这个默认的ClassLoader根据CLASSPATH环境变量的值 在本地文件系统中寻找合适的字节码文件
应用定制ClassLoader要求对这个过程有较为深入的认识 我们首先必须创建一个定制ClassLoader类的实例 然后显式地要求它装入另外一个类 这就强制JVM把该类以及所有它所需要的类关联到定制的ClassLoader Listing 显示了如何用定制ClassLoader装入类文件
【Listing 利用定制的ClassLoader装入类文件】
以下是引用片段
// 首先创建一个ClassLoader对象 ClassLoader myClassLoader = new myClassLoader(); // 利用定制ClassLoader对象装入类文件 // 并把它转换成Class对象 Class myClass = myClassLoader loadClass( mypackage MyClass ); // 最后 创建该类的一个实例 Object newInstance = myClass newInstance(); // 注意 MyClass所需要的所有其他类 都将通过 // 定制的ClassLoader自动装入
如前所述 定制ClassLoader只需先获取类文件的数据 然后把字节码传递给运行时系统 由后者完成余下的任务
ClassLoader有几个重要的方法 创建定制的ClassLoader时 我们只需覆盖其中的一个 即loadClass 提供获取原始类文件数据的代码 这个方法有两个参数 类的名字 以及一个表示JVM是否要求解析类名字的标记(即是否同时装入有依赖关系的类) 如果这个标记是true 我们只需在返回JVM之前调用resolveClass
【Listing ClassLoader loadClass()的一个简单实现】
以下是引用片段
public Class loadClass( String name boolean resolve ) throws ClassNotFoundException { try { // 我们要创建的Class对象 Class clasz = null; // 必需的步骤 如果类已经在系统缓冲之中 // 我们不必再次装入它 clasz = findLoadedClass( name ); if (clasz != null) return clasz; // 下面是定制部分 byte classData[] = /* 通过某种方法获取字节码数据 */; if (classData != null) { // 成功读取字节码数据 现在把它转换成一个Class对象 clasz = defineClass( name classData classData length ); } // 必需的步骤 如果上面没有成功 // 我们尝试用默认的ClassLoader装入它 if (clasz == null) clasz = findSystemClass( name ); // 必需的步骤 如有必要 则装入相关的类 if (resolve clasz != null) resolveClass( clasz ); // 把类返回给调用者 return clasz; } catch( IOException ie ) { throw new ClassNotFoundException( ie toString() ); } catch( GeneralSecurityException gse ) { throw new ClassNotFoundException( gse toString() ); } }
Listing 显示了一个简单的loadClass实现 代码中的大部分对所有ClassLoader对象来说都一样 但有一小部分(已通过注释标记)是特有的 在处理过程中 ClassLoader对象要用到其他几个辅助方法
findLoadedClass 用来进行检查 以便确认被请求的类当前还不存在 loadClass方法应该首先调用它
defineClass 获得原始类文件字节码数据之后 调用defineClass把它转换成一个Class对象 任何loadClass实现都必须调用这个方法
findSystemClass 提供默认ClassLoader的支持 如果用来寻找类的定制方法不能找到指定的类(或者有意地不用定制方法) 则可以调用该方法尝试默认的装入方式 这是很有用的 特别是从普通的JAR文件装入标准Java类时
resolveClass 当JVM想要装入的不仅包括指定的类 而且还包括该类引用的所有其他类时 它会把loadClass的resolve参数设置成true 这时 我们必须在返回刚刚装入的Class对象给调用者之前调用resolveClass
三 加密 解密
Java加密扩展即Java Cryptography Extension 简称JCE 它是Sun的加密服务软件 包含了加密和密匙生成功能 JCE是JCA(Java Cryptography Architecture)的一种扩展
JCE没有规定具体的加密算法 但提供了一个框架 加密算法的具体实现可以作为服务提供者加入 除了JCE框架之外 JCE软件包还包含了SunJCE服务提供者 其中包括许多有用的加密算法 比如DES(Data Encryption Standard)和Blowfish
为简单计 在本文中我们将用DES算法加密和解密字节码 下面是用JCE加密和解密数据必须遵循的基本步骤
步骤 生成一个安全密匙 在加密或解密任何数据之前需要有一个密匙 密匙是随同被加密的应用一起发布的一小段数据 Listing 显示了如何生成一个密匙 【Listing 生成一个密匙】
以下是引用片段
// DES算法要求有一个可信任的随机数源 SecureRandom sr = new SecureRandom(); // 为我们选择的DES算法生成一个KeyGenerator对象 KeyGenerator kg = KeyGenerator getInstance( DES ); kg init( sr ); // 生成密匙 SecretKey key = kg generateKey(); // 获取密匙数据 byte rawKeyData[] = key getEncoded(); /* 接下来就可以用密匙进行加密或解密 或者把它保存 为文件供以后使用 */ doSomething( rawKeyData ); 步骤 加密数据 得到密匙之后 接下来就可以用它加密数据 除了解密的ClassLoader之外 一般还要有一个加密待发布应用的独立程序(见Listing ) 【Listing 用密匙加密原始数据】
以下是引用片段
// DES算法要求有一个可信任的随机数源 SecureRandom sr = new SecureRandom(); byte rawKeyData[] = /* 用某种方法获得密匙数据 */; // 从原始密匙数据创建DESKeySpec对象 DESKeySpec dks = new DESKeySpec( rawKeyData ); // 创建一个密匙工厂 然后用它把DESKeySpec转换成 // 一个SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory getInstance( DES ); SecretKey key = keyFactory generateSecret( dks ); // Cipher对象实际完成加密操作 Cipher cipher = Cipher getInstance( DES ); // 用密匙初始化Cipher对象 cipher init( Cipher ENCRYPT_MODE key sr ); // 现在 获取数据并加密 byte data[] = /* 用某种方法获取数据 */ // 正式执行加密操作 byte encryptedData[] = cipher doFinal( data ); // 进一步处理加密后的数据 doSomething( encryptedData ); 步骤 解密数据 运行经过加密的应用时 ClassLoader分析并解密类文件 操作步骤如Listing 所示 【Listing 用密匙解密数据】
// DES算法要求有一个可信任的随机数源 SecureRandom sr = new SecureRandom(); byte rawKeyData[] = /* 用某种方法获取原始密匙数据 */; // 从原始密匙数据创建一个DESKeySpec对象 DESKeySpec dks = new DESKeySpec( rawKeyData ); // 创建一个密匙工厂 然后用它把DESKeySpec对象转换成 // 一个SecretKey对象 SecretKeyFactory keyFactory = SecretKeyFactory getInstance( DES ); SecretKey key = keyFactory generateSecret( dks ); // Cipher对象实际完成解密操作 Cipher cipher = Cipher getInstance( DES ); // 用密匙初始化Cipher对象 cipher init( Cipher DECRYPT_MODE key sr ); // 现在 获取数据并解密 byte encryptedData[] = /* 获得经过加密的数据 */ // 正式执行解密操作 byte decryptedData[] = cipher doFinal( encryptedData ); // 进一步处理解密后的数据 doSomething( decryptedData );
四 应用实例
前面介绍了如何加密和解密数据 要部署一个经过加密的应用 步骤如下
步骤 创建应用 我们的例子包含一个App主类 两个辅助类(分别称为Foo和Bar) 这个应用没有什么实际功用 但只要我们能够加密这个应用 加密其他应用也就不在话下
步骤 生成一个安全密匙 在命令行 利用GenerateKey工具(参见GenerateKey java)把密匙写入一个文件 % java GenerateKey key data
步骤 加密应用 在命令行 利用EncryptClasses工具(参见EncryptClasses java)加密应用的类 % java EncryptClasses key data App class Foo class Bar class
该命令把每一个 class文件替换成它们各自的加密版本
步骤 运行经过加密的应用 用户通过一个DecryptStart程序运行经过加密的应用 DecryptStart程序如Listing 所示 【Listing DecryptStart java 启动被加密应用的程序】
以下是引用片段
import java io *; import java security *; import java lang reflect *; import javax crypto *; import javax crypto spec *; public class DecryptStart extends ClassLoader { // 这些对象在构造函数中设置 // 以后loadClass()方法将利用它们解密类 private SecretKey key; private Cipher cipher; // 构造函数 设置解密所需要的对象 public DecryptStart( SecretKey key ) throws GeneralSecurityException IOException { this key = key; String algorithm = DES ; SecureRandom sr = new SecureRandom(); System err println( [DecryptStart: creating cipher] ); cipher = Cipher getInstance( algorithm ); cipher init( Cipher DECRYPT_MODE key sr ); } // main过程 我们要在这里读入密匙 创建DecryptStart的 // 实例 它就是我们的定制ClassLoader // 设置好ClassLoader以后 我们用它装入应用实例 // 最后 我们通过Java Reflection API调用应用实例的main方法 static public void main( String args[] ) throws Exception { String keyFilename = args[ ]; String appName = args[ ]; // 这些是传递给应用本身的参数 String realArgs[] = new String[args length ]; System arraycopy( args realArgs args length ); // 读取密匙 System err println( [DecryptStart: reading key] ); byte rawKey[] = Util readFile( keyFilename ); DESKeySpec dks = new DESKeySpec( rawKey ); SecretKeyFactory keyFactory = SecretKeyFactory getInstance( DES ); SecretKey key = keyFactory generateSecret( dks ); // 创建解密的ClassLoader DecryptStart dr = new DecryptStart( key ); // 创建应用主类的一个实例 // 通过ClassLoader装入它 System err println( [DecryptStart: loading +appName+ ] ); Class clasz = dr loadClass( appName ); // 最后 通过Reflection API调用应用实例 // 的main()方法 // 获取一个对main()的引用 String proto[] = new String[ ]; Class mainArgs[] = { (new String[ ]) getClass() }; Method main = clasz getMethod( main mainArgs ); // 创建一个包含main()方法参数的数组 Object argsArray[] = { realArgs }; System err println( [DecryptStart: running +appName+ main()] ); // 调用main() main invoke( null argsArray ); } public Class loadClass( String name boolean resolve ) throws ClassNotFoundException { try { // 我们要创建的Class对象 Class clasz = null; // 必需的步骤 如果类已经在系统缓冲之中 // 我们不必再次装入它 clasz = findLoadedClass( name ); if (clasz != null) return clasz; // 下面是定制部分 try { // 读取经过加密的类文件 byte classData[] = Util readFile( name+ class ); if (classData != null) { // 解密 byte decryptedClassData[] = cipher doFinal( classData ); // 再把它转换成一个类 clasz = defineClass( name decryptedClassData decryptedClassData length ); System err println( [DecryptStart: decrypting class +name+ ] ); } } catch( FileNotFoundException fnfe ) // 必需的步骤 如果上面没有成功 // 我们尝试用默认的ClassLoader装入它 if (clasz == null) clasz = findSystemClass( name ); // 必需的步骤 如有必要 则装入相关的类 if (resolve clasz != null) resolveClass( clasz ); // 把类返回给调用者 return clasz; } catch( IOException ie ) { throw new ClassNotFoundException( ie toString() ); } catch( GeneralSecurityException gse ) { throw new ClassNotFoundException( gse toString() ); } } } 对于未经加密的应用 正常执行方式如下 % java App arg arg arg
对于经过加密的应用 则相应的运行方式为 % java DecryptStart key data App arg arg arg
DecryptStart有两个目的 一个DecryptStart的实例就是一个实施即时解密操作的定制ClassLoader;同时 DecryptStart还包含一个main过程 它创建解密器实例并用它装入和运行应用 示例应用App的代码包含在App java Foo java和Bar java内 Util java是一个文件I/O工具 本文示例多处用到了它 完整的代码请从本文最后下载
五 注意事项
我们看到 要在不修改源代码的情况下加密一个Java应用是很容易的 不过 世上没有完全安全的系统 本文的加密方式提供了一定程度的源代码保护 但对某些攻击来说它是脆弱的
虽然应用本身经过了加密 但启动程序DecryptStart没有加密 攻击者可以反编译启动程序并修改它 把解密后的类文件保存到磁盘 降低这种风险的办法之一是对启动程序进行高质量的模糊处理 或者 启动程序也可以采用直接编译成机器语言的代码 使得启动程序具有传统执行文件格式的安全性
另外还要记住的是 大多数JVM本身并不安全 狡猾的黑客可能会修改JVM 从ClassLoader之外获取解密后的代码并保存到磁盘 从而绕过本文的加密技术 Java没有为此提供真正有效的补救措施
lishixinzhi/Article/program/Java/hx/201311/25751
简单,
1,运动物体两个属性(x,y) 标定当前位置
2,障碍物对象(x1,y1) 是一个常量,标志它的位置
3,利用多线程编程,主线程运行物体,辅助线程进行判断
if(x=x1 且 y =y1),表示碰到障碍物了
4,剩下的就是决定你的下一步路径怎么走了,
无非是定义 一个路径函数f(),if-else的进行判断,
比如当碰到障碍物时,if(x==x1y==y1){x = f(x);y=f(y)}
自己决定,短小精悍
---
请问你补充的那个有什么用呢?
如果你要想绕过电线杆,还不是要先知道电线杆在哪?
..
然后再决定怎么绕,不就是x+ y+么?再不懂我无语了
使用J2EE技术或者叫JavaWeb技术开发一个Web服务器,服务器返回Json数据,android客户端解析json数据,使用http协议和服务器通信,android有相应模块和API。首先楼主得懂Java后台也就是JavaEE,用Servlet搭建Web服务,Serlvet映射一个URL,Android请求这个URL,Servlet处理请求,然后就是Java编程,Web分层、JDBC等技术。服务器返回标准的JSON轻量数据(就是纯文本),Android来解析。具体技术:(1)JavaServlet作为Web服务的处理入口;(2)Java编程编写业务处理程序;(3)JDBC访问数据库;(4)Android端的HTTP模块,API;(5)Android端解析JSON数据;(6)Servlet或者Java端生成JSON数据;等楼主技术强了,就可以接触各种框架、EJB技术了。Spring、SpringMVC、Struts、Hibernate,甚至NOSQL、分布式、负载、node.js、模板技术等等。
开发过程需要注意的问题有下面这些:\x0d\x0a\x0d\x0a1. 服务器端必须提供输入数据正确性的验证,客户端的Javascript验证可以没有。这是基于安全性的考虑,因为Javascript是很容易被绕过的,增加客户端验证只是为了减少服务器压力、界面更加容易使用。 \x0d\x0a\x0d\x0a2. 适度使用Session,尽量不要在Session里放很大的集合对象,以免内存消耗过大,因为很多用户访问的时候会产生很多的Session。参数传递应该尽量通过Request。熟悉HTTP有助于更好的理解Session、Cookie、Request等的机制。\x0d\x0a\x0d\x0a3. 在带有分页的查询界面,尽量不要使用POST方法来传递参数,POST传递的参数在地址栏里是看不到的,刷新后会有重新提交表单的提示。使用GET方法传递参数要注意URL的长度不能超过1K。 \x0d\x0a\x0d\x0a4. 分层应该清晰,一般目前我们分为View(Jsp或FreeMarker加上Action)、Bean(Service)、DAO这么三层,即显示层、业务层、数据层。记录集ResultSet这种只能出现在DAO层中的对象不能出现在Bean(Service)层中,同样HttpServletRequest这种只能出现在View层的对象也不应出现在Bean(Service)层中。这并不是绝对的。 \x0d\x0a\x0d\x0a5. 尽量使用简单SQL,避免两表以及多表联查。多表联查会导致数据库压力大幅增加,而且不利于在内存中对部分记录进行缓存,代码的重用性也难以提高。 \x0d\x0a\x0d\x0a6. 避免在循环里执行findXXById这样的方法,不如执行一个findXXByIds这样的方法一次性把记录取到Map里。大部分有实际对象对应的表应该提供这样一个方法。 \x0d\x0a\x0d\x0a7. 如果使用最原始的jdbc编程的话需要注意资源的正确释放,在循环里new出来的Statement或者ResultSet就要在循环里关闭。 \x0d\x0a\x0d\x0a8. 在编写SQL进行查询的时候,需要能够判断这个SQL是否已经使用了索引,避免全表扫描,必要的时候增加索引。 \x0d\x0a\x0d\x0a9. 在写一个方法前,首先查看有没有相同功能或者很类似功能的方法已经有了,尤其是工具类方法,往往已经写过了,避免重复代码的产生,发现重复代码及时进行处理。如果一段代码被重复使用两遍或以上,那么可以考虑专门写个方法来放这段代码,同样多次使用的常量也应该专门定义出来。 \x0d\x0a\x0d\x0a10. 在一个方法里并不一定只能有一个return,如果已经有结果了尽早return,没必要增加嵌套的层次,那样会导致代码可读性不佳,但也不能return太多,代码看起来比较舒服就可以了。 \x0d\x0a\x0d\x0a11. 不要满足于能够熟练的编写DAO和Bean(Service)的代码,相比较而言,后台如果在成熟框架的支持下,编码是没有太大难度的,也不值得沾沾自喜,因为这是对Java研发工程师基本的要求。适当的培养一下前台的编码能力,学会使用Dreamweaver。不要轻视界面,也别认为这是界面设计的事情,界面对用户来说就是软件,学会编写CSS和调整界面对你没有坏处。 \x0d\x0a\x0d\x0a12. 非常明确Java和javascript作用的范围,明确它们能做的事情。 \x0d\x0a\x0d\x0a13. 一般很奇怪的现象都是由一些低级错误引起的,如果你查了一段时间也没有结果,那么让别人来查吧。 \x0d\x0a\x0d\x0a14. 不要用可能被修改的字段来做主键,那样会让相关记录的更新成为一个大麻烦。 \x0d\x0a\x0d\x0a15. 如果被迫使用Hibernate和jdbc混合操作数据库的话,不要用Hibernate来做复杂查询和统计。Hibernate用的不好的话,带来的便利是非常有限的。 \x0d\x0a\x0d\x0a16. 数据库中经常被读取,但是很少修改的话,应该把这样的数据读到内存中用OSCache之类的缓存起来,然后定期或者触发的去更新,有助于减少读数据库次数,提升性能。 \x0d\x0a\x0d\x0a17. 编码的时候应该注意部署环境带来的影响,这种影响包括操作系统不同带来路径的差异;应用服务器和数据库服务器之间时间的差异;外网可能部署在多台服务器上,放到Session里的对象因为需要复制所以要实现java.io.Serializable接口等。 \x0d\x0a\x0d\x0a18. 尽量不要在jsp上编写太多代码,保持jsp的整洁很重要,用Dreamweaver打开不至于一塌糊涂,根本看不出来这是个什么界面。 \x0d\x0a\x0d\x0a19. 目前我们的项目一般都使用Spring来管理数据库事务,而且一般都配置在Bean(Service)即业务层这一层,应该注意要保持事务的完整性,不要把一些应该放在一起的操作分散在Action这一层。相关的更新操作可以认为是一个事务,比如:增加一个家长,同时更新学生是否有家长的字段。 \x0d\x0a\x0d\x0a20. 在Spring的配置中,对于有些需要保持独立事务的方法操作,比如生成主键等,应该声明该方法为独立事务ROPAGATION_REQUIRES_NEW。Bean(Service)里如果抛出checked exception,事务默认是不会回滚的,需要加以声明,比如PROPAGATION_REQUIRED,-PassportException。 \x0d\x0a\x0d\x0a21. 在一个Bean(Service)中引用其他Bean(Service)的时候尽量引用Bean(Service),而不是DAO。因为其他的Bean(Service)往往封装DAO的操作后,又做了进一步的完善,比如增加校验等,所以应该重用这些方法,而不必要去引用DAO的方法来重写这些操作。 \x0d\x0a\x0d\x0a22. 至少在Bean(Service)的接口定义上增加注释,方便他人引用你写的方法。 \x0d\x0a\x0d\x0a23. 好好利用集合框架里的Map、List、Set。尤其是HashMap、ArrayList、HashSet用的最多,这些类是多条数据操作的基础,它们都不是线程安全的。 \x0d\x0a\x0d\x0a24. 现在跑的快的页面,随着数据量的增加,可能会变的很慢,所以应该意识到页面可能变慢的原因,而不是现在看起来很快。影响速度的大部分原因是对数据库的压力太大了,在java代码执行上花费很多时间的情况是不常见的。 \x0d\x0a\x0d\x0a25. 避免不必要的跳转,如果页面执行的足够快,那么中间的载入进度提示页是不必要的,那样会让用户觉得闪烁。 \x0d\x0a\x0d\x0a26. 注意页面的文件大小,并不是每个用户的带宽都是非常理想的,文件小一点,速度快一点,总是感觉更好一点。 \x0d\x0a\x0d\x0a27. 不要去修改用户的浏览器,比如隐藏他们的地址栏、菜单、右键菜单等,这可能会引起部分用户的反感。尽量不要使用弹出窗口,可能会被拦截。 \x0d\x0a\x0d\x0a28. 网站的权限控制至少应该保证有访问权限的用户才能访问页面,通过隐藏链接之类的方法是很不安全的,用户看不见了并不表示安全了。权限的控制尽量使用框架里的拦截器这样的机制,而不是把权限控制代码写的到处都是。过滤器不宜过多的使用,不仅因为过滤器的作用范围很难控制,而且容易引起页面执行效果的混乱,错误不易排查。 \x0d\x0a\x0d\x0a29. 页面里大部分情况使用相对路径,保持Action路径层次和页面一致,这样应用就可以发布在各种目录下。