热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Shiro权限绕过漏洞分析

 前言前段时间遇到通过分号绕过nginx层屏蔽并顺利访问到Springboot项目actuator端点的问题,修复过程中偶然发现当项目使用shiro组件时,若将shiro升级到1.6.0可间接修复分号

 

前言

前段时间遇到通过分号绕过nginx层屏蔽并顺利访问到Springboot项目actuator端点的问题,修复过程中偶然发现当项目使用shiro组件时,若将shiro升级到1.6.0可间接修复分号绕过的问题,当请求url中包含分号时响应状态码为400;

考虑到shiro主要用来执行身份验证授权等,理论上不适合直接阻断存在分号的请求;

为搞明白此问题,决定对shiro的权限校验问题进行整理学习,下面为常见的shiro权限绕过漏洞分析修复过程。

 

Shiro Filter

学习shiro权限绕过漏洞之前,有必要了解下shiro filter的过滤过程;

一个http请求过来,首先经过web容器的处理(这里默认为tomcat)被投放到相应的web应用,web应用会通过过滤器Filter链式的对http请求进行预处理,这里将会经过shiro的Filter(SpringShiroFilter)处理:

org.apache.catalina.core.ApplicationFilterChain#internalDoFilter --> org.apache.shiro.web.servlet.OncePerRequestFilter#doFilter -->
org.apache.shiro.web.servlet.AbstractShiroFilter#doFilterInternal -->
org.apache.shiro.web.servlet.AbstractShiroFilter#executeChain

shiro的Filter执行过会通过getExecutionChain()获取执行链并执行对应的doFilter函数:

重点在获取chain的org.apache.shiro.web.servlet.AbstractShiroFilter#getExecutionChain中;

首先会尝试获取到在springboot启动时加载的shiro配置文件中配置的Shiro filterChains(resolver)并判断是否为null,当为null则返回原始过滤器链origChain,当不为空时则尝试通过org.apache.shiro.web.filter.mgt.FilterChainResolver#getChain方法根据当前请求的url使用Ant模式获取相应的拦截器链 FilterChain代理(resolved),否则返回原始过滤器链origChain:

protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
FilterChain chain = origChain;
FilterChainResolver resolver = getFilterChainResolver();
if (resolver == null) {
log.debug("No FilterChainResolver configured. Returning original FilterChain.");
return origChain;
}
FilterChain resolved = resolver.getChain(request, response, origChain);
if (resolved != null) {
log.trace("Resolved a configured FilterChain for the current request.");
chain = resolved;
} else {
log.trace("No FilterChain configured for the current request. Using the default.");
}
return chain;
}

其中获取shiro对应的FilterChain代理是在org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain中完成,主要通过获取所有的filter链(filterChainManager)及requestURI,并循环遍历filterChainManager进行匹配,当匹配时则直接返回相对应的FilterChain代理,否则返回null:

附上filterChainManager接口说明:

public interface FilterChainManager {
// 得到注册的拦截器
Map getFilters();
// 获取拦截器链
NamedFilterList getChain(String chainName);
// 是否有拦截器链
boolean hasChains();
// 得到所有拦截器链的名字
Set getChainNames();
// 使用指定的拦截器链代理原始拦截器链
FilterChain proxy(FilterChain original, String chainName);
// 注册拦截器
void addFilter(String name, Filter filter);
// 注册拦截器
void addFilter(String name, Filter filter, boolean init);
// 根据拦截器链定义创建拦截器链
void createChain(String chainName, String chainDefinition);
// 添加拦截器到指定的拦截器链
void addToChain(String chainName, String filterName);
// 添加拦截器(带有配置的)到指定的拦截器链
void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) throws ConfigurationException;
}

最终链式执行过滤器:

org.apache.catalina.core.ApplicationFilterChain#doFilter --> org.apache.catalina.core.ApplicationFilterChain#internalDoFilter

执行完过滤器后将调用servlet.service,进而执行controller解析及service等:

javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse):

接下来的SpringMVC controller解析部分详情可参见下面CVE-2020-1957漏洞分析部分。

 

漏洞环境

可参考:

https://github.com/apache/shiro/releases

https://github.com/ttestoo/java-sec-study/tree/main/sec-shiro/java-shiro-bypass

 

shiro权限绕过 CVE-2020-1957



利用条件

Apache Shiro <1.5.2


利用过程

1、正常情况下访问/hello/a会进行跳转:http://127.0.0.1:9091/hello/a

2、可通过分号进行绕过:

http://127.0.0.1:9091/;/hello/a

http://127.0.0.1:9091/aa/..;test=123/admin/index


漏洞分析

shiro filter主要过程上述部分已简单介绍,其中根据分号绕过可初步判断问题可能出现在获取requestURI处,毕竟是拿requestURI和shiro配置的FilterChain进行匹配的,也就是说通过分号使得shiro filter过程的requestURI能绕过shiro的Ant格式的规则匹配;

可以直接在org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain中的String requestURI = getPathWithinApplication(request); 处下断点进行调试:

注意这里测试过程中URL为:http://127.0.0.1:9091/aa/..;test=123/admin/index

跟进getPathWithinApplication函数,将通过WebUtils.getPathWithinApplication —> WebUtils.getRequestUri 获取requestURI:

最终还是通过request.getRequestURI获取请求中的URI,即:/aa/..;test=123/admin/index

获取到请求中的URI后,将通过org.apache.shiro.web.util.WebUtils#normalize进行标准化处理,其中参数调用了decodeAndCleanUriString函数处理,在decodeAndCleanUriString中可以清晰的看到根据 ; 对uri进行了分割,并获取到 ; 之前的部分,即/aa/..

且在normalize函数中,主要标准化处理内容如下:


  • \ —> /

  • // —> /

  • /./ —> /

  • /../ —> /

private static String normalize(String path, boolean replaceBackSlash) {
if (path == null)
return null;
// Create a place for the normalized path
String normalized = path;
if (replaceBackSlash && normalized.indexOf('\\') >= 0)
normalized = normalized.replace('\\', '/');
if (normalized.equals("/."))
return "/";
// Add a leading "/" if necessary
if (!normalized.startsWith("/"))
normalized = "/" + normalized;
// Resolve occurrences of "//" in the normalized path
while (true) {
int index = normalized.indexOf("//");
if (index <0)
break;
normalized = normalized.substring(0, index) +
normalized.substring(index + 1);
}
// Resolve occurrences of "/./" in the normalized path
while (true) {
int index = normalized.indexOf("/./");
if (index <0)
break;
normalized = normalized.substring(0, index) +
normalized.substring(index + 2);
}
// Resolve occurrences of "/../" in the normalized path
while (true) {
int index = normalized.indexOf("/../");
if (index <0)
break;
if (index == 0)
return (null); // Trying to go outside our context
int index2 = normalized.lastIndexOf('/', index - 1);
normalized = normalized.substring(0, index2) +
normalized.substring(index + 3);
}
// Return the normalized path that we have completed
return (normalized);
}

由于此时传入的为经过decodeAndCleanUriString处理后的值(/aa/..),所以这里经过normalize处理后结果不变:

也就是说此时通过WebUtils.getRequestUri获取到的requestUri的值为/aa/..

且getPathWithinApplication后的值也为/aa/..

接下来将对requestURI和在shiro配置文件中配置的过滤规则filterChains进行匹配,则/aa/..不会和任何规则匹配成功,返回null:

至此,我们通过在请求url中添加 ; 成功绕过了shiro的filter过滤匹配,并成功进入SpringMVC controller解析过程,其中入口为SpringMVC的DispatcherServlet.doService():

org.apache.catalina.core.ApplicationFilterChain#internalDoFilter --> javax.servlet.Servlet#service --> javax.servlet.http.HttpServlet#service --> javax.servlet.http.HttpServlet#doGet --> org.springframework.web.servlet.FrameworkServlet#processRequest -->
org.springframework.web.servlet.DispatcherServlet#doService -->

在DispatcherServlet.doService中将调用核心的doDispatch方法进行下一步处理:

其中doDispatch主要处理逻辑如下:


  1. checkMultipart 检查是不是文件上传请求,如果是,则对当前 request 重新进行包装,如果不是,则直接将参数返回;

  2. 根据当前请求,调用 getHandler 方法获取请求处理器,如果没找到对应的请求处理器,则调用 noHandlerFound 方法抛出异常或者给出 404;

  3. getHandlerAdapter 方法,根据当前的处理器找到处理器适配器;

  4. 然后处理 GET 和 HEAD 请求头的 Last_Modified 字段。当浏览器第一次发起 GET 或者 HEAD 请求时,请求的响应头中包含一个 Last-Modified 字段,这个字段表示该资源最后一次修改时间,以后浏览器再次发送 GET、HEAD 请求时,都会携带上该字段,服务端收到该字段之后,和资源的最后一次修改时间进行对比,如果资源还没有过期,则直接返回 304 告诉浏览器之前的资源还是可以继续用的,如果资源已经过期,则服务端会返回新的资源以及新的 Last-Modified;

  5. 接下来调用拦截器的 preHandle 方法,如果该方法返回 false,则直接 return 掉当前请求;

  6. 接下来执行 ha.handle 去调用真正的请求,获取到返回结果 mv;

  7. 接下来判断当前请求是否需要异步处理,如果需要,则直接 return 掉;如果不需要异步处理,则执行 applyDefaultViewName 方法,检查当前 mv 是否没有视图,如果没有(例如方法返回值为 void),则给一个默认的视图名;

  8. processDispatchResult 方法对执行结果进行处理,包括异常处理、渲染页面以及执行拦截器的 afterCompletion 方法都在这里完成;

  9. 最后在 finally 代码块中判断是否开启了异步处理,如果开启了,则调用相应的拦截器;如果请求是文件上传请求,则再调用 cleanupMultipart 方法清除文件上传过程产生的一些临时文件。

其中我们关心的是获取请求处理器的过程,即getHandler方法实现细节:

首先Spring会循环所有注册的HandlerMapping并返回第一个匹配的HandlerExecutionChain:

对于mapping为RequestMappingHandlerMapping时,则会调用org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler进行获取对应的handler,

在getHandler中调用getHandlerInternal方法,并在其中进行调用getLookupPathForRequest—>getPathWithinServletMapping解析请求路径lookupPath:

对于getPathWithinServletMapping中首先通过getPathWithinApplication获取请求URI在web应用中的路径,其中经过如下调用链:

最终在org.springframework.web.util.UrlPathHelper#getRequestUri中通过request.getRequestURI()获取请求的uri,即/aa/..;test=123/admin/index,并通过decodeAndCleanUriString方法对uri进行处理:

decodeAndCleanUriString方法中主要做了三件事:

1、调用removeSemicolonContent方法对uri进行处理,其中将分号及后面的key-value进行了去除得到新的requestUri为/aa/../admin/index:

2、通过调用decodeRequestString进行URL解码:

3、通过调用getSanitizedPath将//替换为/:

至此经过decodeAndCleanUriString方法处理后最终获取到的uri为/aa/../admin/index,即getPathWithinApplication返回的结果pathWithinApp的值也为/aa/../admin/index:

接下来回到getPathWithinServletMapping中,此时pathWithinApp值为/aa/../admin/index,servletPath为/admin/index,正常情况下将通过getRemainingPath方法将pathWithinApp中servletPath给截取掉,但此时由于两者值无法截取成功返回为null:

由于path为null,则进入else这种特殊情况,最终返回servletPath,即/admin/index:

即最终获取到的lookupPath值为/admin/index,并根据lookupPath的值寻找相对应的handlerMethod为com.ttestoo.bypass.controller.LoginController#admin_index():

至此,SpringMVC中获取handler的过程已结束,成功获取到/admin/index相对应的handler方法,并将继续获取HandlerAdapter,并执行HandlerAdapter的handle方法利用反射机制执行/admin/index相对应的controller方法com.ttestoo.bypass.controller.LoginController#admin_index():

总结:

当我们发起请求http://127.0.0.1:9091/aa/..;test=123/admin/index时,经过shiro filter进行权限校验,此时shiro解析到的路径为/aa/..不会和任何规则匹配成功,从而通过了shiro的权限校验;

接下来会由springmvc进行controller解析,此时springmvc解析到的路径为/admin/index,并获取到/admin/index对应的handler方法admin_index,从而正常的执行service并得到最终的响应。

漏洞本质:

当路径中包含特殊字符时,shiro解析得到的路径和SpringMVC解析得到的路径不一致,导致可正常通过shiro的权限校验并正常的完成service的执行获取执行结果。


利用场景

在实际场景中每个API会通过网关层统一校验或@RequiresPermissions注解等方式校验所需访问权限,此漏洞仅能绕过shiro全局的Filter校验,无法绕过API配置的访问权限;

个人理解此问题在实战中利用场景相对有限。


修复方式

根据官方commit记录,可知在1.5.2开始,在org.apache.shiro.web.util.WebUtils#getRequestUri中获取uri的方式从request.getRequestURI()换成了request.getContextPath()+request.getServletPath()+request.getPathInfo()组合的方式:

此时请求http://127.0.0.1:9091/aa/..;test=123/admin/index获取到的url为/admin/index,无法绕过shiro的权限校验:

 

shiro权限绕过 CVE-2020-11989



利用条件

主要是针对CVE-2020-1957修复后的绕过探索,主要两种方式:


  • URL双编码:Apache Shiro = 1.5.2 & controller需为类似”/hello/{page}”的方式

  • 分号绕过:Apache Shiro <1.5.3 & server.servlet.context-path不为根/



利用过程

URL双编码

shiro版本需为1.5.2,访问http://127.0.0.1:9091/hello/test,被shiro权限校验拦截:

访问http://127.0.0.1:9091/hello/te%25%32%66st可直接绕过权限校验:

分号绕过

需要配置server.servlet.context-path:

server.servlet.context-path=/test

http://127.0.0.1:9091/test/hello/test

http://127.0.0.1:9091/a/..;a=1/test/hello/test


漏洞分析

URL双编码分析

首先发起http请求时,若url中存在URL编码字符,则会被容器进行一次URL解码,此时/hello/te%25%32%66st —> /hello/te%2fst,接下来则同CVE-2020-1957过程一样,在shiro处理过程中会在org.apache.shiro.web.util.WebUtils#getPathWithinApplication中通过getRequestUri函数获取uri:

在org.apache.shiro.web.util.WebUtils#getRequestUri中首先通过request.getContextPath()+request.getServletPath()+request.getPathInfo()组合的方式获取uri为://hello/te%2fst

接着在进入normalize函数进行格式化处理时,传入的参数经过了decodeAndCleanUriString方法的处理,其中通过调用org.apache.shiro.web.util.WebUtils#decodeRequestString方法,利用URLDecoder.decode进行了URL解码为://hello/te/st

继续进入normalize函数,将//替换为/,最终获取到的uri为:/hello/te/st

接下来就同上面shiro过程一致了,/hello/te/st不会和任何的规则匹配,成功解析controller并执行相对应的service获取响应结果:

补充,在1.5.1及之前版本中,获取uri采用request.getRequestURI方式,获取到的为URL双编码的值,shiro进行一次解码并格式化处理后为/hello/te%2fst,则会和/hello/*进行匹配:

分号绕过分析

当请求为http://127.0.0.1:9091/a/..;a=1/test/hello/test时,

同URL双编码主要区别在经过request.getContextPath()+request.getServletPath()+request.getPathInfo()组合获取到的uri为:/a/..;a=1/test//hello/test

request.getContextPath() --> /a/..;a=1/test
request.getServletPath() --> /hello/test
request.getPathInfo() --> null

接着经过org.apache.shiro.web.util.WebUtils#decodeAndCleanUriString处理后,会根据 ; 进行分割,最终uri结果为:/a/..

从而绕过shiro的权限校验,而接下来则和cve-2020-1957流程一致,springmvc会将 ; 进行剔除,最终根据/a/../test/hello/test,即/test/hello/test成功解析controller并执行service获取响应结果:


修复方式

在1.5.3中,获取uri方式改成了getServletPath(request) + getPathInfo(request)组合的方式,且去除了解码过程:

此时,同样的请求http://127.0.0.1:9091/a/..;a=1/test/hello/test,获取到的requestURI为:/hello/test

请求http://127.0.0.1:9091/hello/te%25%32%66st获取到的requestURI为:/hello/te%27st

 

shiro权限绕过 CVE-2020-13933



利用条件

Apache shiro<=1.5.3 即 Apache Shiro <1.6.0

controller需为类似”/hello/{page}”的方式利用过程


利用过程

http://127.0.0.1:9091/hello/test

http://127.0.0.1:9091/hello/%3btest


漏洞分析

当发起请求http://127.0.0.1:9091/hello/%3btest时,

首先容器会进行URL解码,/hello/%3btest —> /hello/;test

进入shiro处理,org.apache.shiro.web.util.WebUtils#getPathWithinApplication中最终结果即requestURI为/hello/:

getServletPath(request) --> /hello/;test
getPathInfo(request) --> ""
removeSemicolon中根据;进行分割

而/hello/不会和/hello/*匹配,顺利进入controller解析并执行service获取响应结果:


修复方式

在1.6.0中,执行shiro过滤器时增加了preHandle方法进行判断是否继续执行:

判断内容主要为:若请求URI中包含分号、反斜杠、非ASCII字符(均可配置),则直接响应400

核心逻辑均在新增的类org.apache.shiro.web.filter.InvalidRequestFilter中:https://shiro.apache.org/static/1.6.0/apidocs/org/apache/shiro/web/filter/InvalidRequestFilter.html

 

shiro权限绕过 CVE-2020-17523



利用条件

Apache Shiro <1.7.1

controller需为类似”/hello/{page}”的方式


利用过程

空格绕过

1、http://127.0.0.1:9091/hello/test

2、http://127.0.0.1:9091/hello/%20

西式句号 全路径绕过

需配置开启全路径匹配:

1、http://127.0.0.1:9091/hello/.


漏洞分析

空格绕过

当发起http://127.0.0.1:9091/hello/%20请求时,

首先经过容器URL解码,/hello/%20 —> /hello/空格

发现在shiro进行匹配过程中,/hello/* 和 /hello/空格 不匹配:

在org.apache.shiro.util.AntPathMatcher#doMatch函数中,/hello/* 和 /hello/空格 经过tokenizeToStringArray函数处理后的结果中/hello空格仅剩/hello:

跟进tokenizeToStringArray方法可知,调用过程中,trimTokens参数值为true:

而当trimTokens为true时,则会调用trim(),此时/hello/后面的空格将会被丢弃:

在接下来的判断过程中,匹配结果为false,即/hello/* 和 /hello/空格 不匹配从而导致shiro的权限绕过:

西式句号 全路径绕过

当请求http://127.0.0.1:9091/hello/.时,首先经过org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getPathWithinApplication处理时,会被normalize函数将/hello/.最后面的 . 删除掉,最终requestURI为/hello/:

在org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver#getChain处理过程中,由于此时requestURI以/结尾,则将会把最后一个/删除,变成/hello:

而/hello和/hello/*不匹配导致shiro权限绕过:

但是,此时SpringMVC进行controller解析时,请求路径为/hello/%2e,spring中.和/默认是作为路径分割符的,不会参与到路径匹配,此时将解析controller失败,返回404:

即,虽然绕过了shiro的权限校验,但默认无法解析到controller,也就无法执行想要的service;

特殊情况:

当手工配置开启springboot的全路径匹配时,可成功执行:

//开启全路径匹配
@ServletComponentScan
@SpringBootApplication
public class Application extends SpringBootServletInitializer implements BeanPostProcessor {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof RequestMappingHandlerMapping) {
((RequestMappingHandlerMapping) bean).setAlwaysUseFullPath(true);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}


修复方式

1、将tokenizeToStringArray函数的trimTokens参数设置为false,防止空格被抛弃:

2、将剔除结尾/的过程移到匹配的下方:

 

总结

开头所说的利用分号绕过了nginx的403屏蔽,请求到springboot项目后携带分号解析成功,而当使用了shiro 1.6.0后,url中携带分号则直接报错400,原因就很清晰了,因为在1.6.0中增加了org.apache.shiro.web.filter.InvalidRequestFilter类,其中会判断请求中包含分号时响应400状态码;

shiro的权限绕过,本质还是shiro对uri的解析规则和后端开发框架的解析规则不一样所导致。

 

巨人的肩膀

https://github.com/apache/shiro/commit/3708d7907016bf2fa12691dff6ff0def1249b8ce

https://shiro.apache.org/static/1.6.0/apidocs/org/apache/shiro/web/filter/InvalidRequestFilter.html

https://github.com/apache/shiro/commit/0842c27fa72d0da5de0c5723a66d402fe20903df

https://shiro.apache.org/security-reports.html

https://xlab.tencent.com/cn/2020/06/30/xlab-20-002/

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

https://github.com/jweny/shiro-cve-2020-17523

https://segmentfault.com/a/1190000039703198

……


推荐阅读
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
author-avatar
凝笙儿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有