diff --git a/pom.xml b/pom.xml
index 45530349..7e88b919 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,6 +32,12 @@
shiro-example-chapter20
shiro-example-chapter21
shiro-example-chapter22
+ shiro-example-chapter23-server
+ shiro-example-chapter23-core
+ shiro-example-chapter23-client
+ shiro-example-chapter23-pom
+ shiro-example-chapter23-app1
+ shiro-example-chapter23-app2
diff --git a/shiro-example-chapter23-app1/pom.xml b/shiro-example-chapter23-app1/pom.xml
new file mode 100644
index 00000000..bcd5be16
--- /dev/null
+++ b/shiro-example-chapter23-app1/pom.xml
@@ -0,0 +1,57 @@
+
+
+
+ shiro-example-chapter23-pom
+ com.github.zhangkaitao
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ shiro-example-chapter23-app1
+
+
+
+ com.github.zhangkaitao
+ shiro-example-chapter23-client
+ 1.0-SNAPSHOT
+
+
+
+
+ chapter23-app1
+
+
+ org.mortbay.jetty
+ jetty-maven-plugin
+ 8.1.8.v20121106
+
+
+ /${project.build.finalName}
+
+
+
+ 9080
+ 60000
+
+
+
+
+
+
+
+ org.apache.tomcat.maven
+ tomcat7-maven-plugin
+ 2.2
+
+ /${project.build.finalName}
+ 9080
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-app1/src/main/java/com/github/zhangkaitao/shiro/chapter23/app1/web/controller/HelloController.java b/shiro-example-chapter23-app1/src/main/java/com/github/zhangkaitao/shiro/chapter23/app1/web/controller/HelloController.java
new file mode 100644
index 00000000..955b4524
--- /dev/null
+++ b/shiro-example-chapter23-app1/src/main/java/com/github/zhangkaitao/shiro/chapter23/app1/web/controller/HelloController.java
@@ -0,0 +1,45 @@
+package com.github.zhangkaitao.shiro.chapter23.app1.web.controller;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresRoles;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+@Controller
+public class HelloController {
+
+ @RequestMapping("/hello")
+ public String hello() {
+ return "success";
+ }
+
+ @RequestMapping(value = "/attr", method = RequestMethod.POST)
+ public String setAttr(
+ @RequestParam("key") String key, @RequestParam("value") String value) {
+ SecurityUtils.getSubject().getSession().setAttribute(key, value);
+ return "success";
+ }
+
+
+ @RequestMapping(value = "/attr", method = RequestMethod.GET)
+ public String getAttr(
+ @RequestParam("key") String key, Model model) {
+ model.addAttribute("value", SecurityUtils.getSubject().getSession().getAttribute(key));
+ return "success";
+ }
+
+ @RequestMapping("/role1")
+ @RequiresRoles("role1")
+ public String role1() {
+ return "success";
+ }
+
+}
diff --git a/shiro-example-chapter23-app1/src/main/java/com/github/zhangkaitao/shiro/chapter23/app1/web/exception/DefaultExceptionHandler.java b/shiro-example-chapter23-app1/src/main/java/com/github/zhangkaitao/shiro/chapter23/app1/web/exception/DefaultExceptionHandler.java
new file mode 100644
index 00000000..0861787d
--- /dev/null
+++ b/shiro-example-chapter23-app1/src/main/java/com/github/zhangkaitao/shiro/chapter23/app1/web/exception/DefaultExceptionHandler.java
@@ -0,0 +1,31 @@
+package com.github.zhangkaitao.shiro.chapter23.app1.web.exception;
+
+import org.apache.shiro.authz.UnauthorizedException;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-12
+ *
Version: 1.0
+ */
+@ControllerAdvice
+public class DefaultExceptionHandler {
+ /**
+ * 没有权限 异常
+ *
+ * 后续根据不同的需求定制即可
+ */
+ @ExceptionHandler({UnauthorizedException.class})
+ @ResponseStatus(HttpStatus.UNAUTHORIZED)
+ public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
+ ModelAndView mv = new ModelAndView();
+ mv.addObject("exception", e);
+ mv.setViewName("unauthorized");
+ return mv;
+ }
+}
diff --git a/shiro-example-chapter23-app1/src/main/resources/client/shiro-client.properties b/shiro-example-chapter23-app1/src/main/resources/client/shiro-client.properties
new file mode 100644
index 00000000..557b6ff7
--- /dev/null
+++ b/shiro-example-chapter23-app1/src/main/resources/client/shiro-client.properties
@@ -0,0 +1,4 @@
+client.app.key=645ba612-370a-43a8-a8e0-993e7a590cf0
+client.success.url=/hello
+client.filter.chain.definitions=/hello=anon;/login=authc;/**=authc
+
diff --git a/shiro-example-chapter23-app1/src/main/resources/spring-mvc.xml b/shiro-example-chapter23-app1/src/main/resources/spring-mvc.xml
new file mode 100644
index 00000000..020e1c1d
--- /dev/null
+++ b/shiro-example-chapter23-app1/src/main/resources/spring-mvc.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-app1/src/main/webapp/WEB-INF/web.xml b/shiro-example-chapter23-app1/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..dc046b8f
--- /dev/null
+++ b/shiro-example-chapter23-app1/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+ contextConfigLocation
+
+ classpath:client/spring-client.xml
+
+
+
+ org.springframework.web.context.ContextLoaderListener
+
+
+
+
+
+
+ shiroFilter
+ org.springframework.web.filter.DelegatingFilterProxy
+ true
+
+ targetFilterLifecycle
+ true
+
+
+
+
+
+
+
+ shiroFilter
+ /*
+
+
+
+ spring
+ org.springframework.web.servlet.DispatcherServlet
+
+ contextConfigLocation
+ classpath:spring-mvc.xml
+
+ 1
+ true
+
+
+ spring
+ /
+
+
+
+
diff --git a/shiro-example-chapter23-app1/src/main/webapp/success.jsp b/shiro-example-chapter23-app1/src/main/webapp/success.jsp
new file mode 100644
index 00000000..88326cd8
--- /dev/null
+++ b/shiro-example-chapter23-app1/src/main/webapp/success.jsp
@@ -0,0 +1,38 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+ hello app1.
+
+
+ 点击登录
+
+
+
+ 欢迎 登录
+
+ 您拥有role1角色
+
+
+ 您没有role1角色
+
+
+ 您没有role2角色
+
+
+ 设置会话属性
+
+ 获取会话属性
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-app1/src/main/webapp/unauthorized.jsp b/shiro-example-chapter23-app1/src/main/webapp/unauthorized.jsp
new file mode 100644
index 00000000..c6758214
--- /dev/null
+++ b/shiro-example-chapter23-app1/src/main/webapp/unauthorized.jsp
@@ -0,0 +1,11 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+ 没有权限
+
+
+
+
+您没有权限[${exception.message}]
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-app2/pom.xml b/shiro-example-chapter23-app2/pom.xml
new file mode 100644
index 00000000..a52c0a56
--- /dev/null
+++ b/shiro-example-chapter23-app2/pom.xml
@@ -0,0 +1,57 @@
+
+
+
+ shiro-example-chapter23-pom
+ com.github.zhangkaitao
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ shiro-example-chapter23-app2
+
+
+
+ com.github.zhangkaitao
+ shiro-example-chapter23-client
+ 1.0-SNAPSHOT
+
+
+
+
+ chapter23-app2
+
+
+ org.mortbay.jetty
+ jetty-maven-plugin
+ 8.1.8.v20121106
+
+
+ /${project.build.finalName}
+
+
+
+ 10080
+ 60000
+
+
+
+
+
+
+
+ org.apache.tomcat.maven
+ tomcat7-maven-plugin
+ 2.2
+
+ /${project.build.finalName}
+ 10080
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-app2/src/main/java/com/github/zhangkaitao/shiro/chapter23/app2/web/controller/HelloController.java b/shiro-example-chapter23-app2/src/main/java/com/github/zhangkaitao/shiro/chapter23/app2/web/controller/HelloController.java
new file mode 100644
index 00000000..6b853bce
--- /dev/null
+++ b/shiro-example-chapter23-app2/src/main/java/com/github/zhangkaitao/shiro/chapter23/app2/web/controller/HelloController.java
@@ -0,0 +1,45 @@
+package com.github.zhangkaitao.shiro.chapter23.app2.web.controller;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresRoles;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+@Controller
+public class HelloController {
+
+ @RequestMapping("/hello")
+ public String hello() {
+ return "success";
+ }
+
+ @RequestMapping(value = "/attr", method = RequestMethod.POST)
+ public String setAttr(
+ @RequestParam("key") String key, @RequestParam("value") String value) {
+ SecurityUtils.getSubject().getSession().setAttribute(key, value);
+ return "success";
+ }
+
+
+ @RequestMapping(value = "/attr", method = RequestMethod.GET)
+ public String getAttr(
+ @RequestParam("key") String key, Model model) {
+ model.addAttribute("value", SecurityUtils.getSubject().getSession().getAttribute(key));
+ return "success";
+ }
+
+ @RequestMapping("/role2")
+ @RequiresRoles("role2")
+ public String role2() {
+ return "success";
+ }
+
+}
diff --git a/shiro-example-chapter23-app2/src/main/java/com/github/zhangkaitao/shiro/chapter23/app2/web/exception/DefaultExceptionHandler.java b/shiro-example-chapter23-app2/src/main/java/com/github/zhangkaitao/shiro/chapter23/app2/web/exception/DefaultExceptionHandler.java
new file mode 100644
index 00000000..569b4496
--- /dev/null
+++ b/shiro-example-chapter23-app2/src/main/java/com/github/zhangkaitao/shiro/chapter23/app2/web/exception/DefaultExceptionHandler.java
@@ -0,0 +1,31 @@
+package com.github.zhangkaitao.shiro.chapter23.app2.web.exception;
+
+import org.apache.shiro.authz.UnauthorizedException;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-12
+ *
Version: 1.0
+ */
+@ControllerAdvice
+public class DefaultExceptionHandler {
+ /**
+ * 没有权限 异常
+ *
+ * 后续根据不同的需求定制即可
+ */
+ @ExceptionHandler({UnauthorizedException.class})
+ @ResponseStatus(HttpStatus.UNAUTHORIZED)
+ public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
+ ModelAndView mv = new ModelAndView();
+ mv.addObject("exception", e);
+ mv.setViewName("unauthorized");
+ return mv;
+ }
+}
diff --git a/shiro-example-chapter23-app2/src/main/resources/client/shiro-client.properties b/shiro-example-chapter23-app2/src/main/resources/client/shiro-client.properties
new file mode 100644
index 00000000..8020f17a
--- /dev/null
+++ b/shiro-example-chapter23-app2/src/main/resources/client/shiro-client.properties
@@ -0,0 +1,4 @@
+client.app.key=645ba613-370a-43a8-a8e0-993e7a590cf0
+client.success.url=/hello
+client.filter.chain.definitions=/hello=anon;/login=authc;/**=authc
+
diff --git a/shiro-example-chapter23-app2/src/main/resources/spring-mvc.xml b/shiro-example-chapter23-app2/src/main/resources/spring-mvc.xml
new file mode 100644
index 00000000..020e1c1d
--- /dev/null
+++ b/shiro-example-chapter23-app2/src/main/resources/spring-mvc.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-app2/src/main/webapp/WEB-INF/web.xml b/shiro-example-chapter23-app2/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..dc046b8f
--- /dev/null
+++ b/shiro-example-chapter23-app2/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+ contextConfigLocation
+
+ classpath:client/spring-client.xml
+
+
+
+ org.springframework.web.context.ContextLoaderListener
+
+
+
+
+
+
+ shiroFilter
+ org.springframework.web.filter.DelegatingFilterProxy
+ true
+
+ targetFilterLifecycle
+ true
+
+
+
+
+
+
+
+ shiroFilter
+ /*
+
+
+
+ spring
+ org.springframework.web.servlet.DispatcherServlet
+
+ contextConfigLocation
+ classpath:spring-mvc.xml
+
+ 1
+ true
+
+
+ spring
+ /
+
+
+
+
diff --git a/shiro-example-chapter23-app2/src/main/webapp/success.jsp b/shiro-example-chapter23-app2/src/main/webapp/success.jsp
new file mode 100644
index 00000000..de8d2c5d
--- /dev/null
+++ b/shiro-example-chapter23-app2/src/main/webapp/success.jsp
@@ -0,0 +1,38 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+ hello app1.
+
+
+ 点击登录
+
+
+
+ 欢迎 登录
+
+ 您拥有role2角色
+
+
+ 您没有role2角色
+
+
+ 您没有role1角色
+
+
+ 设置会话属性
+
+ 获取会话属性
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-app2/src/main/webapp/unauthorized.jsp b/shiro-example-chapter23-app2/src/main/webapp/unauthorized.jsp
new file mode 100644
index 00000000..c6758214
--- /dev/null
+++ b/shiro-example-chapter23-app2/src/main/webapp/unauthorized.jsp
@@ -0,0 +1,11 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+ 没有权限
+
+
+
+
+您没有权限[${exception.message}]
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-client/pom.xml b/shiro-example-chapter23-client/pom.xml
new file mode 100644
index 00000000..9d0a79de
--- /dev/null
+++ b/shiro-example-chapter23-client/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ shiro-example-chapter23-pom
+ com.github.zhangkaitao
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ shiro-example-chapter23-client
+
+
+
+ com.github.zhangkaitao
+ shiro-example-chapter23-core
+ 1.0-SNAPSHOT
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientAuthenticationFilter.java b/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientAuthenticationFilter.java
new file mode 100644
index 00000000..dc37f24f
--- /dev/null
+++ b/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientAuthenticationFilter.java
@@ -0,0 +1,47 @@
+package com.github.zhangkaitao.shiro.chapter23.client;
+
+import com.github.zhangkaitao.shiro.chapter23.core.ClientSavedRequest;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.web.filter.authc.AuthenticationFilter;
+import org.apache.shiro.web.util.SavedRequest;
+import org.apache.shiro.web.util.WebUtils;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-3-14
+ *
Version: 1.0
+ */
+public class ClientAuthenticationFilter extends AuthenticationFilter {
+
+ @Override
+ protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+ Subject subject = getSubject(request, response);
+ return subject.isAuthenticated();
+ }
+
+ @Override
+ protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+ String successUrl = request.getParameter("backurl");
+ if(StringUtils.isEmpty(successUrl)) {
+ successUrl = getSuccessUrl();
+ }
+ saveRequest(request, successUrl);
+ redirectToLogin(request, response);
+ return false;
+ }
+
+ protected void saveRequest(ServletRequest request, String successUrl) {
+ Subject subject = SecurityUtils.getSubject();
+ Session session = subject.getSession();
+ HttpServletRequest httpRequest = WebUtils.toHttp(request);
+ SavedRequest savedRequest = new ClientSavedRequest(httpRequest, successUrl);
+ session.setAttribute(WebUtils.SAVED_REQUEST_KEY, savedRequest);
+ }
+}
diff --git a/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientRealm.java b/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientRealm.java
new file mode 100644
index 00000000..a6800a2b
--- /dev/null
+++ b/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientRealm.java
@@ -0,0 +1,47 @@
+package com.github.zhangkaitao.shiro.chapter23.client;
+
+import com.github.zhangkaitao.shiro.chapter23.remote.PermissionContext;
+import com.github.zhangkaitao.shiro.chapter23.remote.RemoteServiceInterface;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+public class ClientRealm extends AuthorizingRealm {
+
+ private RemoteServiceInterface remoteService;
+ private String appKey;
+
+ public void setRemoteService(RemoteServiceInterface remoteService) {
+ this.remoteService = remoteService;
+ }
+
+ public void setAppKey(String appKey) {
+ this.appKey = appKey;
+ }
+
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+ String username = (String) principals.getPrimaryPrincipal();
+ SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
+ PermissionContext context = remoteService.getPermissions(appKey, username);
+ authorizationInfo.setRoles(context.getRoles());
+ authorizationInfo.setStringPermissions(context.getPermissions());
+ return authorizationInfo;
+ }
+
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+ //永远不会被调用
+ return null;
+ }
+}
diff --git a/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientSessionDAO.java b/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientSessionDAO.java
new file mode 100644
index 00000000..af726627
--- /dev/null
+++ b/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientSessionDAO.java
@@ -0,0 +1,50 @@
+package com.github.zhangkaitao.shiro.chapter23.client;
+
+import com.github.zhangkaitao.shiro.chapter23.remote.RemoteServiceInterface;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
+
+import java.io.Serializable;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+public class ClientSessionDAO extends CachingSessionDAO {
+
+ private RemoteServiceInterface remoteService;
+ private String appKey;
+
+ public void setRemoteService(RemoteServiceInterface remoteService) {
+ this.remoteService = remoteService;
+ }
+
+ public void setAppKey(String appKey) {
+ this.appKey = appKey;
+ }
+
+
+ @Override
+ protected void doDelete(Session session) {
+ remoteService.deleteSession(appKey, session);
+ }
+
+ @Override
+ protected void doUpdate(Session session) {
+ remoteService.updateSession(appKey, session);
+ }
+
+
+ @Override
+ protected Serializable doCreate(Session session) {
+ Serializable sessionId = remoteService.createSession(session);
+ assignSessionId(session, sessionId);
+ return sessionId;
+ }
+
+ @Override
+ protected Session doReadSession(Serializable sessionId) {
+ return remoteService.getSession(appKey, sessionId);
+ }
+}
diff --git a/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientShiroFilterFactoryBean.java b/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientShiroFilterFactoryBean.java
new file mode 100644
index 00000000..e21b2606
--- /dev/null
+++ b/shiro-example-chapter23-client/src/main/java/com/github/zhangkaitao/shiro/chapter23/client/ClientShiroFilterFactoryBean.java
@@ -0,0 +1,44 @@
+package com.github.zhangkaitao.shiro.chapter23.client;
+
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.Filter;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+public class ClientShiroFilterFactoryBean extends ShiroFilterFactoryBean implements ApplicationContextAware {
+
+ private ApplicationContext applicationContext;
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ public void setFiltersStr(String filters) {
+ if(StringUtils.isEmpty(filters)) {
+ return;
+ }
+ String[] filterArray = filters.split(";");
+ for(String filter : filterArray) {
+ String[] o = filter.split("=");
+ getFilters().put(o[0], (Filter)applicationContext.getBean(o[1]));
+ }
+ }
+
+ public void setFilterChainDefinitionsStr(String filterChainDefinitions) {
+ if(StringUtils.isEmpty(filterChainDefinitions)) {
+ return;
+ }
+ String[] chainDefinitionsArray = filterChainDefinitions.split(";");
+ for(String filter : chainDefinitionsArray) {
+ String[] o = filter.split("=");
+ getFilterChainDefinitionMap().put(o[0], o[1]);
+ }
+ }
+}
diff --git a/shiro-example-chapter23-client/src/main/resources/client/shiro-client-default.properties b/shiro-example-chapter23-client/src/main/resources/client/shiro-client-default.properties
new file mode 100644
index 00000000..19a5033a
--- /dev/null
+++ b/shiro-example-chapter23-client/src/main/resources/client/shiro-client-default.properties
@@ -0,0 +1,20 @@
+client.app.key=
+#Զ̷URLַ
+client.remote.service.url=http://localhost/chapter23-server/remoteService
+#¼ַ
+client.login.url=http://localhost/chapter23-server/login
+client.success.url=/
+#δȨַ
+client.unauthorized.url=http://localhost/chapter23-server/unauthorized
+client.cookie.domain=
+client.cookie.path=/
+#cookieеsession id
+client.session.id=sid
+#cookieеremember me
+client.rememberMe.id=rememberMe
+client.session.timeout=1800000
+# name=filter-ref;name=filter-ref
+client.filters=
+# ʽ url=filters;url=filters
+client.filter.chain.definitions=/**=anon
+
diff --git a/shiro-example-chapter23-client/src/main/resources/client/spring-client-remote-service.xml b/shiro-example-chapter23-client/src/main/resources/client/spring-client-remote-service.xml
new file mode 100644
index 00000000..836dc960
--- /dev/null
+++ b/shiro-example-chapter23-client/src/main/resources/client/spring-client-remote-service.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-client/src/main/resources/client/spring-client-shiro.xml b/shiro-example-chapter23-client/src/main/resources/client/spring-client-shiro.xml
new file mode 100644
index 00000000..b1439e7f
--- /dev/null
+++ b/shiro-example-chapter23-client/src/main/resources/client/spring-client-shiro.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-client/src/main/resources/client/spring-client.xml b/shiro-example-chapter23-client/src/main/resources/client/spring-client.xml
new file mode 100644
index 00000000..09d4d22b
--- /dev/null
+++ b/shiro-example-chapter23-client/src/main/resources/client/spring-client.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-core/pom.xml b/shiro-example-chapter23-core/pom.xml
new file mode 100644
index 00000000..abc4c1f1
--- /dev/null
+++ b/shiro-example-chapter23-core/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ shiro-example-chapter23-pom
+ com.github.zhangkaitao
+ 1.0-SNAPSHOT
+
+ 4.0.0
+ com.github.zhangkaitao
+ shiro-example-chapter23-core
+ 1.0-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/core/ClientSavedRequest.java b/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/core/ClientSavedRequest.java
new file mode 100644
index 00000000..fa9c23d9
--- /dev/null
+++ b/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/core/ClientSavedRequest.java
@@ -0,0 +1,75 @@
+package com.github.zhangkaitao.shiro.chapter23.core;
+
+import org.apache.shiro.web.util.SavedRequest;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-3-14
+ *
Version: 1.0
+ */
+public class ClientSavedRequest extends SavedRequest {
+ private String scheme;
+ private String domain;
+ private int port;
+ private String contextPath;
+ private String successUrl;
+
+ public ClientSavedRequest(HttpServletRequest request, String successUrl) {
+ super(request);
+ this.scheme = request.getScheme();
+ this.domain = request.getServerName();
+ this.port = request.getServerPort();
+ this.successUrl = successUrl;
+ this.contextPath = request.getContextPath();
+ }
+
+ public String getScheme() {
+ return scheme;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getContextPath() {
+ return contextPath;
+ }
+
+ public String getSuccessUrl() {
+ return successUrl;
+ }
+
+ public String getRequestUrl() {
+ System.out.println(successUrl);
+ String requestURI = getRequestURI();
+ if(successUrl != null) {
+ if(successUrl.toLowerCase().startsWith("http://") || successUrl.toLowerCase().startsWith("https://")) {
+ return successUrl;
+ } else if(!successUrl.startsWith(contextPath)) {
+ requestURI = contextPath + successUrl;
+ } else {
+ requestURI = successUrl;
+ }
+ }
+
+ StringBuilder requestUrl = new StringBuilder(getScheme());
+ requestUrl.append("://");
+ requestUrl.append(getDomain());
+ if("http".equalsIgnoreCase(getScheme()) && getPort() != 80) {
+ requestUrl.append(String.valueOf(port));
+ } else if("https".equalsIgnoreCase(getScheme()) && getPort() != 443) {
+ requestUrl.append(String.valueOf(port));
+ }
+ requestUrl.append(requestURI);
+ if (successUrl == null && getQueryString() != null) {
+ requestUrl.append("?").append(getQueryString());
+ }
+ return requestUrl.toString();
+ }
+}
diff --git a/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/PermissionContext.java b/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/PermissionContext.java
new file mode 100644
index 00000000..9808eab5
--- /dev/null
+++ b/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/PermissionContext.java
@@ -0,0 +1,39 @@
+package com.github.zhangkaitao.shiro.chapter23.remote;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+public class PermissionContext implements Serializable {
+ private Set roles;
+ private Set permissions;
+
+ public Set getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Set roles) {
+ this.roles = roles;
+ }
+
+ public Set getPermissions() {
+ return permissions;
+ }
+
+ public void setPermissions(Set permissions) {
+ this.permissions = permissions;
+ }
+
+
+ @Override
+ public String toString() {
+ return "PermissionContext{" +
+ ", roles=" + roles +
+ ", permissions=" + permissions +
+ '}';
+ }
+}
diff --git a/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/RemoteServiceInterface.java b/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/RemoteServiceInterface.java
new file mode 100644
index 00000000..b4a41c12
--- /dev/null
+++ b/shiro-example-chapter23-core/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/RemoteServiceInterface.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2005-2012 https://github.com/zhangkaitao
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+package com.github.zhangkaitao.shiro.chapter23.remote;
+
+import org.apache.shiro.session.Session;
+
+import java.io.Serializable;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+public interface RemoteServiceInterface {
+
+ public Session getSession(String appKey, Serializable sessionId);
+ public void updateSession(String appKey, Session session);
+ public void deleteSession(String appKey, Session session);
+
+ public PermissionContext getPermissions(String appKey, String username);
+
+ Serializable createSession(Session session);
+}
diff --git a/shiro-example-chapter23-pom/pom.xml b/shiro-example-chapter23-pom/pom.xml
new file mode 100644
index 00000000..4f585b5d
--- /dev/null
+++ b/shiro-example-chapter23-pom/pom.xml
@@ -0,0 +1,139 @@
+
+
+
+ 4.0.0
+ com.github.zhangkaitao
+ shiro-example-chapter23-pom
+ 1.0-SNAPSHOT
+ pom
+
+
+
+
+ junit
+ junit
+ 4.9
+ test
+
+
+ org.springframework
+ spring-test
+ 4.0.0.RELEASE
+ test
+
+
+
+ commons-logging
+ commons-logging
+ 1.1.3
+
+
+ commons-collections
+ commons-collections
+ 3.2.1
+
+
+
+
+ org.apache.shiro
+ shiro-core
+ 1.2.2
+
+
+
+ org.apache.shiro
+ shiro-web
+ 1.2.2
+
+
+
+ org.apache.shiro
+ shiro-ehcache
+ 1.2.2
+
+
+
+ org.apache.shiro
+ shiro-quartz
+ 1.2.2
+
+
+
+ org.apache.shiro
+ shiro-spring
+ 1.2.2
+
+
+
+
+ org.apache.shiro
+ shiro-core
+ 1.2.2
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.0.1
+ provided
+
+
+ javax.servlet.jsp
+ jsp-api
+ 2.2
+
+
+ javax.servlet
+ jstl
+ 1.2
+
+
+
+
+
+ org.aspectj
+ aspectjrt
+ 1.7.4
+
+
+ org.aspectj
+ aspectjweaver
+ 1.7.4
+
+
+
+ org.springframework
+ spring-context
+ 4.0.0.RELEASE
+
+
+
+ org.springframework
+ spring-aop
+ 4.0.0.RELEASE
+
+
+
+
+ org.springframework
+ spring-jdbc
+ 4.0.0.RELEASE
+
+
+
+ org.springframework
+ spring-web
+ 4.0.0.RELEASE
+
+
+
+ org.springframework
+ spring-webmvc
+ 4.0.0.RELEASE
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/pom.xml b/shiro-example-chapter23-server/pom.xml
new file mode 100644
index 00000000..152b3313
--- /dev/null
+++ b/shiro-example-chapter23-server/pom.xml
@@ -0,0 +1,65 @@
+
+
+
+ shiro-example-chapter23-pom
+ com.github.zhangkaitao
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ shiro-example-chapter23-server
+
+
+
+
+
+ mysql
+ mysql-connector-java
+ 5.1.25
+
+
+ com.alibaba
+ druid
+ 0.2.23
+
+
+
+ com.github.zhangkaitao
+ shiro-example-chapter23-core
+ 1.0-SNAPSHOT
+
+
+
+
+
+ chapter23-server
+
+
+ org.mortbay.jetty
+ jetty-maven-plugin
+ 8.1.8.v20121106
+
+
+ /${project.build.finalName}
+
+
+
+
+
+ org.apache.tomcat.maven
+ tomcat7-maven-plugin
+ 2.2
+
+ /${project.build.finalName}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/Constants.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/Constants.java
new file mode 100644
index 00000000..bec84223
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/Constants.java
@@ -0,0 +1,12 @@
+package com.github.zhangkaitao.shiro.chapter23;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-15
+ *
Version: 1.0
+ */
+public class Constants {
+ public static final String CURRENT_USER = "user";
+
+ public static final String SERVER_APP_KEY = "645ba616-370a-43a8-a8e0-993e7a590cf0";
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/credentials/RetryLimitHashedCredentialsMatcher.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/credentials/RetryLimitHashedCredentialsMatcher.java
new file mode 100644
index 00000000..c609a937
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/credentials/RetryLimitHashedCredentialsMatcher.java
@@ -0,0 +1,46 @@
+package com.github.zhangkaitao.shiro.chapter23.credentials;
+
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.ExcessiveAttemptsException;
+import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheManager;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
+
+ private Cache passwordRetryCache;
+
+ public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
+ passwordRetryCache = cacheManager.getCache("passwordRetryCache");
+ }
+
+ @Override
+ public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
+ String username = (String)token.getPrincipal();
+ //retry count + 1
+ AtomicInteger retryCount = passwordRetryCache.get(username);
+ if(retryCount == null) {
+ retryCount = new AtomicInteger(0);
+ passwordRetryCache.put(username, retryCount);
+ }
+ if(retryCount.incrementAndGet() > 5) {
+ //if retry count > 5 throw
+ throw new ExcessiveAttemptsException();
+ }
+
+ boolean matches = super.doCredentialsMatch(token, info);
+ if(matches) {
+ //clear retry count
+ passwordRetryCache.remove(username);
+ }
+ return matches;
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AppDao.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AppDao.java
new file mode 100644
index 00000000..c7a61205
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AppDao.java
@@ -0,0 +1,22 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.App;
+
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface AppDao {
+
+ public App createApp(App app);
+ public App updateApp(App app);
+ public void deleteApp(Long appId);
+
+ public App findOne(Long appId);
+ public List findAll();
+
+ Long findAppIdByAppKey(String appKey);
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AppDaoImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AppDaoImpl.java
new file mode 100644
index 00000000..d8a6608b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AppDaoImpl.java
@@ -0,0 +1,87 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.App;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * App: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Repository
+public class AppDaoImpl implements AppDao {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ public App createApp(final App app) {
+ final String sql = "insert into sys_app(name, app_key, app_secret, available) values(?,?,?,?)";
+
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ jdbcTemplate.update(new PreparedStatementCreator() {
+ @Override
+ public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+ PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
+ int count = 1;
+ psst.setString(count++, app.getName());
+ psst.setString(count++, app.getAppKey());
+ psst.setString(count++, app.getAppSecret());
+ psst.setBoolean(count++, app.getAvailable());
+ return psst;
+ }
+ }, keyHolder);
+ app.setId(keyHolder.getKey().longValue());
+ return app;
+ }
+
+ @Override
+ public App updateApp(App app) {
+ final String sql = "update sys_app set name=?, app_key=?, app_secret=?, available=? where id=?";
+ jdbcTemplate.update(
+ sql,
+ app.getName(), app.getAppKey(), app.getAppSecret(), app.getAvailable(), app.getId());
+ return app;
+ }
+
+ public void deleteApp(Long appId) {
+ final String sql = "delete from sys_app where id=?";
+ jdbcTemplate.update(sql, appId);
+ }
+
+
+ @Override
+ public App findOne(Long appId) {
+ final String sql = "select id, name, app_key, app_secret, available from sys_app where id=?";
+ List appList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(App.class), appId);
+ if(appList.size() == 0) {
+ return null;
+ }
+ return appList.get(0);
+ }
+
+ @Override
+ public List findAll() {
+ final String sql = "select id, name, app_key, app_secret, available from sys_app";
+ return jdbcTemplate.query(sql, new BeanPropertyRowMapper(App.class));
+ }
+
+ @Override
+ public Long findAppIdByAppKey(String appKey) {
+ final String sql = "select id from sys_app where app_key=?";
+ List appIdList = jdbcTemplate.queryForList(sql, Long.class, appKey);
+ if(appIdList.size() == 0) {
+ return null;
+ }
+ return appIdList.get(0);
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AuthorizationDao.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AuthorizationDao.java
new file mode 100644
index 00000000..9f3220ba
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AuthorizationDao.java
@@ -0,0 +1,22 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Authorization;
+
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface AuthorizationDao {
+
+ public Authorization createAuthorization(Authorization authorization);
+ public Authorization updateAuthorization(Authorization authorization);
+ public void deleteAuthorization(Long authorizationId);
+
+ public Authorization findOne(Long authorizationId);
+ public List findAll();
+
+ public Authorization findByAppUser(Long appId, Long userId);
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AuthorizationDaoImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AuthorizationDaoImpl.java
new file mode 100644
index 00000000..e6dfe920
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/AuthorizationDaoImpl.java
@@ -0,0 +1,88 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Authorization;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * Authorization: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Repository
+public class AuthorizationDaoImpl implements AuthorizationDao {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ public Authorization createAuthorization(final Authorization authorization) {
+
+ final String sql = "insert into sys_user_app_roles(user_id, app_id, role_ids) values(?,?,?)";
+
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ jdbcTemplate.update(new PreparedStatementCreator() {
+ @Override
+ public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+ PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
+ int count = 1;
+ psst.setLong(count++, authorization.getUserId());
+ psst.setLong(count++, authorization.getAppId());
+ psst.setString(count++, authorization.getRoleIdsStr());
+ return psst;
+ }
+ }, keyHolder);
+ authorization.setId(keyHolder.getKey().longValue());
+ return authorization;
+ }
+
+ @Override
+ public Authorization updateAuthorization(Authorization authorization) {
+ final String sql = "update sys_user_app_roles set user_id=?, app_id=?, role_ids=? where id=?";
+ jdbcTemplate.update(
+ sql,
+ authorization.getUserId(), authorization.getAppId(), authorization.getRoleIdsStr(), authorization.getId());
+ return authorization;
+ }
+
+ public void deleteAuthorization(Long authorizationId) {
+ final String sql = "delete from sys_user_app_roles where id=?";
+ jdbcTemplate.update(sql, authorizationId);
+ }
+
+
+ @Override
+ public Authorization findOne(Long authorizationId) {
+ final String sql = "select id, user_id, app_id, role_ids as roleIdsStr from sys_user_app_roles where id=?";
+ List authorizationList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Authorization.class), authorizationId);
+ if(authorizationList.size() == 0) {
+ return null;
+ }
+ return authorizationList.get(0);
+ }
+
+ @Override
+ public List findAll() {
+ final String sql = "select id, user_id, app_id, role_ids as roleIdsStr from sys_user_app_roles";
+ return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Authorization.class));
+ }
+
+
+ @Override
+ public Authorization findByAppUser(Long appId, Long userId) {
+ final String sql = "select id, user_id, app_id, role_ids as roleIdsStr from sys_user_app_roles where app_id=? and user_id=?";
+ List authorizationList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Authorization.class), appId, userId);
+ if(authorizationList.size() == 0) {
+ return null;
+ }
+ return authorizationList.get(0);
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/OrganizationDao.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/OrganizationDao.java
new file mode 100644
index 00000000..bbb12a57
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/OrganizationDao.java
@@ -0,0 +1,24 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Organization;
+
+import java.util.List;
+
+/**
+ * Organization: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface OrganizationDao {
+
+ public Organization createOrganization(Organization organization);
+ public Organization updateOrganization(Organization organization);
+ public void deleteOrganization(Long organizationId);
+
+ Organization findOne(Long organizationId);
+ List findAll();
+
+ List findAllWithExclude(Organization excludeOraganization);
+
+ void move(Organization source, Organization target);
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/OrganizationDaoImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/OrganizationDaoImpl.java
new file mode 100644
index 00000000..5eb73bf1
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/OrganizationDaoImpl.java
@@ -0,0 +1,95 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Organization;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * Organization: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Repository
+public class OrganizationDaoImpl implements OrganizationDao {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ public Organization createOrganization(final Organization organization) {
+ final String sql = "insert into sys_organization( name, parent_id, parent_ids, available) values(?,?,?,?)";
+
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ jdbcTemplate.update(new PreparedStatementCreator() {
+ @Override
+ public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+ PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
+ int count = 1;
+ psst.setString(count++, organization.getName());
+ psst.setLong(count++, organization.getParentId());
+ psst.setString(count++, organization.getParentIds());
+ psst.setBoolean(count++, organization.getAvailable());
+ return psst;
+ }
+ }, keyHolder);
+ organization.setId(keyHolder.getKey().longValue());
+ return organization;
+ }
+
+ @Override
+ public Organization updateOrganization(Organization organization) {
+ final String sql = "update sys_organization set name=?, parent_id=?, parent_ids=?, available=? where id=?";
+ jdbcTemplate.update(
+ sql,
+ organization.getName(), organization.getParentId(), organization.getParentIds(), organization.getAvailable(), organization.getId());
+ return organization;
+ }
+
+ public void deleteOrganization(Long organizationId) {
+ Organization organization = findOne(organizationId);
+ final String deleteSelfSql = "delete from sys_organization where id=?";
+ jdbcTemplate.update(deleteSelfSql, organizationId);
+ final String deleteDescendantsSql = "delete from sys_organization where parent_ids like ?";
+ jdbcTemplate.update(deleteDescendantsSql, organization.makeSelfAsParentIds() + "%");
+ }
+
+
+ @Override
+ public Organization findOne(Long organizationId) {
+ final String sql = "select id, name, parent_id, parent_ids, available from sys_organization where id=?";
+ List organizationList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Organization.class), organizationId);
+ if(organizationList.size() == 0) {
+ return null;
+ }
+ return organizationList.get(0);
+ }
+
+ @Override
+ public List findAll() {
+ final String sql = "select id, name, parent_id, parent_ids, available from sys_organization";
+ return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Organization.class));
+ }
+
+ @Override
+ public List findAllWithExclude(Organization excludeOraganization) {
+ //TODO 改成not exists 利用索引
+ final String sql = "select id, name, parent_id, parent_ids, available from sys_organization where id!=? and parent_ids not like ?";
+ return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Organization.class), excludeOraganization.getId(), excludeOraganization.makeSelfAsParentIds() + "%");
+ }
+
+ @Override
+ public void move(Organization source, Organization target) {
+ String moveSourceSql = "update sys_organization set parent_id=?,parent_ids=? where id=?";
+ jdbcTemplate.update(moveSourceSql, target.getId(), target.getParentIds(), source.getId());
+ String moveSourceDescendantsSql = "update sys_organization set parent_ids=concat(?, substring(parent_ids, length(?))) where parent_ids like ?";
+ jdbcTemplate.update(moveSourceDescendantsSql, target.makeSelfAsParentIds(), source.makeSelfAsParentIds(), source.makeSelfAsParentIds() + "%");
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/ResourceDao.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/ResourceDao.java
new file mode 100644
index 00000000..1c55fca3
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/ResourceDao.java
@@ -0,0 +1,21 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Resource;
+
+import java.util.List;
+
+/**
+ * Resource: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface ResourceDao {
+
+ public Resource createResource(Resource resource);
+ public Resource updateResource(Resource resource);
+ public void deleteResource(Long resourceId);
+
+ Resource findOne(Long resourceId);
+ List findAll();
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/ResourceDaoImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/ResourceDaoImpl.java
new file mode 100644
index 00000000..1ec0a4c3
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/ResourceDaoImpl.java
@@ -0,0 +1,83 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Resource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * Resource: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Repository
+public class ResourceDaoImpl implements ResourceDao {
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ public Resource createResource(final Resource resource) {
+ final String sql = "insert into sys_resource(name, type, url, permission, parent_id, parent_ids, available) values(?,?,?,?,?,?,?)";
+
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ jdbcTemplate.update(new PreparedStatementCreator() {
+ @Override
+ public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+ PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
+ int count = 1;
+ psst.setString(count++, resource.getName());
+ psst.setString(count++, resource.getType().name());
+ psst.setString(count++, resource.getUrl());
+ psst.setString(count++, resource.getPermission());
+ psst.setLong(count++, resource.getParentId());
+ psst.setString(count++, resource.getParentIds());
+ psst.setBoolean(count++, resource.getAvailable());
+ return psst;
+ }
+ }, keyHolder);
+ resource.setId(keyHolder.getKey().longValue());
+ return resource;
+ }
+
+ @Override
+ public Resource updateResource(Resource resource) {
+ final String sql = "update sys_resource set name=?, type=?, url=?, permission=?, parent_id=?, parent_ids=?, available=? where id=?";
+ jdbcTemplate.update(
+ sql,
+ resource.getName(), resource.getType().name(), resource.getUrl(), resource.getPermission(), resource.getParentId(), resource.getParentIds(), resource.getAvailable(), resource.getId());
+ return resource;
+ }
+
+ public void deleteResource(Long resourceId) {
+ Resource resource = findOne(resourceId);
+ final String deleteSelfSql = "delete from sys_resource where id=?";
+ jdbcTemplate.update(deleteSelfSql, resourceId);
+ final String deleteDescendantsSql = "delete from sys_resource where parent_ids like ?";
+ jdbcTemplate.update(deleteDescendantsSql, resource.makeSelfAsParentIds() + "%");
+ }
+
+
+ @Override
+ public Resource findOne(Long resourceId) {
+ final String sql = "select id, name, type, url, permission, parent_id, parent_ids, available from sys_resource where id=?";
+ List resourceList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Resource.class), resourceId);
+ if(resourceList.size() == 0) {
+ return null;
+ }
+ return resourceList.get(0);
+ }
+
+ @Override
+ public List findAll() {
+ final String sql = "select id, name, type, url, permission, parent_id, parent_ids, available from sys_resource order by concat(parent_ids, id) asc";
+ return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Resource.class));
+ }
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/RoleDao.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/RoleDao.java
new file mode 100644
index 00000000..9beb5423
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/RoleDao.java
@@ -0,0 +1,20 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Role;
+
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface RoleDao {
+
+ public Role createRole(Role role);
+ public Role updateRole(Role role);
+ public void deleteRole(Long roleId);
+
+ public Role findOne(Long roleId);
+ public List findAll();
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/RoleDaoImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/RoleDaoImpl.java
new file mode 100644
index 00000000..0ff8ee19
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/RoleDaoImpl.java
@@ -0,0 +1,78 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Role;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * Role: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Repository
+public class RoleDaoImpl implements RoleDao {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ public Role createRole(final Role role) {
+ final String sql = "insert into sys_role(role, description, resource_ids, available) values(?,?,?,?)";
+
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ jdbcTemplate.update(new PreparedStatementCreator() {
+ @Override
+ public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+ PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
+ int count = 1;
+ psst.setString(count++, role.getRole());
+ psst.setString(count++, role.getDescription());
+ psst.setString(count++, role.getResourceIdsStr());
+ psst.setBoolean(count++, role.getAvailable());
+ return psst;
+ }
+ }, keyHolder);
+ role.setId(keyHolder.getKey().longValue());
+ return role;
+ }
+
+ @Override
+ public Role updateRole(Role role) {
+ final String sql = "update sys_role set role=?, description=?, resource_ids=?, available=? where id=?";
+ jdbcTemplate.update(
+ sql,
+ role.getRole(), role.getDescription(), role.getResourceIdsStr(), role.getAvailable(), role.getId());
+ return role;
+ }
+
+ public void deleteRole(Long roleId) {
+ final String sql = "delete from sys_role where id=?";
+ jdbcTemplate.update(sql, roleId);
+ }
+
+
+ @Override
+ public Role findOne(Long roleId) {
+ final String sql = "select id, role, description, resource_ids as resourceIdsStr, available from sys_role where id=?";
+ List roleList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Role.class), roleId);
+ if(roleList.size() == 0) {
+ return null;
+ }
+ return roleList.get(0);
+ }
+
+ @Override
+ public List findAll() {
+ final String sql = "select id, role, description, resource_ids as resourceIdsStr, available from sys_role";
+ return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Role.class));
+ }
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/UserDao.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/UserDao.java
new file mode 100644
index 00000000..f977e29f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/UserDao.java
@@ -0,0 +1,24 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.User;
+
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface UserDao {
+
+ public User createUser(User user);
+ public User updateUser(User user);
+ public void deleteUser(Long userId);
+
+ User findOne(Long userId);
+
+ List findAll();
+
+ User findByUsername(String username);
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/UserDaoImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/UserDaoImpl.java
new file mode 100644
index 00000000..dbbcc4b9
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/dao/UserDaoImpl.java
@@ -0,0 +1,88 @@
+package com.github.zhangkaitao.shiro.chapter23.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.stereotype.Repository;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Repository
+public class UserDaoImpl implements UserDao {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ public User createUser(final User user) {
+ final String sql = "insert into sys_user(organization_id, username, password, salt, locked) values(?,?,?,?,?,?)";
+
+ GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
+ jdbcTemplate.update(new PreparedStatementCreator() {
+ @Override
+ public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+ PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
+ int count = 1;
+ psst.setLong(count++, user.getOrganizationId());
+ psst.setString(count++, user.getUsername());
+ psst.setString(count++, user.getPassword());
+ psst.setString(count++, user.getSalt());
+ psst.setBoolean(count++, user.getLocked());
+ return psst;
+ }
+ }, keyHolder);
+
+ user.setId(keyHolder.getKey().longValue());
+ return user;
+ }
+
+ public User updateUser(User user) {
+ String sql = "update sys_user set organization_id=?,username=?, password=?, salt=?, locked=? where id=?";
+ jdbcTemplate.update(
+ sql,
+ user.getOrganizationId(), user.getUsername(), user.getPassword(), user.getSalt(), user.getLocked(), user.getId());
+ return user;
+ }
+
+ public void deleteUser(Long userId) {
+ String sql = "delete from sys_user where id=?";
+ jdbcTemplate.update(sql, userId);
+ }
+
+ @Override
+ public User findOne(Long userId) {
+ String sql = "select id, organization_id, username, password, salt, locked from sys_user where id=?";
+ List userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class), userId);
+ if(userList.size() == 0) {
+ return null;
+ }
+ return userList.get(0);
+ }
+
+ @Override
+ public List findAll() {
+ String sql = "select id, organization_id, username, password, salt, locked from sys_user";
+ return jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class));
+ }
+
+
+ @Override
+ public User findByUsername(String username) {
+ String sql = "select id, organization_id, username, password, salt, locked from sys_user where username=?";
+ List userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class), username);
+ if(userList.size() == 0) {
+ return null;
+ }
+ return userList.get(0);
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/App.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/App.java
new file mode 100644
index 00000000..0b3b87fb
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/App.java
@@ -0,0 +1,84 @@
+package com.github.zhangkaitao.shiro.chapter23.entity;
+
+import java.io.Serializable;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+public class App implements Serializable {
+ private Long id;
+ private String name;
+ private String appKey;
+ private String appSecret;
+ private Boolean available = Boolean.FALSE;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getAppKey() {
+ return appKey;
+ }
+
+ public void setAppKey(String appKey) {
+ this.appKey = appKey;
+ }
+
+ public String getAppSecret() {
+ return appSecret;
+ }
+
+ public void setAppSecret(String appSecret) {
+ this.appSecret = appSecret;
+ }
+
+ public Boolean getAvailable() {
+ return available;
+ }
+
+ public void setAvailable(Boolean available) {
+ this.available = available;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ App app = (App) o;
+
+ if (id != null ? !id.equals(app.id) : app.id != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "App{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", appKey='" + appKey + '\'' +
+ ", appSecret='" + appSecret + '\'' +
+ ", available=" + available +
+ '}';
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Authorization.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Authorization.java
new file mode 100644
index 00000000..b3358ca2
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Authorization.java
@@ -0,0 +1,107 @@
+package com.github.zhangkaitao.shiro.chapter23.entity;
+
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+public class Authorization implements Serializable {
+ private Long id;
+ private Long userId;
+ private Long appId;
+ private List roleIds;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Long userId) {
+ this.userId = userId;
+ }
+
+ public Long getAppId() {
+ return appId;
+ }
+
+ public void setAppId(Long appId) {
+ this.appId = appId;
+ }
+ public List getRoleIds() {
+ if(roleIds == null) {
+ roleIds = new ArrayList();
+ }
+ return roleIds;
+ }
+
+ public void setRoleIds(List roleIds) {
+ this.roleIds = roleIds;
+ }
+
+
+ public String getRoleIdsStr() {
+ if(CollectionUtils.isEmpty(roleIds)) {
+ return "";
+ }
+ StringBuilder s = new StringBuilder();
+ for(Long roleId : roleIds) {
+ s.append(roleId);
+ s.append(",");
+ }
+ return s.toString();
+ }
+
+ public void setRoleIdsStr(String roleIdsStr) {
+ if(StringUtils.isEmpty(roleIdsStr)) {
+ return;
+ }
+ String[] roleIdStrs = roleIdsStr.split(",");
+ for(String roleIdStr : roleIdStrs) {
+ if(StringUtils.isEmpty(roleIdStr)) {
+ continue;
+ }
+ getRoleIds().add(Long.valueOf(roleIdStr));
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Authorization that = (Authorization) o;
+
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Authorization{" +
+ "id=" + id +
+ ", userId=" + userId +
+ ", appId=" + appId +
+ ", roleIds=" + roleIds +
+ '}';
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Organization.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Organization.java
new file mode 100644
index 00000000..dfcdea57
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Organization.java
@@ -0,0 +1,93 @@
+package com.github.zhangkaitao.shiro.chapter23.entity;
+
+import java.io.Serializable;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public class Organization implements Serializable {
+ private Long id; //编号
+ private String name; //组织机构名称
+ private Long parentId; //父编号
+ private String parentIds; //父编号列表,如1/2/
+ private Boolean available = Boolean.FALSE;
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Long getParentId() {
+ return parentId;
+ }
+
+ public void setParentId(Long parentId) {
+ this.parentId = parentId;
+ }
+
+ public String getParentIds() {
+ return parentIds;
+ }
+
+ public void setParentIds(String parentIds) {
+ this.parentIds = parentIds;
+ }
+
+ public Boolean getAvailable() {
+ return available;
+ }
+
+ public void setAvailable(Boolean available) {
+ this.available = available;
+ }
+
+ public boolean isRootNode() {
+ return parentId == 0;
+ }
+
+ public String makeSelfAsParentIds() {
+ return getParentIds() + getId() + "/";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Organization that = (Organization) o;
+
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Organization{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", parentId=" + parentId +
+ ", parentIds='" + parentIds + '\'' +
+ ", available=" + available +
+ '}';
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Resource.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Resource.java
new file mode 100644
index 00000000..af7041f6
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Resource.java
@@ -0,0 +1,135 @@
+package com.github.zhangkaitao.shiro.chapter23.entity;
+
+import java.io.Serializable;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public class Resource implements Serializable {
+ private Long id; //编号
+ private String name; //资源名称
+ private ResourceType type = ResourceType.menu; //资源类型
+ private String url; //资源路径
+ private String permission; //权限字符串
+ private Long parentId; //父编号
+ private String parentIds; //父编号列表
+ private Boolean available = Boolean.FALSE;
+
+ public static enum ResourceType {
+ menu("菜单"), button("按钮");
+
+ private final String info;
+ private ResourceType(String info) {
+ this.info = info;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+ }
+
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ResourceType getType() {
+ return type;
+ }
+
+ public void setType(ResourceType type) {
+ this.type = type;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getPermission() {
+ return permission;
+ }
+
+ public void setPermission(String permission) {
+ this.permission = permission;
+ }
+
+ public Long getParentId() {
+ return parentId;
+ }
+
+ public void setParentId(Long parentId) {
+ this.parentId = parentId;
+ }
+
+ public String getParentIds() {
+ return parentIds;
+ }
+
+ public void setParentIds(String parentIds) {
+ this.parentIds = parentIds;
+ }
+
+ public Boolean getAvailable() {
+ return available;
+ }
+
+ public void setAvailable(Boolean available) {
+ this.available = available;
+ }
+
+ public boolean isRootNode() {
+ return parentId == 0;
+ }
+
+ public String makeSelfAsParentIds() {
+ return getParentIds() + getId() + "/";
+ }
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Resource resource = (Resource) o;
+
+ if (id != null ? !id.equals(resource.id) : resource.id != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Resource{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", type=" + type +
+ ", permission='" + permission + '\'' +
+ ", parentId=" + parentId +
+ ", parentIds='" + parentIds + '\'' +
+ ", available=" + available +
+ '}';
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Role.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Role.java
new file mode 100644
index 00000000..e8c8f2f4
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/Role.java
@@ -0,0 +1,126 @@
+package com.github.zhangkaitao.shiro.chapter23.entity;
+
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public class Role implements Serializable {
+ private Long id; //编号
+ private String role; //角色标识 程序中判断使用,如"admin"
+ private String description; //角色描述,UI界面显示使用
+ private List resourceIds; //拥有的资源
+ private Boolean available = Boolean.FALSE; //是否可用,如果不可用将不会添加给用户
+
+ public Role() {
+ }
+
+ public Role(String role, String description, Boolean available) {
+ this.role = role;
+ this.description = description;
+ this.available = available;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public List getResourceIds() {
+ if(resourceIds == null) {
+ resourceIds = new ArrayList();
+ }
+ return resourceIds;
+ }
+
+ public void setResourceIds(List resourceIds) {
+ this.resourceIds = resourceIds;
+ }
+
+ public String getResourceIdsStr() {
+ if(CollectionUtils.isEmpty(resourceIds)) {
+ return "";
+ }
+ StringBuilder s = new StringBuilder();
+ for(Long resourceId : resourceIds) {
+ s.append(resourceId);
+ s.append(",");
+ }
+ return s.toString();
+ }
+
+ public void setResourceIdsStr(String resourceIdsStr) {
+ if(StringUtils.isEmpty(resourceIdsStr)) {
+ return;
+ }
+ String[] resourceIdStrs = resourceIdsStr.split(",");
+ for(String resourceIdStr : resourceIdStrs) {
+ if(StringUtils.isEmpty(resourceIdStr)) {
+ continue;
+ }
+ getResourceIds().add(Long.valueOf(resourceIdStr));
+ }
+ }
+
+ public Boolean getAvailable() {
+ return available;
+ }
+
+ public void setAvailable(Boolean available) {
+ this.available = available;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Role role = (Role) o;
+
+ if (id != null ? !id.equals(role.id) : role.id != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Role{" +
+ "id=" + id +
+ ", role='" + role + '\'' +
+ ", description='" + description + '\'' +
+ ", resourceIds=" + resourceIds +
+ ", available=" + available +
+ '}';
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/User.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/User.java
new file mode 100644
index 00000000..8a6017e8
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/entity/User.java
@@ -0,0 +1,113 @@
+package com.github.zhangkaitao.shiro.chapter23.entity;
+
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public class User implements Serializable {
+ private Long id; //编号
+ private Long organizationId; //所属公司
+ private String username; //用户名
+ private String password; //密码
+ private String salt; //加密密码的盐
+ private Boolean locked = Boolean.FALSE;
+
+ public User() {
+ }
+
+ public User(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getOrganizationId() {
+ return organizationId;
+ }
+
+ public void setOrganizationId(Long organizationId) {
+ this.organizationId = organizationId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getSalt() {
+ return salt;
+ }
+
+ public void setSalt(String salt) {
+ this.salt = salt;
+ }
+
+ public String getCredentialsSalt() {
+ return username + salt;
+ }
+
+
+
+ public Boolean getLocked() {
+ return locked;
+ }
+
+ public void setLocked(Boolean locked) {
+ this.locked = locked;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ User user = (User) o;
+
+ if (id != null ? !id.equals(user.id) : user.id != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "User{" +
+ "id=" + id +
+ ", organizationId=" + organizationId +
+ ", username='" + username + '\'' +
+ ", password='" + password + '\'' +
+ ", salt='" + salt + '\'' +
+ ", locked=" + locked +
+ '}';
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/realm/UserRealm.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/realm/UserRealm.java
new file mode 100644
index 00000000..9c85b164
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/realm/UserRealm.java
@@ -0,0 +1,91 @@
+package com.github.zhangkaitao.shiro.chapter23.realm;
+
+import com.github.zhangkaitao.shiro.chapter23.Constants;
+import com.github.zhangkaitao.shiro.chapter23.entity.User;
+import com.github.zhangkaitao.shiro.chapter23.service.AuthorizationService;
+import com.github.zhangkaitao.shiro.chapter23.service.UserService;
+import org.apache.shiro.authc.*;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.util.ByteSource;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public class UserRealm extends AuthorizingRealm {
+
+ @Autowired
+ private UserService userService;
+
+ @Autowired
+ private AuthorizationService authorizationService;
+
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+ String username = (String)principals.getPrimaryPrincipal();
+
+ SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
+ authorizationInfo.setRoles(authorizationService.findRoles(Constants.SERVER_APP_KEY, username));
+ authorizationInfo.setStringPermissions(authorizationService.findPermissions(Constants.SERVER_APP_KEY, username));
+ return authorizationInfo;
+ }
+
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+
+ String username = (String)token.getPrincipal();
+
+ User user = userService.findByUsername(username);
+
+ if(user == null) {
+ throw new UnknownAccountException();//没找到帐号
+ }
+
+ if(Boolean.TRUE.equals(user.getLocked())) {
+ throw new LockedAccountException(); //帐号锁定
+ }
+
+ //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
+ SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
+ user.getUsername(), //用户名
+ user.getPassword(), //密码
+ ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
+ getName() //realm name
+ );
+ return authenticationInfo;
+ }
+
+ @Override
+ public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
+ super.clearCachedAuthorizationInfo(principals);
+ }
+
+ @Override
+ public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
+ super.clearCachedAuthenticationInfo(principals);
+ }
+
+ @Override
+ public void clearCache(PrincipalCollection principals) {
+ super.clearCache(principals);
+ }
+
+ public void clearAllCachedAuthorizationInfo() {
+ getAuthorizationCache().clear();
+ }
+
+ public void clearAllCachedAuthenticationInfo() {
+ getAuthenticationCache().clear();
+ }
+
+ public void clearAllCache() {
+ clearAllCachedAuthenticationInfo();
+ clearAllCachedAuthorizationInfo();
+ }
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/RemoteService.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/RemoteService.java
new file mode 100644
index 00000000..a4799684
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/remote/RemoteService.java
@@ -0,0 +1,50 @@
+package com.github.zhangkaitao.shiro.chapter23.remote;
+
+import com.github.zhangkaitao.shiro.chapter23.service.AuthorizationService;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.eis.SessionDAO;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.Serializable;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-3-13
+ *
Version: 1.0
+ */
+public class RemoteService implements RemoteServiceInterface {
+
+ @Autowired
+ private AuthorizationService authorizationService;
+
+ @Autowired
+ private SessionDAO sessionDAO;
+
+ @Override
+ public Session getSession(String appKey, Serializable sessionId) {
+ return sessionDAO.readSession(sessionId);
+ }
+
+ @Override
+ public Serializable createSession(Session session) {
+ return sessionDAO.create(session);
+ }
+
+ @Override
+ public void updateSession(String appKey, Session session) {
+ sessionDAO.update(session);
+ }
+
+ @Override
+ public void deleteSession(String appKey, Session session) {
+ sessionDAO.delete(session);
+ }
+
+ @Override
+ public PermissionContext getPermissions(String appKey, String username) {
+ PermissionContext permissionContext = new PermissionContext();
+ permissionContext.setRoles(authorizationService.findRoles(appKey, username));
+ permissionContext.setPermissions(authorizationService.findPermissions(appKey, username));
+ return permissionContext;
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AppService.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AppService.java
new file mode 100644
index 00000000..d945b4a1
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AppService.java
@@ -0,0 +1,23 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.App;
+
+import java.util.List;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface AppService {
+
+
+ public App createApp(App app);
+ public App updateApp(App app);
+ public void deleteApp(Long appId);
+
+ public App findOne(Long appId);
+ public List findAll();
+
+ public Long findAppIdByAppKey(String appKey);
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AppServiceImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AppServiceImpl.java
new file mode 100644
index 00000000..6ebd359f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AppServiceImpl.java
@@ -0,0 +1,48 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.dao.AppDao;
+import com.github.zhangkaitao.shiro.chapter23.entity.App;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Service
+
+public class AppServiceImpl implements AppService {
+
+ @Autowired
+ private AppDao appDao;
+
+ public App createApp(App app) {
+ return appDao.createApp(app);
+ }
+
+ public App updateApp(App app) {
+ return appDao.updateApp(app);
+ }
+
+ public void deleteApp(Long appId) {
+ appDao.deleteApp(appId);
+ }
+
+ @Override
+ public App findOne(Long appId) {
+ return appDao.findOne(appId);
+ }
+
+ @Override
+ public List findAll() {
+ return appDao.findAll();
+ }
+
+ @Override
+ public Long findAppIdByAppKey(String appKey) {
+ return appDao.findAppIdByAppKey(appKey);
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AuthorizationService.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AuthorizationService.java
new file mode 100644
index 00000000..edc51581
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AuthorizationService.java
@@ -0,0 +1,38 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Authorization;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface AuthorizationService {
+
+
+ public Authorization createAuthorization(Authorization authorization);
+ public Authorization updateAuthorization(Authorization authorization);
+ public void deleteAuthorization(Long authorizationId);
+
+ public Authorization findOne(Long authorizationId);
+ public List findAll();
+
+ /**
+ * 根据用户名查找其角色
+ * @param username
+ * @return
+ */
+ public Set findRoles(String appKey, String username);
+
+ /**
+ * 根据用户名查找其权限
+ * @param username
+ * @return
+ */
+ public Set findPermissions(String appKey, String username);
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AuthorizationServiceImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AuthorizationServiceImpl.java
new file mode 100644
index 00000000..7ce6e332
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/AuthorizationServiceImpl.java
@@ -0,0 +1,120 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.dao.AuthorizationDao;
+import com.github.zhangkaitao.shiro.chapter23.entity.Authorization;
+import com.github.zhangkaitao.shiro.chapter23.entity.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Service
+
+public class AuthorizationServiceImpl implements AuthorizationService {
+
+ @Autowired
+ private AuthorizationDao authorizationDao;
+ @Autowired
+ private UserService userService;
+ @Autowired
+ private RoleService roleService;
+ @Autowired
+ private AppService appService;
+
+ public Authorization createAuthorization(Authorization authorization) {
+ return merge(authorization);
+ }
+
+ public Authorization updateAuthorization(Authorization authorization) {
+ return merge(authorization);
+ }
+
+ public Authorization merge(Authorization authorization) {
+ Authorization dbAuthorization = authorizationDao.findByAppUser(authorization.getAppId(), authorization.getUserId());
+ if(dbAuthorization == null) {//如果数据库中不存在相应记录 直接新增
+ return authorizationDao.createAuthorization(authorization);
+ }
+
+ if(dbAuthorization.equals(authorization)) {//如果是同一条记录直接更新即可
+ return authorizationDao.updateAuthorization(authorization);
+ }
+
+ for(Long roleId : authorization.getRoleIds()) {//否则合并
+ if(!dbAuthorization.getRoleIds().contains(roleId)) {
+ dbAuthorization.getRoleIds().add(roleId);
+ }
+ }
+
+ if(dbAuthorization.getRoleIds().isEmpty()) {//如果没有角色 直接删除记录即可
+ authorizationDao.deleteAuthorization(dbAuthorization.getId());
+ return dbAuthorization;
+ }
+ //否则更新
+ return authorizationDao.updateAuthorization(dbAuthorization);
+ }
+
+ public void deleteAuthorization(Long authorizationId) {
+ authorizationDao.deleteAuthorization(authorizationId);
+ }
+
+ @Override
+ public Authorization findOne(Long authorizationId) {
+ return authorizationDao.findOne(authorizationId);
+ }
+
+ @Override
+ public List findAll() {
+ return authorizationDao.findAll();
+ }
+
+ /**
+ * 根据用户名查找其角色
+ * @param username
+ * @return
+ */
+ public Set findRoles(String appKey, String username) {
+ User user = userService.findByUsername(username);
+ if(user == null) {
+ return Collections.EMPTY_SET;
+ }
+ Long appId = appService.findAppIdByAppKey(appKey);
+ if(appId == null) {
+ return Collections.EMPTY_SET;
+ }
+ Authorization authorization = authorizationDao.findByAppUser(appId, user.getId());
+ if(authorization == null) {
+ return Collections.EMPTY_SET;
+ }
+ return roleService.findRoles(authorization.getRoleIds().toArray(new Long[0]));
+ }
+
+ /**
+ * 根据用户名查找其权限
+ * @param username
+ * @return
+ */
+ public Set findPermissions(String appKey, String username) {
+ User user = userService.findByUsername(username);
+ if(user == null) {
+ return Collections.EMPTY_SET;
+ }
+ Long appId = appService.findAppIdByAppKey(appKey);
+ if(appId == null) {
+ return Collections.EMPTY_SET;
+ }
+ Authorization authorization = authorizationDao.findByAppUser(appId, user.getId());
+ if(authorization == null) {
+ return Collections.EMPTY_SET;
+ }
+ return roleService.findPermissions(authorization.getRoleIds().toArray(new Long[0]));
+ }
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/OrganizationService.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/OrganizationService.java
new file mode 100644
index 00000000..4fef3b09
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/OrganizationService.java
@@ -0,0 +1,25 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Organization;
+
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface OrganizationService {
+
+
+ public Organization createOrganization(Organization organization);
+ public Organization updateOrganization(Organization organization);
+ public void deleteOrganization(Long organizationId);
+
+ Organization findOne(Long organizationId);
+ List findAll();
+
+ Object findAllWithExclude(Organization excludeOraganization);
+
+ void move(Organization source, Organization target);
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/OrganizationServiceImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/OrganizationServiceImpl.java
new file mode 100644
index 00000000..b62c819e
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/OrganizationServiceImpl.java
@@ -0,0 +1,54 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.dao.OrganizationDao;
+import com.github.zhangkaitao.shiro.chapter23.entity.Organization;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Service
+public class OrganizationServiceImpl implements OrganizationService {
+ @Autowired
+ private OrganizationDao organizationDao;
+
+ @Override
+ public Organization createOrganization(Organization organization) {
+ return organizationDao.createOrganization(organization);
+ }
+
+ @Override
+ public Organization updateOrganization(Organization organization) {
+ return organizationDao.updateOrganization(organization);
+ }
+
+ @Override
+ public void deleteOrganization(Long organizationId) {
+ organizationDao.deleteOrganization(organizationId);
+ }
+
+ @Override
+ public Organization findOne(Long organizationId) {
+ return organizationDao.findOne(organizationId);
+ }
+
+ @Override
+ public List findAll() {
+ return organizationDao.findAll();
+ }
+
+ @Override
+ public List findAllWithExclude(Organization excludeOraganization) {
+ return organizationDao.findAllWithExclude(excludeOraganization);
+ }
+
+ @Override
+ public void move(Organization source, Organization target) {
+ organizationDao.move(source, target);
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/PasswordHelper.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/PasswordHelper.java
new file mode 100644
index 00000000..4fb2864f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/PasswordHelper.java
@@ -0,0 +1,50 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.User;
+import org.apache.shiro.crypto.RandomNumberGenerator;
+import org.apache.shiro.crypto.SecureRandomNumberGenerator;
+import org.apache.shiro.crypto.hash.SimpleHash;
+import org.apache.shiro.util.ByteSource;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Service
+public class PasswordHelper {
+
+ private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
+
+ @Value("${password.algorithmName}")
+ private String algorithmName = "md5";
+ @Value("${password.hashIterations}")
+ private int hashIterations = 2;
+
+ public void setRandomNumberGenerator(RandomNumberGenerator randomNumberGenerator) {
+ this.randomNumberGenerator = randomNumberGenerator;
+ }
+
+ public void setAlgorithmName(String algorithmName) {
+ this.algorithmName = algorithmName;
+ }
+
+ public void setHashIterations(int hashIterations) {
+ this.hashIterations = hashIterations;
+ }
+
+ public void encryptPassword(User user) {
+
+ user.setSalt(randomNumberGenerator.nextBytes().toHex());
+
+ String newPassword = new SimpleHash(
+ algorithmName,
+ user.getPassword(),
+ ByteSource.Util.bytes(user.getCredentialsSalt()),
+ hashIterations).toHex();
+
+ user.setPassword(newPassword);
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/ResourceService.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/ResourceService.java
new file mode 100644
index 00000000..7094289b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/ResourceService.java
@@ -0,0 +1,36 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Resource;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface ResourceService {
+
+
+ public Resource createResource(Resource resource);
+ public Resource updateResource(Resource resource);
+ public void deleteResource(Long resourceId);
+
+ Resource findOne(Long resourceId);
+ List findAll();
+
+ /**
+ * 得到资源对应的权限字符串
+ * @param resourceIds
+ * @return
+ */
+ Set findPermissions(Set resourceIds);
+
+ /**
+ * 根据用户权限得到菜单
+ * @param permissions
+ * @return
+ */
+ List findMenus(Set permissions);
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/ResourceServiceImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/ResourceServiceImpl.java
new file mode 100644
index 00000000..e332a68a
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/ResourceServiceImpl.java
@@ -0,0 +1,96 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.dao.ResourceDao;
+import com.github.zhangkaitao.shiro.chapter23.entity.Resource;
+import org.apache.shiro.authz.permission.WildcardPermission;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Service
+
+public class ResourceServiceImpl implements ResourceService {
+
+ @Autowired
+ private ResourceDao resourceDao;
+
+ @Override
+ public Resource createResource(Resource resource) {
+ return resourceDao.createResource(resource);
+ }
+
+ @Override
+ public Resource updateResource(Resource resource) {
+ return resourceDao.updateResource(resource);
+ }
+
+ @Override
+ public void deleteResource(Long resourceId) {
+ resourceDao.deleteResource(resourceId);
+ }
+
+ @Override
+ public Resource findOne(Long resourceId) {
+ return resourceDao.findOne(resourceId);
+ }
+
+ @Override
+ public List findAll() {
+ return resourceDao.findAll();
+ }
+
+ @Override
+ public Set findPermissions(Set resourceIds) {
+ Set permissions = new HashSet();
+ for(Long resourceId : resourceIds) {
+ Resource resource = findOne(resourceId);
+ if(resource != null && !StringUtils.isEmpty(resource.getPermission())) {
+ permissions.add(resource.getPermission());
+ }
+ }
+ return permissions;
+ }
+
+ @Override
+ public List findMenus(Set permissions) {
+ List allResources = findAll();
+ List menus = new ArrayList();
+ for(Resource resource : allResources) {
+ if(resource.isRootNode()) {
+ continue;
+ }
+ if(resource.getType() != Resource.ResourceType.menu) {
+ continue;
+ }
+ if(!hasPermission(permissions, resource)) {
+ continue;
+ }
+ menus.add(resource);
+ }
+ return menus;
+ }
+
+ private boolean hasPermission(Set permissions, Resource resource) {
+ if(StringUtils.isEmpty(resource.getPermission())) {
+ return true;
+ }
+ for(String permission : permissions) {
+ WildcardPermission p1 = new WildcardPermission(permission);
+ WildcardPermission p2 = new WildcardPermission(resource.getPermission());
+ if(p1.implies(p2) || p2.implies(p1)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/RoleService.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/RoleService.java
new file mode 100644
index 00000000..6251a04d
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/RoleService.java
@@ -0,0 +1,36 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Role;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface RoleService {
+
+
+ public Role createRole(Role role);
+ public Role updateRole(Role role);
+ public void deleteRole(Long roleId);
+
+ public Role findOne(Long roleId);
+ public List findAll();
+
+ /**
+ * 根据角色编号得到角色标识符列表
+ * @param roleIds
+ * @return
+ */
+ Set findRoles(Long... roleIds);
+
+ /**
+ * 根据角色编号得到权限字符串列表
+ * @param roleIds
+ * @return
+ */
+ Set findPermissions(Long[] roleIds);
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/RoleServiceImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/RoleServiceImpl.java
new file mode 100644
index 00000000..1b86a66f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/RoleServiceImpl.java
@@ -0,0 +1,71 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.dao.RoleDao;
+import com.github.zhangkaitao.shiro.chapter23.entity.Role;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Service
+
+public class RoleServiceImpl implements RoleService {
+
+ @Autowired
+ private RoleDao roleDao;
+ @Autowired
+ private ResourceService resourceService;
+
+ public Role createRole(Role role) {
+ return roleDao.createRole(role);
+ }
+
+ public Role updateRole(Role role) {
+ return roleDao.updateRole(role);
+ }
+
+ public void deleteRole(Long roleId) {
+ roleDao.deleteRole(roleId);
+ }
+
+ @Override
+ public Role findOne(Long roleId) {
+ return roleDao.findOne(roleId);
+ }
+
+ @Override
+ public List findAll() {
+ return roleDao.findAll();
+ }
+
+ @Override
+ public Set findRoles(Long... roleIds) {
+ Set roles = new HashSet();
+ for(Long roleId : roleIds) {
+ Role role = findOne(roleId);
+ if(role != null) {
+ roles.add(role.getRole());
+ }
+ }
+ return roles;
+ }
+
+ @Override
+ public Set findPermissions(Long[] roleIds) {
+ Set resourceIds = new HashSet();
+ for(Long roleId : roleIds) {
+ Role role = findOne(roleId);
+ if(role != null) {
+ resourceIds.addAll(role.getResourceIds());
+ }
+ }
+ return resourceService.findPermissions(resourceIds);
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/UserService.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/UserService.java
new file mode 100644
index 00000000..9fa04ddb
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/UserService.java
@@ -0,0 +1,45 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.User;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+public interface UserService {
+
+ /**
+ * 创建用户
+ * @param user
+ */
+ public User createUser(User user);
+
+ public User updateUser(User user);
+
+ public void deleteUser(Long userId);
+
+ /**
+ * 修改密码
+ * @param userId
+ * @param newPassword
+ */
+ public void changePassword(Long userId, String newPassword);
+
+
+ User findOne(Long userId);
+
+ List findAll();
+
+ /**
+ * 根据用户名查找用户
+ * @param username
+ * @return
+ */
+ public User findByUsername(String username);
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/UserServiceImpl.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/UserServiceImpl.java
new file mode 100644
index 00000000..3ee796b6
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/service/UserServiceImpl.java
@@ -0,0 +1,78 @@
+package com.github.zhangkaitao.shiro.chapter23.service;
+
+import com.github.zhangkaitao.shiro.chapter23.dao.UserDao;
+import com.github.zhangkaitao.shiro.chapter23.entity.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-1-28
+ *
Version: 1.0
+ */
+@Service
+
+public class UserServiceImpl implements UserService {
+
+ @Autowired
+ private UserDao userDao;
+ @Autowired
+ private PasswordHelper passwordHelper;
+ @Autowired
+ private RoleService roleService;
+
+ /**
+ * 创建用户
+ * @param user
+ */
+ public User createUser(User user) {
+ //加密密码
+ passwordHelper.encryptPassword(user);
+ return userDao.createUser(user);
+ }
+
+ @Override
+ public User updateUser(User user) {
+ return userDao.updateUser(user);
+ }
+
+ @Override
+ public void deleteUser(Long userId) {
+ userDao.deleteUser(userId);
+ }
+
+ /**
+ * 修改密码
+ * @param userId
+ * @param newPassword
+ */
+ public void changePassword(Long userId, String newPassword) {
+ User user =userDao.findOne(userId);
+ user.setPassword(newPassword);
+ passwordHelper.encryptPassword(user);
+ userDao.updateUser(user);
+ }
+
+ @Override
+ public User findOne(Long userId) {
+ return userDao.findOne(userId);
+ }
+
+ @Override
+ public List findAll() {
+ return userDao.findAll();
+ }
+
+ /**
+ * 根据用户名查找用户
+ * @param username
+ * @return
+ */
+ public User findByUsername(String username) {
+ return userDao.findByUsername(username);
+ }
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/session/dao/MySqlSessionDAO.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/session/dao/MySqlSessionDAO.java
new file mode 100644
index 00000000..c946544c
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/session/dao/MySqlSessionDAO.java
@@ -0,0 +1,51 @@
+package com.github.zhangkaitao.shiro.chapter23.session.dao;
+
+import com.github.zhangkaitao.shiro.chapter23.utils.SerializableUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.ValidatingSession;
+import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-2-8
+ *
Version: 1.0
+ */
+public class MySqlSessionDAO extends CachingSessionDAO {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ @Override
+ protected Serializable doCreate(Session session) {
+ Serializable sessionId = generateSessionId(session);
+ assignSessionId(session, sessionId);
+ String sql = "insert into sessions(id, session) values(?,?)";
+ jdbcTemplate.update(sql, sessionId, SerializableUtils.serialize(session));
+ return session.getId();
+ }
+ @Override
+ protected void doUpdate(Session session) {
+ if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()) {
+ return; //如果会话过期/停止 没必要再更新了
+ }
+ String sql = "update sessions set session=? where id=?";
+ jdbcTemplate.update(sql, SerializableUtils.serialize(session), session.getId());
+ }
+ @Override
+ protected void doDelete(Session session) {
+ String sql = "delete from sessions where id=?";
+ jdbcTemplate.update(sql, session.getId());
+ }
+ @Override
+ protected Session doReadSession(Serializable sessionId) {
+ String sql = "select session from sessions where id=?";
+ List sessionStrList = jdbcTemplate.queryForList(sql, String.class, sessionId);
+ if(sessionStrList.size() == 0) return null;
+ return SerializableUtils.deserialize(sessionStrList.get(0));
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/session/scheduler/MySqlSessionValidationScheduler.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/session/scheduler/MySqlSessionValidationScheduler.java
new file mode 100644
index 00000000..26af7d07
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/session/scheduler/MySqlSessionValidationScheduler.java
@@ -0,0 +1,117 @@
+package com.github.zhangkaitao.shiro.chapter23.session.scheduler;
+
+import com.github.zhangkaitao.shiro.chapter23.utils.SerializableUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.session.mgt.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.util.ReflectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-2-9
+ *
Version: 1.0
+ */
+public class MySqlSessionValidationScheduler implements SessionValidationScheduler, Runnable {
+
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ /** Private internal log instance. */
+ private static final Logger log = LoggerFactory.getLogger(MySqlSessionValidationScheduler.class);
+
+ ValidatingSessionManager sessionManager;
+ private ScheduledExecutorService service;
+ private long interval = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
+ private boolean enabled = false;
+
+ public MySqlSessionValidationScheduler() {
+ super();
+ }
+
+ public ValidatingSessionManager getSessionManager() {
+ return sessionManager;
+ }
+
+ public void setSessionManager(ValidatingSessionManager sessionManager) {
+ this.sessionManager = sessionManager;
+ }
+
+ public long getInterval() {
+ return interval;
+ }
+
+ public void setInterval(long interval) {
+ this.interval = interval;
+ }
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ /**
+ * Creates a single thread {@link java.util.concurrent.ScheduledExecutorService} to validate sessions at fixed intervals
+ * and enables this scheduler. The executor is created as a daemon thread to allow JVM to shut down
+ */
+ //TODO Implement an integration test to test for jvm exit as part of the standalone example
+ // (so we don't have to change the unit test execution model for the core module)
+ public void enableSessionValidation() {
+ if (this.interval > 0l) {
+ this.service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r);
+ thread.setDaemon(true);
+ return thread;
+ }
+ });
+ this.service.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS);
+ this.enabled = true;
+ }
+ }
+
+ public void run() {
+ if (log.isDebugEnabled()) {
+ log.debug("Executing session validation...");
+ }
+ long startTime = System.currentTimeMillis();
+
+ //分页获取会话并验证
+ String sql = "select session from sessions limit ?,?";
+ int start = 0; //起始记录
+ int size = 20; //每页大小
+ List sessionList = jdbcTemplate.queryForList(sql, String.class, start, size);
+ while(sessionList.size() > 0) {
+ for(String sessionStr : sessionList) {
+ try {
+ Session session = SerializableUtils.deserialize(sessionStr);
+ Method validateMethod = ReflectionUtils.findMethod(AbstractValidatingSessionManager.class, "validate", Session.class, SessionKey.class);
+ validateMethod.setAccessible(true);
+ ReflectionUtils.invokeMethod(validateMethod, sessionManager, session, new DefaultSessionKey(session.getId()));
+ } catch (Exception e) {
+ //ignore
+ }
+ }
+ start = start + size;
+ sessionList = jdbcTemplate.queryForList(sql, String.class, start, size);
+ }
+
+ long stopTime = System.currentTimeMillis();
+ if (log.isDebugEnabled()) {
+ log.debug("Session validation completed successfully in " + (stopTime - startTime) + " milliseconds.");
+ }
+ }
+
+ public void disableSessionValidation() {
+ this.service.shutdownNow();
+ this.enabled = false;
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/utils/SerializableUtils.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/utils/SerializableUtils.java
new file mode 100644
index 00000000..99e43ba3
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/utils/SerializableUtils.java
@@ -0,0 +1,37 @@
+package com.github.zhangkaitao.shiro.chapter23.utils;
+
+import org.apache.shiro.codec.Base64;
+import org.apache.shiro.session.Session;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-2-8
+ *
Version: 1.0
+ */
+public class SerializableUtils {
+
+ public static String serialize(Session session) {
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ oos.writeObject(session);
+ return Base64.encodeToString(bos.toByteArray());
+ } catch (Exception e) {
+ throw new RuntimeException("serialize session error", e);
+ }
+ }
+ public static Session deserialize(String sessionStr) {
+ try {
+ ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(sessionStr));
+ ObjectInputStream ois = new ObjectInputStream(bis);
+ return (Session)ois.readObject();
+ } catch (Exception e) {
+ throw new RuntimeException("deserialize session error", e);
+ }
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/bind/annotation/CurrentUser.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/bind/annotation/CurrentUser.java
new file mode 100644
index 00000000..394a752c
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/bind/annotation/CurrentUser.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.zhangkaitao.shiro.chapter23.web.bind.annotation;
+
+import com.github.zhangkaitao.shiro.chapter23.Constants;
+
+import java.lang.annotation.*;
+
+/**
+ *
绑定当前登录的用户
+ * 不同于@ModelAttribute
+ *
+ * @author Zhang Kaitao
+ */
+@Target({ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface CurrentUser {
+
+ /**
+ * 当前用户在request中的名字
+ *
+ * @return
+ */
+ String value() default Constants.CURRENT_USER;
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/bind/method/CurrentUserMethodArgumentResolver.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/bind/method/CurrentUserMethodArgumentResolver.java
new file mode 100644
index 00000000..614974bb
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/bind/method/CurrentUserMethodArgumentResolver.java
@@ -0,0 +1,34 @@
+package com.github.zhangkaitao.shiro.chapter23.web.bind.method;
+
+import com.github.zhangkaitao.shiro.chapter23.web.bind.annotation.CurrentUser;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+/**
+ * 用于绑定@FormModel的方法参数解析器
+ *
User: Zhang Kaitao
+ *
Date: 13-1-12 下午5:01
+ *
Version: 1.0
+ */
+public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
+
+ public CurrentUserMethodArgumentResolver() {
+ }
+
+ @Override
+ public boolean supportsParameter(MethodParameter parameter) {
+ if (parameter.hasParameterAnnotation(CurrentUser.class)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
+ CurrentUser currentUserAnnotation = parameter.getParameterAnnotation(CurrentUser.class);
+ return webRequest.getAttribute(currentUserAnnotation.value(), NativeWebRequest.SCOPE_REQUEST);
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/AppController.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/AppController.java
new file mode 100644
index 00000000..b6f6b755
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/AppController.java
@@ -0,0 +1,86 @@
+package com.github.zhangkaitao.shiro.chapter23.web.controller;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.App;
+import com.github.zhangkaitao.shiro.chapter23.service.AppService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import java.util.UUID;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Controller
+@RequestMapping("/app")
+public class AppController {
+
+ @Autowired
+ private AppService appService;
+
+ @RequiresPermissions("app:view")
+ @RequestMapping(method = RequestMethod.GET)
+ public String list(Model model) {
+ model.addAttribute("appList", appService.findAll());
+ return "app/list";
+ }
+
+ @RequiresPermissions("app:create")
+ @RequestMapping(value = "/create", method = RequestMethod.GET)
+ public String showCreateForm(Model model) {
+ App app = new App();
+ app.setAppKey(UUID.randomUUID().toString());
+ app.setAppSecret(UUID.randomUUID().toString());
+ model.addAttribute("app", app);
+ model.addAttribute("op", "新增");
+ return "app/edit";
+ }
+
+ @RequiresPermissions("app:create")
+ @RequestMapping(value = "/create", method = RequestMethod.POST)
+ public String create(App app, RedirectAttributes redirectAttributes) {
+ appService.createApp(app);
+ redirectAttributes.addFlashAttribute("msg", "新增成功");
+ return "redirect:/app";
+ }
+
+ @RequiresPermissions("app:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.GET)
+ public String showUpdateForm(@PathVariable("id") Long id, Model model) {
+ model.addAttribute("app", appService.findOne(id));
+ model.addAttribute("op", "修改");
+ return "app/edit";
+ }
+
+ @RequiresPermissions("app:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
+ public String update(App app, RedirectAttributes redirectAttributes) {
+ appService.updateApp(app);
+ redirectAttributes.addFlashAttribute("msg", "修改成功");
+ return "redirect:/app";
+ }
+
+ @RequiresPermissions("app:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.GET)
+ public String showDeleteForm(@PathVariable("id") Long id, Model model) {
+ model.addAttribute("app", appService.findOne(id));
+ model.addAttribute("op", "删除");
+ return "app/edit";
+ }
+
+ @RequiresPermissions("app:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.POST)
+ public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
+ appService.deleteApp(id);
+ redirectAttributes.addFlashAttribute("msg", "删除成功");
+ return "redirect:/app";
+ }
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/AuthorizationController.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/AuthorizationController.java
new file mode 100644
index 00000000..d2ecca1f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/AuthorizationController.java
@@ -0,0 +1,101 @@
+package com.github.zhangkaitao.shiro.chapter23.web.controller;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Authorization;
+import com.github.zhangkaitao.shiro.chapter23.service.AppService;
+import com.github.zhangkaitao.shiro.chapter23.service.AuthorizationService;
+import com.github.zhangkaitao.shiro.chapter23.service.RoleService;
+import com.github.zhangkaitao.shiro.chapter23.service.UserService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Controller
+@RequestMapping("/authorization")
+public class AuthorizationController {
+
+ @Autowired
+ private AuthorizationService authorizationService;
+ @Autowired
+ private UserService userService;
+ @Autowired
+ private AppService appService;
+ @Autowired
+ private RoleService roleService;
+
+ @RequiresPermissions("authorization:view")
+ @RequestMapping(method = RequestMethod.GET)
+ public String list(Model model) {
+ model.addAttribute("authorizationList", authorizationService.findAll());
+ return "authorization/list";
+ }
+
+ @RequiresPermissions("authorization:create")
+ @RequestMapping(value = "/create", method = RequestMethod.GET)
+ public String showCreateForm(Model model) {
+ setCommonData(model);
+ Authorization authorization = new Authorization();
+ model.addAttribute("authorization", authorization);
+ model.addAttribute("op", "新增");
+ return "authorization/edit";
+ }
+
+ @RequiresPermissions("authorization:create")
+ @RequestMapping(value = "/create", method = RequestMethod.POST)
+ public String create(Authorization authorization, RedirectAttributes redirectAttributes) {
+ authorizationService.createAuthorization(authorization);
+ redirectAttributes.addFlashAttribute("msg", "新增成功");
+ return "redirect:/authorization";
+ }
+
+ @RequiresPermissions("authorization:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.GET)
+ public String showUpdateForm(@PathVariable("id") Long id, Model model) {
+ setCommonData(model);
+ model.addAttribute("authorization", authorizationService.findOne(id));
+ model.addAttribute("op", "修改");
+ return "authorization/edit";
+ }
+
+ @RequiresPermissions("authorization:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
+ public String update(Authorization authorization, RedirectAttributes redirectAttributes) {
+ authorizationService.updateAuthorization(authorization);
+ redirectAttributes.addFlashAttribute("msg", "修改成功");
+ return "redirect:/authorization";
+ }
+
+ @RequiresPermissions("authorization:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.GET)
+ public String showDeleteForm(@PathVariable("id") Long id, Model model) {
+ setCommonData(model);
+ model.addAttribute("authorization", authorizationService.findOne(id));
+ model.addAttribute("op", "删除");
+ return "authorization/edit";
+ }
+
+ @RequiresPermissions("authorization:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.POST)
+ public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
+ authorizationService.deleteAuthorization(id);
+ redirectAttributes.addFlashAttribute("msg", "删除成功");
+ return "redirect:/authorization";
+ }
+
+ private void setCommonData(Model model) {
+ model.addAttribute("userList", userService.findAll());
+ model.addAttribute("roleList", roleService.findAll());
+ model.addAttribute("appList", appService.findAll());
+ }
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/IndexController.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/IndexController.java
new file mode 100644
index 00000000..fbb9aa2f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/IndexController.java
@@ -0,0 +1,43 @@
+package com.github.zhangkaitao.shiro.chapter23.web.controller;
+
+import com.github.zhangkaitao.shiro.chapter23.Constants;
+import com.github.zhangkaitao.shiro.chapter23.entity.Resource;
+import com.github.zhangkaitao.shiro.chapter23.service.AuthorizationService;
+import com.github.zhangkaitao.shiro.chapter23.service.ResourceService;
+import com.github.zhangkaitao.shiro.chapter23.web.bind.annotation.CurrentUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Controller
+public class IndexController {
+
+ @Autowired
+ private ResourceService resourceService;
+ @Autowired
+ private AuthorizationService authorizationService;
+
+ @RequestMapping("/")
+ public String index(@CurrentUser com.github.zhangkaitao.shiro.chapter23.entity.User loginUser, Model model) {
+ Set permissions = authorizationService.findPermissions(Constants.SERVER_APP_KEY, loginUser.getUsername());
+ List menus = resourceService.findMenus(permissions);
+ model.addAttribute("menus", menus);
+ return "index";
+ }
+
+ @RequestMapping("/welcome")
+ public String welcome() {
+ return "welcome";
+ }
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/LoginController.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/LoginController.java
new file mode 100644
index 00000000..dfc163a3
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/LoginController.java
@@ -0,0 +1,35 @@
+package com.github.zhangkaitao.shiro.chapter23.web.controller;
+
+import org.apache.shiro.authc.IncorrectCredentialsException;
+import org.apache.shiro.authc.UnknownAccountException;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-2-15
+ *
Version: 1.0
+ */
+@Controller
+public class LoginController {
+
+ @RequestMapping(value = "/login" )
+ public String showLoginForm(HttpServletRequest req, Model model) {
+ String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
+ String error = null;
+ if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
+ error = "用户名/密码错误";
+ } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
+ error = "用户名/密码错误";
+ } else if(exceptionClassName != null) {
+ error = "其他错误:" + exceptionClassName;
+ }
+ model.addAttribute("error", error);
+ return "login";
+ }
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/OrganizationController.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/OrganizationController.java
new file mode 100644
index 00000000..5df0a5c3
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/OrganizationController.java
@@ -0,0 +1,111 @@
+package com.github.zhangkaitao.shiro.chapter23.web.controller;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Organization;
+import com.github.zhangkaitao.shiro.chapter23.service.OrganizationService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Controller
+@RequestMapping("/organization")
+public class OrganizationController {
+
+ @Autowired
+ private OrganizationService organizationService;
+
+ @RequiresPermissions("organization:view")
+ @RequestMapping(method = RequestMethod.GET)
+ public String index(Model model) {
+ return "organization/index";
+ }
+
+ @RequiresPermissions("organization:view")
+ @RequestMapping(value = "/tree", method = RequestMethod.GET)
+ public String showTree(Model model) {
+ model.addAttribute("organizationList", organizationService.findAll());
+ return "organization/tree";
+ }
+
+ @RequiresPermissions("organization:create")
+ @RequestMapping(value = "/{parentId}/appendChild", method = RequestMethod.GET)
+ public String showAppendChildForm(@PathVariable("parentId") Long parentId, Model model) {
+ Organization parent = organizationService.findOne(parentId);
+ model.addAttribute("parent", parent);
+ Organization child = new Organization();
+ child.setParentId(parentId);
+ child.setParentIds(parent.makeSelfAsParentIds());
+ model.addAttribute("child", child);
+ model.addAttribute("op", "新增");
+ return "organization/appendChild";
+ }
+
+ @RequiresPermissions("organization:create")
+ @RequestMapping(value = "/{parentId}/appendChild", method = RequestMethod.POST)
+ public String create(Organization organization) {
+ organizationService.createOrganization(organization);
+ return "redirect:/organization/success";
+ }
+
+ @RequiresPermissions("organization:update")
+ @RequestMapping(value = "/{id}/maintain", method = RequestMethod.GET)
+ public String showMaintainForm(@PathVariable("id") Long id, Model model) {
+ model.addAttribute("organization", organizationService.findOne(id));
+ return "organization/maintain";
+ }
+
+ @RequiresPermissions("organization:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
+ public String update(Organization organization, RedirectAttributes redirectAttributes) {
+ organizationService.updateOrganization(organization);
+ redirectAttributes.addFlashAttribute("msg", "修改成功");
+ return "redirect:/organization/success";
+ }
+
+ @RequiresPermissions("organization:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.POST)
+ public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
+ organizationService.deleteOrganization(id);
+ redirectAttributes.addFlashAttribute("msg", "删除成功");
+ return "redirect:/organization/success";
+ }
+
+
+ @RequiresPermissions("organization:update")
+ @RequestMapping(value = "/{sourceId}/move", method = RequestMethod.GET)
+ public String showMoveForm(@PathVariable("sourceId") Long sourceId, Model model) {
+ Organization source = organizationService.findOne(sourceId);
+ model.addAttribute("source", source);
+ model.addAttribute("targetList", organizationService.findAllWithExclude(source));
+ return "organization/move";
+ }
+
+ @RequiresPermissions("organization:update")
+ @RequestMapping(value = "/{sourceId}/move", method = RequestMethod.POST)
+ public String move(
+ @PathVariable("sourceId") Long sourceId,
+ @RequestParam("targetId") Long targetId) {
+ Organization source = organizationService.findOne(sourceId);
+ Organization target = organizationService.findOne(targetId);
+ organizationService.move(source, target);
+ return "redirect:/organization/success";
+ }
+
+ @RequiresPermissions("organization:view")
+ @RequestMapping(value = "/success", method = RequestMethod.GET)
+ public String success() {
+ return "organization/success";
+ }
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/ResourceController.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/ResourceController.java
new file mode 100644
index 00000000..288484c2
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/ResourceController.java
@@ -0,0 +1,85 @@
+package com.github.zhangkaitao.shiro.chapter23.web.controller;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Resource;
+import com.github.zhangkaitao.shiro.chapter23.service.ResourceService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Controller
+@RequestMapping("/resource")
+public class ResourceController {
+
+ @Autowired
+ private ResourceService resourceService;
+
+ @ModelAttribute("types")
+ public Resource.ResourceType[] resourceTypes() {
+ return Resource.ResourceType.values();
+ }
+
+ @RequiresPermissions("resource:view")
+ @RequestMapping(method = RequestMethod.GET)
+ public String list(Model model) {
+ model.addAttribute("resourceList", resourceService.findAll());
+ return "resource/list";
+ }
+
+ @RequiresPermissions("resource:create")
+ @RequestMapping(value = "/{parentId}/appendChild", method = RequestMethod.GET)
+ public String showAppendChildForm(@PathVariable("parentId") Long parentId, Model model) {
+ Resource parent = resourceService.findOne(parentId);
+ model.addAttribute("parent", parent);
+ Resource child = new Resource();
+ child.setParentId(parentId);
+ child.setParentIds(parent.makeSelfAsParentIds());
+ model.addAttribute("resource", child);
+ model.addAttribute("op", "新增子节点");
+ return "resource/edit";
+ }
+
+ @RequiresPermissions("resource:create")
+ @RequestMapping(value = "/{parentId}/appendChild", method = RequestMethod.POST)
+ public String create(Resource resource, RedirectAttributes redirectAttributes) {
+ resourceService.createResource(resource);
+ redirectAttributes.addFlashAttribute("msg", "新增子节点成功");
+ return "redirect:/resource";
+ }
+
+ @RequiresPermissions("resource:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.GET)
+ public String showUpdateForm(@PathVariable("id") Long id, Model model) {
+ model.addAttribute("resource", resourceService.findOne(id));
+ model.addAttribute("op", "修改");
+ return "resource/edit";
+ }
+
+ @RequiresPermissions("resource:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
+ public String update(Resource resource, RedirectAttributes redirectAttributes) {
+ resourceService.updateResource(resource);
+ redirectAttributes.addFlashAttribute("msg", "修改成功");
+ return "redirect:/resource";
+ }
+
+ @RequiresPermissions("resource:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.GET)
+ public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
+ resourceService.deleteResource(id);
+ redirectAttributes.addFlashAttribute("msg", "删除成功");
+ return "redirect:/resource";
+ }
+
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/RoleController.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/RoleController.java
new file mode 100644
index 00000000..1eebc917
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/RoleController.java
@@ -0,0 +1,92 @@
+package com.github.zhangkaitao.shiro.chapter23.web.controller;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.Role;
+import com.github.zhangkaitao.shiro.chapter23.service.ResourceService;
+import com.github.zhangkaitao.shiro.chapter23.service.RoleService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Controller
+@RequestMapping("/role")
+public class RoleController {
+
+ @Autowired
+ private RoleService roleService;
+
+ @Autowired
+ private ResourceService resourceService;
+
+ @RequiresPermissions("role:view")
+ @RequestMapping(method = RequestMethod.GET)
+ public String list(Model model) {
+ model.addAttribute("roleList", roleService.findAll());
+ return "role/list";
+ }
+
+ @RequiresPermissions("role:create")
+ @RequestMapping(value = "/create", method = RequestMethod.GET)
+ public String showCreateForm(Model model) {
+ setCommonData(model);
+ model.addAttribute("role", new Role());
+ model.addAttribute("op", "新增");
+ return "role/edit";
+ }
+
+ @RequiresPermissions("role:create")
+ @RequestMapping(value = "/create", method = RequestMethod.POST)
+ public String create(Role role, RedirectAttributes redirectAttributes) {
+ roleService.createRole(role);
+ redirectAttributes.addFlashAttribute("msg", "新增成功");
+ return "redirect:/role";
+ }
+
+ @RequiresPermissions("role:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.GET)
+ public String showUpdateForm(@PathVariable("id") Long id, Model model) {
+ setCommonData(model);
+ model.addAttribute("role", roleService.findOne(id));
+ model.addAttribute("op", "修改");
+ return "role/edit";
+ }
+
+ @RequiresPermissions("role:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
+ public String update(Role role, RedirectAttributes redirectAttributes) {
+ roleService.updateRole(role);
+ redirectAttributes.addFlashAttribute("msg", "修改成功");
+ return "redirect:/role";
+ }
+
+ @RequiresPermissions("role:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.GET)
+ public String showDeleteForm(@PathVariable("id") Long id, Model model) {
+ setCommonData(model);
+ model.addAttribute("role", roleService.findOne(id));
+ model.addAttribute("op", "删除");
+ return "role/edit";
+ }
+
+ @RequiresPermissions("role:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.POST)
+ public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
+ roleService.deleteRole(id);
+ redirectAttributes.addFlashAttribute("msg", "删除成功");
+ return "redirect:/role";
+ }
+
+ private void setCommonData(Model model) {
+ model.addAttribute("resourceList", resourceService.findAll());
+ }
+
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/UserController.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/UserController.java
new file mode 100644
index 00000000..e15fffc6
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/controller/UserController.java
@@ -0,0 +1,108 @@
+package com.github.zhangkaitao.shiro.chapter23.web.controller;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.User;
+import com.github.zhangkaitao.shiro.chapter23.service.OrganizationService;
+import com.github.zhangkaitao.shiro.chapter23.service.RoleService;
+import com.github.zhangkaitao.shiro.chapter23.service.UserService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-14
+ *
Version: 1.0
+ */
+@Controller
+@RequestMapping("/user")
+public class UserController {
+
+ @Autowired
+ private UserService userService;
+ @Autowired
+ private OrganizationService organizationService;
+
+ @RequiresPermissions("user:view")
+ @RequestMapping(method = RequestMethod.GET)
+ public String list(Model model) {
+ model.addAttribute("userList", userService.findAll());
+ return "user/list";
+ }
+
+ @RequiresPermissions("user:create")
+ @RequestMapping(value = "/create", method = RequestMethod.GET)
+ public String showCreateForm(Model model) {
+ setCommonData(model);
+ model.addAttribute("user", new User());
+ model.addAttribute("op", "新增");
+ return "user/edit";
+ }
+
+ @RequiresPermissions("user:create")
+ @RequestMapping(value = "/create", method = RequestMethod.POST)
+ public String create(User user, RedirectAttributes redirectAttributes) {
+ userService.createUser(user);
+ redirectAttributes.addFlashAttribute("msg", "新增成功");
+ return "redirect:/user";
+ }
+
+ @RequiresPermissions("user:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.GET)
+ public String showUpdateForm(@PathVariable("id") Long id, Model model) {
+ setCommonData(model);
+ model.addAttribute("user", userService.findOne(id));
+ model.addAttribute("op", "修改");
+ return "user/edit";
+ }
+
+ @RequiresPermissions("user:update")
+ @RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
+ public String update(User user, RedirectAttributes redirectAttributes) {
+ userService.updateUser(user);
+ redirectAttributes.addFlashAttribute("msg", "修改成功");
+ return "redirect:/user";
+ }
+
+ @RequiresPermissions("user:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.GET)
+ public String showDeleteForm(@PathVariable("id") Long id, Model model) {
+ setCommonData(model);
+ model.addAttribute("user", userService.findOne(id));
+ model.addAttribute("op", "删除");
+ return "user/edit";
+ }
+
+ @RequiresPermissions("user:delete")
+ @RequestMapping(value = "/{id}/delete", method = RequestMethod.POST)
+ public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
+ userService.deleteUser(id);
+ redirectAttributes.addFlashAttribute("msg", "删除成功");
+ return "redirect:/user";
+ }
+
+
+ @RequiresPermissions("user:update")
+ @RequestMapping(value = "/{id}/changePassword", method = RequestMethod.GET)
+ public String showChangePasswordForm(@PathVariable("id") Long id, Model model) {
+ model.addAttribute("user", userService.findOne(id));
+ model.addAttribute("op", "修改密码");
+ return "user/changePassword";
+ }
+
+ @RequiresPermissions("user:update")
+ @RequestMapping(value = "/{id}/changePassword", method = RequestMethod.POST)
+ public String changePassword(@PathVariable("id") Long id, String newPassword, RedirectAttributes redirectAttributes) {
+ userService.changePassword(id, newPassword);
+ redirectAttributes.addFlashAttribute("msg", "修改密码成功");
+ return "redirect:/user";
+ }
+
+ private void setCommonData(Model model) {
+ model.addAttribute("organizationList", organizationService.findAll());
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/exception/DefaultExceptionHandler.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/exception/DefaultExceptionHandler.java
new file mode 100644
index 00000000..1d2e691a
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/exception/DefaultExceptionHandler.java
@@ -0,0 +1,31 @@
+package com.github.zhangkaitao.shiro.chapter23.web.exception;
+
+import org.apache.shiro.authz.UnauthorizedException;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.servlet.ModelAndView;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-12
+ *
Version: 1.0
+ */
+@ControllerAdvice
+public class DefaultExceptionHandler {
+ /**
+ * 没有权限 异常
+ *
+ * 后续根据不同的需求定制即可
+ */
+ @ExceptionHandler({UnauthorizedException.class})
+ @ResponseStatus(HttpStatus.UNAUTHORIZED)
+ public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
+ ModelAndView mv = new ModelAndView();
+ mv.addObject("exception", e);
+ mv.setViewName("unauthorized");
+ return mv;
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/shiro/filter/SysUserFilter.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/shiro/filter/SysUserFilter.java
new file mode 100644
index 00000000..e6519cb8
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/shiro/filter/SysUserFilter.java
@@ -0,0 +1,29 @@
+package com.github.zhangkaitao.shiro.chapter23.web.shiro.filter;
+
+import com.github.zhangkaitao.shiro.chapter23.Constants;
+import com.github.zhangkaitao.shiro.chapter23.service.UserService;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.web.filter.PathMatchingFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/**
+ * User: Zhang Kaitao
+ *
Date: 14-2-15
+ *
Version: 1.0
+ */
+public class SysUserFilter extends PathMatchingFilter {
+
+ @Autowired
+ private UserService userService;
+
+ @Override
+ protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
+
+ String username = (String)SecurityUtils.getSubject().getPrincipal();
+ request.setAttribute(Constants.CURRENT_USER, userService.findByUsername(username));
+ return true;
+ }
+}
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/taglib/Functions.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/taglib/Functions.java
new file mode 100644
index 00000000..1b776be7
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/chapter23/web/taglib/Functions.java
@@ -0,0 +1,167 @@
+package com.github.zhangkaitao.shiro.chapter23.web.taglib;
+
+import com.github.zhangkaitao.shiro.chapter23.entity.*;
+import com.github.zhangkaitao.shiro.chapter23.service.*;
+import com.github.zhangkaitao.shiro.spring.SpringUtils;
+import org.springframework.util.CollectionUtils;
+
+import java.util.Collection;
+
+/**
+ *
User: Zhang Kaitao
+ *
Date: 14-2-15
+ *
Version: 1.0
+ */
+public class Functions {
+
+ public static boolean in(Iterable iterable, Object element) {
+ if(iterable == null) {
+ return false;
+ }
+ return CollectionUtils.contains(iterable.iterator(), element);
+ }
+
+ public static String username(Long userId) {
+ User user = getUserService().findOne(userId);
+ if(user == null) {
+ return "";
+ }
+ return user.getUsername();
+ }
+ public static String appName(Long appId) {
+ App app = getAppService().findOne(appId);
+ if(app == null) {
+ return "";
+ }
+ return app.getName();
+ }
+
+ public static String organizationName(Long organizationId) {
+ Organization organization = getOrganizationService().findOne(organizationId);
+ if(organization == null) {
+ return "";
+ }
+ return organization.getName();
+ }
+
+ public static String organizationNames(Collection organizationIds) {
+ if(CollectionUtils.isEmpty(organizationIds)) {
+ return "";
+ }
+
+ StringBuilder s = new StringBuilder();
+ for(Long organizationId : organizationIds) {
+ Organization organization = getOrganizationService().findOne(organizationId);
+ if(organization == null) {
+ return "";
+ }
+ s.append(organization.getName());
+ s.append(",");
+ }
+
+ if(s.length() > 0) {
+ s.deleteCharAt(s.length() - 1);
+ }
+
+ return s.toString();
+ }
+ public static String roleName(Long roleId) {
+ Role role = getRoleService().findOne(roleId);
+ if(role == null) {
+ return "";
+ }
+ return role.getDescription();
+ }
+
+ public static String roleNames(Collection roleIds) {
+ if(CollectionUtils.isEmpty(roleIds)) {
+ return "";
+ }
+
+ StringBuilder s = new StringBuilder();
+ for(Long roleId : roleIds) {
+ Role role = getRoleService().findOne(roleId);
+ if(role == null) {
+ return "";
+ }
+ s.append(role.getDescription());
+ s.append(",");
+ }
+
+ if(s.length() > 0) {
+ s.deleteCharAt(s.length() - 1);
+ }
+
+ return s.toString();
+ }
+ public static String resourceName(Long resourceId) {
+ Resource resource = getResourceService().findOne(resourceId);
+ if(resource == null) {
+ return "";
+ }
+ return resource.getName();
+ }
+ public static String resourceNames(Collection resourceIds) {
+ if(CollectionUtils.isEmpty(resourceIds)) {
+ return "";
+ }
+
+ StringBuilder s = new StringBuilder();
+ for(Long resourceId : resourceIds) {
+ Resource resource = getResourceService().findOne(resourceId);
+ if(resource == null) {
+ return "";
+ }
+ s.append(resource.getName());
+ s.append(",");
+ }
+
+ if(s.length() > 0) {
+ s.deleteCharAt(s.length() - 1);
+ }
+
+ return s.toString();
+ }
+
+ private static OrganizationService organizationService;
+ private static RoleService roleService;
+ private static ResourceService resourceService;
+ private static UserService userService;
+ private static AppService appService;
+
+ public static UserService getUserService() {
+ if(userService == null) {
+ userService = SpringUtils.getBean(UserService.class);
+ }
+ return userService;
+ }
+
+ public static AppService getAppService() {
+ if(appService == null) {
+ appService = SpringUtils.getBean(AppService.class);
+ }
+ return appService;
+ }
+
+ public static OrganizationService getOrganizationService() {
+ if(organizationService == null) {
+ organizationService = SpringUtils.getBean(OrganizationService.class);
+ }
+ return organizationService;
+ }
+
+ public static RoleService getRoleService() {
+ if(roleService == null) {
+ roleService = SpringUtils.getBean(RoleService.class);
+ }
+ return roleService;
+ }
+
+ public static ResourceService getResourceService() {
+ if(resourceService == null) {
+ resourceService = SpringUtils.getBean(ResourceService.class);
+ }
+ return resourceService;
+ }
+}
+
diff --git a/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/spring/SpringUtils.java b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/spring/SpringUtils.java
new file mode 100644
index 00000000..4ea981fe
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/java/com/github/zhangkaitao/shiro/spring/SpringUtils.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2005-2012 https://github.com/zhangkaitao
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+package com.github.zhangkaitao.shiro.spring;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+
+public final class SpringUtils implements BeanFactoryPostProcessor {
+
+ private static ConfigurableListableBeanFactory beanFactory; // Spring应用上下文环境
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ SpringUtils.beanFactory = beanFactory;
+ }
+
+ /**
+ * 获取对象
+ *
+ * @param name
+ * @return Object 一个以所给名字注册的bean的实例
+ * @throws org.springframework.beans.BeansException
+ *
+ */
+ @SuppressWarnings("unchecked")
+ public static T getBean(String name) throws BeansException {
+ return (T) beanFactory.getBean(name);
+ }
+
+ /**
+ * 获取类型为requiredType的对象
+ *
+ * @param clz
+ * @return
+ * @throws org.springframework.beans.BeansException
+ *
+ */
+ public static T getBean(Class clz) throws BeansException {
+ @SuppressWarnings("unchecked")
+ T result = (T) beanFactory.getBean(clz);
+ return result;
+ }
+
+ /**
+ * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+ *
+ * @param name
+ * @return boolean
+ */
+ public static boolean containsBean(String name) {
+ return beanFactory.containsBean(name);
+ }
+
+ /**
+ * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+ *
+ * @param name
+ * @return boolean
+ * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+ *
+ */
+ public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+ return beanFactory.isSingleton(name);
+ }
+
+ /**
+ * @param name
+ * @return Class 注册对象的类型
+ * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+ *
+ */
+ public static Class> getType(String name) throws NoSuchBeanDefinitionException {
+ return beanFactory.getType(name);
+ }
+
+ /**
+ * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+ *
+ * @param name
+ * @return
+ * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+ *
+ */
+ public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
+ return beanFactory.getAliases(name);
+ }
+
+}
diff --git a/shiro-example-chapter23-server/src/main/resources/ehcache/ehcache-shiro.xml b/shiro-example-chapter23-server/src/main/resources/ehcache/ehcache-shiro.xml
new file mode 100644
index 00000000..92d603a9
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/resources/ehcache/ehcache-shiro.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shiro-example-chapter23-server/src/main/resources/resources.properties b/shiro-example-chapter23-server/src/main/resources/resources.properties
new file mode 100644
index 00000000..32d55c40
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/resources/resources.properties
@@ -0,0 +1,24 @@
+#dataSource configure
+connection.url=jdbc:mysql://localhost:3306/shiro3
+connection.username=root
+connection.password=
+
+#druid datasource
+#ο https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_DruidDataSource%E5%8F%82%E8%80%83%E9%85%8D%E7%BD%AE
+druid.initialSize=10
+druid.minIdle=10
+druid.maxActive=50
+druid.maxWait=60000
+druid.timeBetweenEvictionRunsMillis=60000
+druid.minEvictableIdleTimeMillis=300000
+druid.validationQuery=SELECT 'x'
+druid.testWhileIdle=true
+druid.testOnBorrow=false
+druid.testOnReturn=false
+druid.poolPreparedStatements=true
+druid.maxPoolPreparedStatementPerConnectionSize=20
+druid.filters=wall,stat
+
+#shiro
+password.algorithmName=md5
+password.hashIterations=2
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/resources/spring-config-shiro.xml b/shiro-example-chapter23-server/src/main/resources/spring-config-shiro.xml
new file mode 100644
index 00000000..b6f5977b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/resources/spring-config-shiro.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /remoteService = anon
+ /login = authc
+ /logout = logout
+ /authenticated = authc
+ /** = user,sysUser
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/resources/spring-config.xml b/shiro-example-chapter23-server/src/main/resources/spring-config.xml
new file mode 100644
index 00000000..bd148069
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/resources/spring-config.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/resources/spring-mvc-remote-service.xml b/shiro-example-chapter23-server/src/main/resources/spring-mvc-remote-service.xml
new file mode 100644
index 00000000..599baa37
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/resources/spring-mvc-remote-service.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/resources/spring-mvc-shiro.xml b/shiro-example-chapter23-server/src/main/resources/spring-mvc-shiro.xml
new file mode 100644
index 00000000..e28d8e1e
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/resources/spring-mvc-shiro.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/resources/spring-mvc.xml b/shiro-example-chapter23-server/src/main/resources/spring-mvc.xml
new file mode 100644
index 00000000..24fdbddc
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/resources/spring-mvc.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/app/edit.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/app/edit.jsp
new file mode 100644
index 00000000..687ec980
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/app/edit.jsp
@@ -0,0 +1,36 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="zhangfn" uri="http://github.com/zhangkaitao/tags/zhang-functions" %>
+
+
+
+
+
+
+
+
+
+
+
+
+ 应用名称:
+
+
+
+
+ 应用KEY:
+
+
+
+
+ 应用安全码:
+
+
+
+ ${op}
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/app/list.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/app/list.jsp
new file mode 100644
index 00000000..a680de9a
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/app/list.jsp
@@ -0,0 +1,49 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="zhangfn" uri="http://github.com/zhangkaitao/tags/zhang-functions" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+
+
+
+
+
+
+ ${msg}
+
+
+
+ 应用新增
+
+
+
+
+ 应用名称
+ 应用KEY
+ 应用安全码
+ 操作
+
+
+
+
+
+ ${app.name}
+ ${app.appKey}
+ ${app.appSecret}
+
+
+ 修改
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/authorization/edit.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/authorization/edit.jsp
new file mode 100644
index 00000000..4f7fed2a
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/authorization/edit.jsp
@@ -0,0 +1,36 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="zhangfn" uri="http://github.com/zhangkaitao/tags/zhang-functions" %>
+
+
+
+
+
+
+
+
+
+
+
+ 应用:
+
+
+
+
+ 用户:
+
+
+
+
+ 角色列表:
+
+ (按住shift键多选)
+
+
+ ${op}
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/authorization/list.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/authorization/list.jsp
new file mode 100644
index 00000000..fcfa2ca5
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/authorization/list.jsp
@@ -0,0 +1,49 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="zhangfn" uri="http://github.com/zhangkaitao/tags/zhang-functions" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+
+
+
+
+
+
+ ${msg}
+
+
+
+ 授权新增
+
+
+
+
+ 应用
+ 用户
+ 角色列表
+ 操作
+
+
+
+
+
+ ${zhangfn:appName(authorization.appId)}
+ ${zhangfn:username(authorization.userId)}
+ ${zhangfn:roleNames(authorization.roleIds)}
+
+
+ 修改
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/index.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/index.jsp
new file mode 100644
index 00000000..cc300e49
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/index.jsp
@@ -0,0 +1,35 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+ Shiro综合案例
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/login.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/login.jsp
new file mode 100644
index 00000000..f2559b51
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/login.jsp
@@ -0,0 +1,19 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+ 登录
+
+
+
+
+${error}
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/appendChild.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/appendChild.jsp
new file mode 100644
index 00000000..b84ed937
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/appendChild.jsp
@@ -0,0 +1,30 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 父节点名称:
+ ${parent.name}
+
+
+
+ 子节点名称:
+
+
+
+ 新增子节点
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/index.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/index.jsp
new file mode 100644
index 00000000..e49108a5
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/index.jsp
@@ -0,0 +1,25 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/maintain.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/maintain.jsp
new file mode 100644
index 00000000..df124c82
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/maintain.jsp
@@ -0,0 +1,73 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 名称:
+
+
+
+ 修改
+
+
+
+
+ 删除
+
+
+
+
+ 添加子节点
+
+
+
+
+ 移动节点
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/move.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/move.jsp
new file mode 100644
index 00000000..fa7511bf
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/move.jsp
@@ -0,0 +1,98 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/success.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/success.jsp
new file mode 100644
index 00000000..bdb3f013
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/success.jsp
@@ -0,0 +1,9 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+
+ 操作成功,点击刷新树
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/tree.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/tree.jsp
new file mode 100644
index 00000000..962be698
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/organization/tree.jsp
@@ -0,0 +1,40 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/resource/edit.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/resource/edit.jsp
new file mode 100644
index 00000000..21ffe9c4
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/resource/edit.jsp
@@ -0,0 +1,49 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 父节点名称:
+ ${parent.name}
+
+
+
+
+ 子 名称:
+
+
+
+ 类型:
+
+
+
+
+ URL路径:
+
+
+
+
+
+ 权限字符串:
+
+
+
+ ${op}
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/resource/list.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/resource/list.jsp
new file mode 100644
index 00000000..f816bc29
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/resource/list.jsp
@@ -0,0 +1,76 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+
+
+
+
+
+
+
+
+
+ ${msg}
+
+
+
+
+
+ 名称
+ 类型
+ URL路径
+ 权限字符串
+ 操作
+
+
+
+
+ data-tt-parent-id='${resource.parentId}'>
+ ${resource.name}
+ ${resource.type.info}
+ ${resource.url}
+ ${resource.permission}
+
+
+
+ 添加子节点
+
+
+
+
+ 修改
+
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/role/edit.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/role/edit.jsp
new file mode 100644
index 00000000..f55f967f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/role/edit.jsp
@@ -0,0 +1,119 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="zhangfn" uri="http://github.com/zhangkaitao/tags/zhang-functions" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 角色名:
+
+
+
+
+ 角色描述:
+
+
+
+
+
+ 拥有的资源列表:
+
+
+
+
+
+ ${op}
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/role/list.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/role/list.jsp
new file mode 100644
index 00000000..77b5883e
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/role/list.jsp
@@ -0,0 +1,49 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="zhangfn" uri="http://github.com/zhangkaitao/tags/zhang-functions" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+
+
+
+
+
+
+ ${msg}
+
+
+
+ 角色新增
+
+
+
+
+ 角色名称
+ 角色描述
+ 拥有的资源
+ 操作
+
+
+
+
+
+ ${role.role}
+ ${role.description}
+ ${zhangfn:resourceNames(role.resourceIds)}
+
+
+ 修改
+
+
+
+ 删除
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/unauthorized.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/unauthorized.jsp
new file mode 100644
index 00000000..c6758214
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/unauthorized.jsp
@@ -0,0 +1,11 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+ 没有权限
+
+
+
+
+您没有权限[${exception.message}]
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/changePassword.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/changePassword.jsp
new file mode 100644
index 00000000..744e15a6
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/changePassword.jsp
@@ -0,0 +1,20 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/edit.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/edit.jsp
new file mode 100644
index 00000000..47d01a01
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/edit.jsp
@@ -0,0 +1,120 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="zhangfn" uri="http://github.com/zhangkaitao/tags/zhang-functions" %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 用户名:
+
+
+
+
+
+ 密码:
+
+
+
+
+
+ 所属组织:
+
+
+
+
+
+ ${op}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/list.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/list.jsp
new file mode 100644
index 00000000..be3197ca
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/user/list.jsp
@@ -0,0 +1,52 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@taglib prefix="zhangfn" uri="http://github.com/zhangkaitao/tags/zhang-functions" %>
+<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
+
+
+
+
+
+
+
+
+ ${msg}
+
+
+
+ 用户新增
+
+
+
+
+
+ 用户名
+ 所属组织
+ 操作
+
+
+
+
+
+ ${user.username}
+ ${zhangfn:organizationName(user.organizationId)}
+
+
+ 修改
+
+
+
+ 删除
+
+
+
+ 改密
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/welcome.jsp b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/welcome.jsp
new file mode 100644
index 00000000..eafd8fef
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/jsp/welcome.jsp
@@ -0,0 +1,6 @@
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+欢迎学习Shiro综合案例,更多案例请访问我的github
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/API_cn.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/API_cn.html
new file mode 100644
index 00000000..60bd7d15
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/API_cn.html
@@ -0,0 +1,103 @@
+
+
+
+
+ API 文档 [zTree -- jQuery 树插件]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/API_en.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/API_en.html
new file mode 100644
index 00000000..69e05804
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/API_en.html
@@ -0,0 +1,102 @@
+
+
+
+
+ API Document [zTree -- jQuery tree plug-ins.]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/api.js b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/api.js
new file mode 100644
index 00000000..84fb59ef
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/api.js
@@ -0,0 +1,591 @@
+var apiContent = {
+ zTree_Setting: null,
+ zTree_Node: null,
+ zTree_Function: null,
+ overlayDiv : null,
+ overlayContent : null,
+ overlayDetailDiv : null,
+ overlayCloseBtn: null,
+ overlayArrow: null,
+ contentBoxDiv : null,
+ settingDiv : null,
+ functionDiv : null,
+ overlaySearch: null,
+ searchKey: null,
+ searchResultInput: null,
+ searchPrevBtn: null,
+ searchNextBtn: null,
+ apiCache: {},
+ lastValue: "",
+ searchNodes: [],
+ searchNodesCur: 0,
+
+ _init: function() {
+ this.overlayDiv = $("#overlayDiv");
+ this.overlayContent = $("#overlayContent");
+ this.overlayDetailDiv = $("#overlayDetailDiv");
+ this.overlayCloseBtn = $("#overlayDivCloseBtn");
+ this.overlayArrow = $("#overlayDivArrow");
+ this.contentBoxDiv = $("#contentBox");
+ this.settingDiv = $("#api_setting");
+ this.functionDiv = $("#api_function");
+ this.searchKey = $(".searchKey");
+ this.overlaySearch = $(".overlaySearch");
+ this.searchResultInput = $(".searchResult");
+ this.searchPrevBtn = $(".searchPrev");
+ this.searchNextBtn = $(".searchNext");
+ var setting = {
+ view: {
+ fontCss: this.getFontCss,
+ showLine: false,
+ showIcon: this.showIcon,
+ showTitle: this.getTitle,
+ selectedMulti: false,
+ dblClickExpand: false
+ },
+ data: {
+ key: {
+ title: "tt"
+ },
+ simpleData: {
+ enable:true,
+ idKey: "id",
+ pIdKey: "pId",
+ rootPId: ""
+ }
+ },
+ callback: {
+ onNodeCreated: this.onNodeCreated,
+ beforeClick: this.beforeClick
+ }
+ }
+ var setting_nodes =[
+ {id:1, pId:0, t:"setting", name:"var setting = {", open:true},
+ {id:11, pId:1, t:"treeId", name:"treeId : \"\",", iconSkin:"core", showAPI:true},
+ {id:12, pId:1, t:"treeObj", name:"treeObj : null,", iconSkin:"core", showAPI:true},
+ {id:121, pId:1, name:""},
+
+ {id:20, pId:1, t:"async", name:"async : {", open:true},
+ {id:201, pId:20, t:"autoParam", name:"autoParam : [],", iconSkin:"core", showAPI:true},
+ {id:208, pId:20, t:"contentType", name:"contentType : \"application...\",", iconSkin:"core", showAPI:true},
+ {id:202, pId:20, t:"dataFilter", name:"dataFilter : null,", iconSkin:"core", showAPI:true},
+ {id:203, pId:20, t:"dataType", name:"dataType : \"text\",", iconSkin:"core", showAPI:true},
+ {id:204, pId:20, t:"enable", name:"enable : false,", iconSkin:"core", showAPI:true},
+ {id:205, pId:20, t:"otherParam", name:"otherParam : [],", iconSkin:"core", showAPI:true},
+ {id:206, pId:20, t:"type", name:"type : \"post\",", iconSkin:"core", showAPI:true},
+ {id:207, pId:20, t:"url", name:"url : \"\"", iconSkin:"core", showAPI:true},
+ {id:21, pId:1, name:"},"},
+ {id:22, pId:1, name:""},
+
+ {id:30, pId:1, t:"callback", name:"callback : {", open:true},
+ {id:3001, pId:30, t:"beforeAsync", name:"beforeAsync : null,", iconSkin:"core", showAPI:true},
+ {id:3002, pId:30, t:"beforeCheck", name:"beforeCheck : null,", iconSkin:"check", showAPI:true},
+ {id:3003, pId:30, t:"beforeClick", name:"beforeClick : null,", iconSkin:"core", showAPI:true},
+ {id:3004, pId:30, t:"beforeCollapse", name:"beforeCollapse : null,", iconSkin:"core", showAPI:true},
+ {id:3004, pId:30, t:"beforeDblClick", name:"beforeDblClick : null,", iconSkin:"core", showAPI:true},
+ {id:3005, pId:30, t:"beforeDrag", name:"beforeDrag : null,", iconSkin:"edit", showAPI:true},
+ {id:3006, pId:30, t:"beforeDragOpen", name:"beforeDragOpen : null,", iconSkin:"edit", showAPI:true},
+ {id:3007, pId:30, t:"beforeDrop", name:"beforeDrop : null,", iconSkin:"edit", showAPI:true},
+ {id:3029, pId:30, t:"beforeEditName", name:"beforeEditName : null,", iconSkin:"edit", showAPI:true},
+ {id:3008, pId:30, t:"beforeExpand", name:"beforeExpand : null,", iconSkin:"core", showAPI:true},
+ {id:3009, pId:30, t:"beforeMouseDown", name:"beforeMouseDown : null,", iconSkin:"core", showAPI:true},
+ {id:3010, pId:30, t:"beforeMouseUp", name:"beforeMouseUp : null,", iconSkin:"core", showAPI:true},
+ {id:3011, pId:30, t:"beforeRemove", name:"beforeRemove : null,", iconSkin:"edit", showAPI:true},
+ {id:3012, pId:30, t:"beforeRename", name:"beforeRename : null,", iconSkin:"edit", showAPI:true},
+ {id:3013, pId:30, t:"beforeRightClick", name:"beforeRightClick : null,", iconSkin:"core", showAPI:true},
+ {id:3014, pId:30, name:""},
+ {id:3015, pId:30, t:"onAsyncError", name:"onAsyncError : null,", iconSkin:"core", showAPI:true},
+ {id:3016, pId:30, t:"onAsyncSuccess", name:"onAsyncSuccess : null,", iconSkin:"core", showAPI:true},
+ {id:3017, pId:30, t:"onCheck", name:"onCheck : null,", iconSkin:"check", showAPI:true},
+ {id:3018, pId:30, t:"onClick", name:"onClick : null,", iconSkin:"core", showAPI:true},
+ {id:3019, pId:30, t:"onCollapse", name:"onCollapse : null,", iconSkin:"core", showAPI:true},
+ {id:3019, pId:30, t:"onDblClick", name:"onDblClick : null,", iconSkin:"core", showAPI:true},
+ {id:3020, pId:30, t:"onDrag", name:"onDrag : null,", iconSkin:"edit", showAPI:true},
+ {id:3021, pId:30, t:"onDrop", name:"onDrop : null,", iconSkin:"edit", showAPI:true},
+ {id:3022, pId:30, t:"onExpand", name:"onExpand : null,", iconSkin:"core", showAPI:true},
+ {id:3023, pId:30, t:"onMouseDown", name:"onMouseDown : null,", iconSkin:"core", showAPI:true},
+ {id:3024, pId:30, t:"onMouseUp", name:"onMouseUp : null,", iconSkin:"core", showAPI:true},
+ {id:3025, pId:30, t:"onNodeCreated", name:"onNodeCreated : null,", iconSkin:"core", showAPI:true},
+ {id:3026, pId:30, t:"onRemove", name:"onRemove : null,", iconSkin:"edit", showAPI:true},
+ {id:3027, pId:30, t:"onRename", name:"onRename : null,", iconSkin:"edit", showAPI:true},
+ {id:3028, pId:30, t:"onRightClick", name:"onRightClick : null", iconSkin:"core", showAPI:true},
+ {id:31, pId:1, name:"},"},
+ {id:32, pId:1, name:""},
+
+ {id:40, pId:1, t:"check", name:"check : {", open:true},
+ {id:405, pId:40, t:"autoCheckTrigger", name:"autoCheckTrigger : false,", iconSkin:"check", showAPI:true},
+ {id:401, pId:40, t:"chkboxType", name:"chkboxType : {\"Y\": \"ps\", \"N\": \"ps\"},", iconSkin:"check", showAPI:true},
+ {id:402, pId:40, t:"chkStyle", name:"chkStyle : \"checkbox\",", iconSkin:"check", showAPI:true},
+ {id:403, pId:40, t:"enable", name:"enable : false,", iconSkin:"check", showAPI:true},
+ {id:406, pId:40, t:"nocheckInherit", name:"nocheckInherit : false", iconSkin:"check", showAPI:true},
+ {id:407, pId:40, t:"chkDisabledInherit", name:"chkDisabledInherit : false", iconSkin:"check", showAPI:true},
+ {id:404, pId:40, t:"radioType", name:"radioType : \"level\"", iconSkin:"check", showAPI:true},
+ {id:41, pId:1, name:"},"},
+ {id:42, pId:1, name:""},
+
+ {id:50, pId:1, t:"data", name:"data : {", open:true},
+ {id:500, pId:50, t:"keep", name:"keep : {", open:true},
+ {id:5001, pId:500, t:"leaf", name:"leaf : false,", iconSkin:"core", showAPI:true},
+ {id:5002, pId:500, t:"parent", name:"parent : false", iconSkin:"core", showAPI:true},
+ {id:501, pId:50, name:"},"},
+
+ {id:510, pId:50, t:"key", name:"key : {", open:true},
+ {id:5101, pId:510, t:"checked", name:"checked : \"checked\",", iconSkin:"check", showAPI:true},
+ {id:5102, pId:510, t:"children", name:"children : \"children\",", iconSkin:"core", showAPI:true},
+ {id:5103, pId:510, t:"name", name:"name : \"name\",", iconSkin:"core", showAPI:true},
+ {id:5104, pId:510, t:"title", name:"title : \"\"", iconSkin:"core", showAPI:true},
+ {id:5105, pId:510, t:"url", name:"url : \"url\"", iconSkin:"core", showAPI:true},
+ {id:511, pId:50, name:"},"},
+
+ {id:520, pId:50, t:"simpleData", name:"simpleData : {", open:true},
+ {id:5201, pId:520, t:"enable", name:"enable : false,", iconSkin:"core", showAPI:true},
+ {id:5202, pId:520, t:"idKey", name:"idKey : \"id\",", iconSkin:"core", showAPI:true},
+ {id:5203, pId:520, t:"pIdKey", name:"pIdKey : \"pId\",", iconSkin:"core", showAPI:true},
+ {id:5204, pId:520, t:"rootPId", name:"rootPId : null", iconSkin:"core", showAPI:true},
+ {id:521, pId:50, name:"}"},
+ {id:51, pId:1, name:"},"},
+ {id:52, pId:1, name:""},
+
+ {id:60, pId:1, t:"edit", name:"edit : {", open:true},
+ {id:601, pId:60, t:"drag", name:"drag : {", open:true},
+ {id:60111, pId:601, t:"autoExpandTrigger", name:"autoExpandTrigger : true,", iconSkin:"edit", showAPI:true},
+ {id:60101, pId:601, t:"isCopy", name:"isCopy : true,", iconSkin:"edit", showAPI:true},
+ {id:60102, pId:601, t:"isMove", name:"isMove : true,", iconSkin:"edit", showAPI:true},
+ {id:60103, pId:601, t:"prev", name:"prev : true,", iconSkin:"edit", showAPI:true},
+ {id:60104, pId:601, t:"next", name:"next : true,", iconSkin:"edit", showAPI:true},
+ {id:60105, pId:601, t:"inner", name:"inner : true,", iconSkin:"edit", showAPI:true},
+ {id:60107, pId:601, t:"borderMax", name:"borderMax : 10,", iconSkin:"edit", showAPI:true},
+ {id:60108, pId:601, t:"borderMin", name:"borderMin : -5,", iconSkin:"edit", showAPI:true},
+ {id:60106, pId:601, t:"minMoveSize", name:"minMoveSize : 5,", iconSkin:"edit", showAPI:true},
+ {id:60109, pId:601, t:"maxShowNodeNum", name:"maxShowNodeNum : 5,", iconSkin:"edit", showAPI:true},
+ {id:60110, pId:601, t:"autoOpenTime", name:"autoOpenTime : 500", iconSkin:"edit", showAPI:true},
+ {id:602, pId:60, name:"},"},
+ {id:608, pId:60, t:"editNameSelectAll", name:"editNameSelectAll : false,", iconSkin:"edit", showAPI:true},
+ {id:603, pId:60, t:"enable", name:"enable : false,", iconSkin:"edit", showAPI:true},
+ {id:604, pId:60, t:"removeTitle", name:"removeTitle : \"remove\",", iconSkin:"edit", showAPI:true},
+ {id:605, pId:60, t:"renameTitle", name:"renameTitle : \"rename\",", iconSkin:"edit", showAPI:true},
+ {id:606, pId:60, t:"showRemoveBtn", name:"showRemoveBtn : true,", iconSkin:"edit", showAPI:true},
+ {id:607, pId:60, t:"showRenameBtn", name:"showRenameBtn : true", iconSkin:"edit", showAPI:true},
+ {id:61, pId:1, name:"},"},
+ {id:62, pId:1, name:""},
+
+ {id:70, pId:1, t:"view", name:"view : {", open:true},
+ {id:7001, pId:70, t:"addDiyDom", name:"addDiyDom : null,", iconSkin:"core", showAPI:true},
+ {id:7002, pId:70, t:"addHoverDom", name:"addHoverDom : null,", iconSkin:"edit", showAPI:true},
+ {id:7003, pId:70, t:"autoCancelSelected", name:"autoCancelSelected : true,", iconSkin:"core", showAPI:true},
+ {id:7004, pId:70, t:"dblClickExpand", name:"dblClickExpand : true,", iconSkin:"core", showAPI:true},
+ {id:7005, pId:70, t:"expandSpeed", name:"expandSpeed : \"fast\",", iconSkin:"core", showAPI:true},
+ {id:7006, pId:70, t:"fontCss", name:"fontCss : {},", iconSkin:"core", showAPI:true},
+ {id:7012, pId:70, t:"nameIsHTML", name:"nameIsHTML : false,", iconSkin:"core", showAPI:true},
+ {id:7007, pId:70, t:"removeHoverDom", name:"removeHoverDom : null,", iconSkin:"edit", showAPI:true},
+ {id:7008, pId:70, t:"selectedMulti", name:"selectedMulti : true,", iconSkin:"core", showAPI:true},
+ {id:7009, pId:70, t:"showIcon", name:"showIcon : true,", iconSkin:"core", showAPI:true},
+ {id:7010, pId:70, t:"showLine", name:"showLine : true,", iconSkin:"core", showAPI:true},
+ {id:7011, pId:70, t:"showTitle", name:"showTitle : true,", iconSkin:"core", showAPI:true},
+ {id:7012, pId:70, t:"txtSelectedEnable", name:"txtSelectedEnable : false", iconSkin:"core", showAPI:true},
+ {id:71, pId:1, name:"}"},
+
+ {id:2, pId:0, name:"}"}
+ ];
+
+ var treenode_nodes =[
+ {id:1, pId:0, t:"treeNode", name:"treeNode : {", open:true},
+ {id:101, pId:1, t:"checked", name:"checked", iconSkin:"check", showAPI:true},
+ {id:102, pId:1, t:"children", name:"children", iconSkin:"core", showAPI:true},
+ {id:128, pId:1, t:"chkDisabled", name:"chkDisabled", iconSkin:"check", showAPI:true},
+ {id:127, pId:1, t:"click", name:"click", iconSkin:"core", showAPI:true},
+ {id:103, pId:1, t:"getCheckStatus", name:"getCheckStatus ()", iconSkin:"check", showAPI:true},
+ {id:104, pId:1, t:"getNextNode", name:"getNextNode ()", iconSkin:"core", showAPI:true},
+ {id:105, pId:1, t:"getParentNode", name:"getParentNode ()", iconSkin:"core", showAPI:true},
+ {id:106, pId:1, t:"getPreNode", name:"getPreNode ()", iconSkin:"core", showAPI:true},
+ {id:129, pId:1, t:"halfCheck", name:"halfCheck", iconSkin:"check", showAPI:true},
+ {id:107, pId:1, t:"icon", name:"icon", iconSkin:"core", showAPI:true},
+ {id:108, pId:1, t:"iconClose", name:"iconClose", iconSkin:"core", showAPI:true},
+ {id:109, pId:1, t:"iconOpen", name:"iconOpen", iconSkin:"core", showAPI:true},
+ {id:110, pId:1, t:"iconSkin", name:"iconSkin", iconSkin:"core", showAPI:true},
+ {id:131, pId:1, t:"isHidden", name:"isHidden", iconSkin:"hide", showAPI:true},
+ {id:111, pId:1, t:"isParent", name:"isParent", iconSkin:"core", showAPI:true},
+ {id:132, pId:1, t:"name", name:"name", iconSkin:"core", showAPI:true},
+ {id:112, pId:1, t:"nocheck", name:"nocheck", iconSkin:"check", showAPI:true},
+ {id:113, pId:1, t:"open", name:"open", iconSkin:"core", showAPI:true},
+ {id:133, pId:1, t:"target", name:"target", iconSkin:"core", showAPI:true},
+ {id:134, pId:1, t:"url", name:"url", iconSkin:"core", showAPI:true},
+ {id:114, pId:1, t:"diy", name:"*DIY*", iconSkin:"core", showAPI:true},
+ {id:115, pId:1, name:""},
+ {id:116, pId:1, t:"check_Child_State", name:"[check_Child_State]", iconSkin:"check", showAPI:true},
+ {id:117, pId:1, t:"check_Focus", name:"[check_Focus]", iconSkin:"check", showAPI:true},
+ {id:118, pId:1, t:"checkedOld", name:"[checkedOld]", iconSkin:"check", showAPI:true},
+ {id:119, pId:1, t:"editNameFlag", name:"[editNameFlag]", iconSkin:"edit", showAPI:true},
+ {id:120, pId:1, t:"isAjaxing", name:"[isAjaxing]", iconSkin:"core", showAPI:true},
+ {id:121, pId:1, t:"isFirstNode", name:"[isFirstNode]", iconSkin:"core", showAPI:true},
+ {id:122, pId:1, t:"isHover", name:"[isHover]", iconSkin:"edit", showAPI:true},
+ {id:123, pId:1, t:"isLastNode", name:"[isLastNode]", iconSkin:"core", showAPI:true},
+ {id:124, pId:1, t:"level", name:"[level]", iconSkin:"core", showAPI:true},
+ {id:125, pId:1, t:"parentTId", name:"[parentTId]", iconSkin:"core", showAPI:true},
+ {id:126, pId:1, t:"tId", name:"[tId]", iconSkin:"core", showAPI:true},
+ {id:130, pId:1, t:"zAsync", name:"[zAsync]", iconSkin:"core", showAPI:true},
+ {id:2, pId:0, name:"}"}
+ ];
+
+ var function_nodes =[
+ {id:1, pId:0, t:"$.fn.zTree", name:"$.fn.zTree : {", open:true},
+ {id:11, pId:1, t:"init", name:"init (obj, zSetting, zNodes)", iconSkin:"core", showAPI:true},
+ {id:12, pId:1, t:"getZTreeObj", name:"getZTreeObj (treeId)", iconSkin:"core", showAPI:true},
+ {id:14, pId:1, t:"destroy", name:"destroy (treeId)", iconSkin:"core", showAPI:true},
+ {id:13, pId:1, t:"_z", name:"_z : {tools, view, event, data}", iconSkin:"core", showAPI:true},
+ {id:2, pId:0, name:"}"},
+ {id:3, pId:0, name:""},
+ {id:4, pId:0, t:"zTreeObj", name:"zTreeObj : {", open:true},
+ {id:401, pId:4, t:"setting", name:"setting", iconSkin:"core", showAPI:true},
+ {id:402, pId:4, t:"addNodes", name:"addNodes (parentNode, newNodes, isSilent)", iconSkin:"core", showAPI:true},
+ {id:403, pId:4, t:"cancelEditName", name:"cancelEditName (newName)", iconSkin:"edit", showAPI:true},
+ {id:404, pId:4, t:"cancelSelectedNode", name:"cancelSelectedNode (node)", iconSkin:"core", showAPI:true},
+ {id:405, pId:4, t:"checkAllNodes", name:"checkAllNodes (checked)", iconSkin:"check", showAPI:true},
+ {id:406, pId:4, t:"checkNode", name:"checkNode (node, checked, checkTypeFlag, callbackFlag)", iconSkin:"check", showAPI:true},
+ {id:407, pId:4, t:"copyNode", name:"copyNode (targetNode, node, moveType, isSilent)", iconSkin:"edit", showAPI:true},
+ {id:436, pId:4, t:"destroy", name:"destroy ()", iconSkin:"core", showAPI:true},
+ {id:408, pId:4, t:"editName", name:"editName (node)", iconSkin:"edit", showAPI:true},
+ {id:409, pId:4, t:"expandAll", name:"expandAll (expandFlag)", iconSkin:"core", showAPI:true},
+ {id:410, pId:4, t:"expandNode", name:"expandNode (node, expandFlag, sonSign, focus, callbackFlag)", iconSkin:"core", showAPI:true},
+ {id:411, pId:4, t:"getChangeCheckedNodes", name:"getChangeCheckedNodes ()", iconSkin:"check", showAPI:true},
+ {id:412, pId:4, t:"getCheckedNodes", name:"getCheckedNodes (checked)", iconSkin:"check", showAPI:true},
+ {id:413, pId:4, t:"getNodeByParam", name:"getNodeByParam (key, value, parentNode)", iconSkin:"core", showAPI:true},
+ {id:414, pId:4, t:"getNodeByTId", name:"getNodeByTId (tId)", iconSkin:"core", showAPI:true},
+ {id:415, pId:4, t:"getNodeIndex", name:"getNodeIndex (node)", iconSkin:"core", showAPI:true},
+ {id:416, pId:4, t:"getNodes", name:"getNodes ()", iconSkin:"core", showAPI:true},
+ {id:431, pId:4, t:"getNodesByFilter", name:"getNodesByFilter (filter, isSingle, parentNode, invokeParam)", iconSkin:"core", showAPI:true},
+ {id:417, pId:4, t:"getNodesByParam", name:"getNodesByParam (key, value, parentNode)", iconSkin:"core", showAPI:true},
+ {id:418, pId:4, t:"getNodesByParamFuzzy", name:"getNodesByParamFuzzy (key, value, parentNode)", iconSkin:"core", showAPI:true},
+ {id:419, pId:4, t:"getSelectedNodes", name:"getSelectedNodes ()", iconSkin:"core", showAPI:true},
+ {id:432, pId:4, t:"hideNode", name:"hideNode (node)", iconSkin:"hide", showAPI:true},
+ {id:433, pId:4, t:"hideNodes", name:"hideNodes (nodes)", iconSkin:"hide", showAPI:true},
+ {id:420, pId:4, t:"moveNode", name:"moveNode (targetNode, node, moveType, isSilent)", iconSkin:"edit", showAPI:true},
+ {id:421, pId:4, t:"reAsyncChildNodes", name:"reAsyncChildNodes (parentNode, reloadType, isSilent)", iconSkin:"core", showAPI:true},
+ {id:422, pId:4, t:"refresh", name:"refresh ()", iconSkin:"core", showAPI:true},
+ {id:423, pId:4, t:"removeChildNodes", name:"removeChildNodes (parentNode)", iconSkin:"core", showAPI:true},
+ {id:424, pId:4, t:"removeNode", name:"removeNode (node, callbackFlag)", iconSkin:"core", showAPI:true},
+ {id:425, pId:4, t:"selectNode", name:"selectNode (node, addFlag)", iconSkin:"core", showAPI:true},
+ {id:430, pId:4, t:"setChkDisabled", name:"setChkDisabled (node, disabled, inheritParent, inheritChildren)", iconSkin:"check", showAPI:true},
+ {id:426, pId:4, t:"setEditable", name:"setEditable (editable)", iconSkin:"edit", showAPI:true},
+ {id:434, pId:4, t:"showNode", name:"showNode (node)", iconSkin:"hide", showAPI:true},
+ {id:435, pId:4, t:"showNodes", name:"showNodes (nodes)", iconSkin:"hide", showAPI:true},
+ {id:427, pId:4, t:"transformToArray", name:"transformToArray (nodes)", iconSkin:"core", showAPI:true},
+ {id:428, pId:4, t:"transformTozTreeNodes", name:"transformTozTreeNodes (simpleNodes)", iconSkin:"core", showAPI:true},
+ {id:429, pId:4, t:"updateNode", name:"updateNode (node, checkTypeFlag)", iconSkin:"core", showAPI:true},
+ {id:5, pId:0, name:"}"}
+ ];
+
+ apiContent.zTree_Setting = $.fn.zTree.init($("#settingTree"), $.fn.zTree._z.tools.clone(setting), setting_nodes);
+ apiContent.zTree_Node = $.fn.zTree.init($("#treenodeTree"), $.fn.zTree._z.tools.clone(setting), treenode_nodes);
+ apiContent.zTree_Function = $.fn.zTree.init($("#functionTree"), $.fn.zTree._z.tools.clone(setting), function_nodes);
+ this.bindEvent();
+
+ },
+ bindEvent: function() {
+ $(document).bind("keydown", this.listenKeyDown)
+ this.overlayCloseBtn.bind("click", apiContent.overlayClose);
+ this.searchResultInput.bind("click", function(e) {
+ $(this).prev().get(0).focus();
+ this.blur();
+ }).bind("focus", function(e) {
+ this.blur();
+ });
+ this.searchKey.bind("focus", this.focusKey)
+ .bind("blur", this.blurKey)
+ .bind("propertychange", this.searchNode)
+ .bind("input", this.searchNode);
+ this.searchPrevBtn.bind("click", this.searchPrev);
+ this.searchNextBtn.bind("click", this.searchNext);
+ },
+ setSameKey: function(value) {
+ apiContent.searchKey.attr("value", value);
+ },
+ focusKey: function(e) {
+ if (apiContent.searchKey.hasClass("empty")) {
+ apiContent.searchKey.removeClass("empty");
+ }
+ },
+ blurKey: function(e) {
+ apiContent.setSameKey(e.target.value);
+ if (e.target.value === "") {
+ apiContent.searchKey.addClass("empty");
+ }
+ },
+ listenKeyDown: function(e) {
+ if (e.keyCode=="13" && apiContent.overlayDiv.is(":hidden")) {
+ apiContent.openAPI();
+ } else if (e.keyCode=="37") {
+ apiContent.searchPrev();
+ } else if (e.keyCode=="13" || e.keyCode=="39") {
+ apiContent.searchNext();
+ }
+ },
+ openAPI: function() {
+ if (apiContent.searchNodes.length > 0) {
+ var setting_zTree = $.fn.zTree.getZTreeObj("settingTree"),
+ treenode_zTree = $.fn.zTree.getZTreeObj("treenodeTree"),
+ function_zTree = $.fn.zTree.getZTreeObj("functionTree");
+ if (apiContent.searchNodesCur < 0 || apiContent.searchNodesCur > apiContent.searchNodes.length -1) {
+ apiContent.searchNodesCur = 0;
+ }
+ var node = apiContent.searchNodes[apiContent.searchNodesCur];
+
+ if (node.tId.indexOf("setting") > -1) {
+ setting_zTree.selectNode(node);
+ } else if (node.tId.indexOf("treenode") > -1) {
+ treenode_zTree.selectNode(node);
+ } else {
+ function_zTree.selectNode(node);
+ }
+ apiContent.beforeClick(node.tId.substring(0, node.tId.indexOf("_")), node, true);
+ apiContent.searchCur();
+ }
+ },
+ searchNode: function(e) {
+ var setting_zTree = $.fn.zTree.getZTreeObj("settingTree"),
+ treenode_zTree = $.fn.zTree.getZTreeObj("treenodeTree"),
+ function_zTree = $.fn.zTree.getZTreeObj("functionTree");
+ if (apiContent.curKey == e.target.value) return;
+ apiContent.curKey = e.target.value;
+ var value = $.trim(apiContent.curKey);
+ apiContent.setSameKey(apiContent.curKey);
+ if (apiContent.searchKey.hasClass("empty")) {
+ value = "";
+ apiContent.searchResultInput.removeClass("noResult").attr("value","");
+ }
+ if (apiContent.lastValue === value) return;
+
+ apiContent.updateNodes(false);
+ apiContent.lastValue = value;
+ if (value === "" || value.length < 2) {
+ apiContent.searchNodes = [];
+ apiContent.searchNodesCur = -1;
+ apiContent.searchCur(true);
+ return;
+ }
+
+ var settingNodeList = setting_zTree.getNodesByFilter(apiContent.searchFilter);
+ var functionNodeList = function_zTree.getNodesByFilter(apiContent.searchFilter);
+ var treenodeNodeList = treenode_zTree.getNodesByFilter(apiContent.searchFilter);
+ apiContent.searchNodes = settingNodeList.concat(functionNodeList).concat(treenodeNodeList);
+ apiContent.searchNodesCur = -1;
+ apiContent.searchCur();
+ apiContent.updateNodes(true);
+ },
+ searchFilter: function(node) {
+ var value = $.trim(apiContent.searchKey.get(0).value).toLowerCase();
+ return (node.showAPI && node.name.toLowerCase().indexOf(value) > -1);
+ },
+ searchPrev: function(e) {
+ if (apiContent.searchPrevBtn.hasClass("disabled")) return;
+ apiContent.searchNodesCur--;
+ if (apiContent.searchNodesCur < 0 || apiContent.searchNodesCur > apiContent.searchNodes.length -1) {
+ apiContent.searchNodesCur = apiContent.searchNodes.length -1;
+ }
+ apiContent.openAPI();
+ },
+ searchNext: function(e) {
+ if (apiContent.searchNextBtn.hasClass("disabled")) return;
+ apiContent.searchNodesCur++;
+ apiContent.openAPI();
+ },
+ searchCur: function(init) {
+ var result = apiContent.searchNodes;
+ if (init) {
+ apiContent.searchResultInput.removeClass("noResult").attr("value","");
+ } else if (result.length == 0) {
+ apiContent.searchResultInput.addClass("noResult").attr("value"," [ 0 / 0 ] ");
+ } else {
+ apiContent.searchResultInput.removeClass("noResult").attr("value"," [ " + (apiContent.searchNodesCur > -1 ? apiContent.searchNodesCur+1 : "?")+ " / " + result.length + " ] ");
+ }
+ if (result.length > 0) {
+ apiContent.searchPrevBtn.removeClass("disabled");
+ apiContent.searchNextBtn.removeClass("disabled");
+ } else {
+ apiContent.searchPrevBtn.addClass("disabled");
+ apiContent.searchNextBtn.addClass("disabled");
+ }
+ },
+ updateNodes: function(highlight) {
+ var setting_zTree = $.fn.zTree.getZTreeObj("settingTree"),
+ treenode_zTree = $.fn.zTree.getZTreeObj("treenodeTree"),
+ function_zTree = $.fn.zTree.getZTreeObj("functionTree"),
+ node = null;
+ for( var i=0, l=apiContent.searchNodes.length; i 0) {
+ node.highlight = highlight;
+ if (node.tId.indexOf("setting") > -1) {
+ setting_zTree.updateNode(node);
+ } else if (node.tId.indexOf("treenode") > -1) {
+ treenode_zTree.updateNode(node);
+ } else {
+ function_zTree.updateNode(node);
+ }
+ }
+ }
+ },
+ getFontCss: function(treeId, treeNode) {
+ return (!!treeNode.highlight) ? {color:"#A60000", "font-weight":"bold"} : {color:"#333", "font-weight":"normal"};
+ },
+ getTitle: function(treeId, node) {
+ var t = [], n = node;
+ while (n && !!n.t) {
+ t.push(n.t);
+ n = n.getParentNode();
+ }
+ t = t.reverse();
+ node.tt = t.join('.');
+ return true;
+ },
+ showIcon: function(treeId, node) {
+ return (!!node.iconSkin);
+ },
+ onNodeCreated: function (e, treeId, node) {
+ var a = $("#" + node.tId + "_a");
+ if (node.showAPI) {
+ a.attr("rel", "#overlayDiv");
+ } else {
+ a.css({cursor: "default"});
+ }
+ },
+ beforeClick: function (treeId, node, noClear) {
+ if (!node.showAPI) return false;
+ var o = $("#" + node.tId + "_a");
+ if (!!apiContent.apiCache[node.tId]) {
+ apiContent.tmpDiv.html(apiContent.apiCache[node.tId]);
+ apiContent.overlayShow(o, (apiContent.lastNode === node));
+ } else {
+ apiContent.overlayAjax(treeId, node);
+ }
+ apiContent.lastNode = node;
+ if (node.tId.indexOf("settingTree")>-1) {
+ apiContent.settingDiv.removeClass("right").addClass("left");
+ apiContent.functionDiv.removeClass("left").addClass("right");
+ } else {
+ apiContent.settingDiv.removeClass("left").addClass("right");
+ apiContent.functionDiv.removeClass("right").addClass("left");
+ }
+
+ if (!noClear) {
+ apiContent.clearSelectedNode();
+ }
+ return true;
+ },
+ clearSelectedNode: function() {
+ apiContent.zTree_Setting.cancelSelectedNode();
+ apiContent.zTree_Node.cancelSelectedNode();
+ apiContent.zTree_Function.cancelSelectedNode();
+ },
+ overlayAutoClose: function(e) {
+ var eId = e.target.id, eRel = e.target.getAttribute("rel"), eClass = e.target.className;
+ if (eId === "overlayDiv" || eId === "overlayDivArrow" || eClass.indexOf("searchPrev") > -1 || eClass.indexOf("searchNext") > -1 || !!eRel) return;
+ if (!$(e.target).parents("[rel]").length && !$(e.target).parents("#overlayDiv").length) {
+ apiContent.overlayClose();
+ }
+ },
+ overlayClose: function() {
+ var o = apiContent.overlayDiv;
+ o.stop();
+ apiContent.clearSelectedNode();
+ if (ie) {
+ o.hide();
+ } else {
+ setTimeout(function() {o.fadeTo("fast", 0, function(){o.hide();})}, 200);
+ }
+ $(document).unbind("click", apiContent.overlayAutoClose);
+ },
+ overlayShow: function(target, isSameNode) {
+ var w = $(window), o = apiContent.overlayDiv, a = apiContent.overlayArrow,
+ oc = apiContent.overlayContent, c = apiContent.contentBoxDiv,
+ t = target.offset().top - 30,
+ cMaxLeft = c.offset().left + c.outerWidth({margin:true}) - o.outerWidth({margin:true}) - 10,
+ l = Math.min(cMaxLeft, target.offset().left + target.width() + 40),
+ arrowT = target.offset().top + 16,
+ wMinTop = 100, footerHeight = 50, onlyFade = false,
+ wHeight = w.height(), wScrollTop=w.scrollTop(), wMaxTop = wHeight + wScrollTop - footerHeight;
+ if (!apiContent.overlayMaxTop) {
+ apiContent.overlayMaxTop = apiContent.contentBoxDiv.offset().top + apiContent.contentBoxDiv.height();
+ }
+ o.stop();
+ if (o.css("display") !== "block") {
+ o.css({top: t, left: l});
+ a.css({top:arrowT - t});
+ $(document).bind("click", apiContent.overlayAutoClose);
+ }
+ if (ie) {
+ onlyFade = true;
+ o.show();
+ } else {
+ o.fadeTo("fast", 1);
+ }
+
+ var h = apiContent.tmpDiv.outerHeight({margin:true}) + apiContent.overlaySearch.outerHeight();
+ if ((t + h) > wMaxTop) {
+ t = wMaxTop - h;
+ }
+ if ((t + h) > apiContent.overlayMaxTop) {
+ t = apiContent.overlayMaxTop - h;
+ }
+ t = Math.max(t, wScrollTop, wMinTop);
+ if ((t + h) > ($("body").height()-footerHeight-20)) {
+ o.css("padding-bottom", footerHeight + "px");
+ } else {
+ o.css("padding-bottom", "0");
+ }
+ apiContent.overlayDetailDiv.empty();
+ apiContent.overlayDetailDiv.append(apiContent.tmpDiv.children());
+ if (!onlyFade) {
+ onlyFade = (isSameNode && t === parseInt(o.css("top").replace("px", "")));
+ }
+
+ a.removeClass("reverse");
+ if ( (arrowT - t) > (h-55) ) {
+ a.addClass("reverse");
+ arrowT -= 55;
+ }
+
+ if (onlyFade) {
+ o.css({top: t, left: l});
+ oc.css({height: h});
+ a.css({top:arrowT - t});
+ } else {
+ o.animate({top: t, left: l}, {duration: "normal",easing: "swing", complete:null});
+ oc.animate({height: h}, {duration: "fast",easing: "swing", complete:null});
+ a.animate({top:arrowT - t}, {duration: "normal",easing: "linear", complete:null});
+ }
+ },
+ overlayAjax: function(treeId, node) {
+ var o = $("#" + node.tId + "_a");
+ if (node.isAjax) return;
+ node.isAjax = true;
+ $.ajax({
+ type: "get",
+ url: "" + lang + "/" + node.tt.replace("$.", "") + ".html",
+ data: null,
+ dataType: "text",
+ success: function(msg) {
+ if (!apiContent.tmpDiv) {
+ var tmpDiv = $(document.createElement("div"));
+ tmpDiv.addClass("baby_overlay_tmp");
+ $("body").append(tmpDiv)
+ apiContent.tmpDiv = $(document.createElement("div"));
+ apiContent.tmpDiv.addClass("details");
+ tmpDiv.append(apiContent.tmpDiv);
+
+ } else {
+ apiContent.tmpDiv.empty();
+ }
+ apiContent.tmpDiv.html(msg);
+ apiContent.overlayShow(o, false);
+ apiContent.apiCache[node.tId] = msg;
+ node.isAjax = false;
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ alert(ajaxMsg)
+ if (apiContent.tmpDiv) apiContent.tmpDiv.empty();
+ node.isAjax = false;
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/common.css b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/common.css
new file mode 100644
index 00000000..78f910c7
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/common.css
@@ -0,0 +1,219 @@
+/* Resets */
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;padding: 0;border: 0;outline: 0;font-weight: inherit;font-style: inherit;font-size: 100%;font-family: inherit;vertical-align: baseline;}
+:focus {outline: 0;}
+body {color: #2f332a;font: 15px/21px Arial, Helvetica, simsun, sans-serif;background: #528036 url(img/background.jpg) no-repeat fixed 0 0;}
+p {padding-bottom: 20px;}
+ol, ul {list-style: none;}
+table {border-collapse: separate;border-spacing: 0;}
+caption, th, td {text-align: left;font-weight: normal;}
+strong {font-weight: bold;}
+em {font-style: italic;}
+hr {display: none;}
+.font1 {color: white;background-color: #528036;}
+.right {float: right;}
+.left {float: left;}
+.hide {display: none;}
+.round {-moz-border-radius: 15px;-webkit-border-radius: 15px;-khtml-border-radius: 15px;border-radius: 15px;}
+.clear {clear: both;}
+.clearfix {display: block;}
+.clearfix:after {content: ".";display: block;clear: both;visibility: hidden;line-height: 0;height: 0;}
+html[xmlns] .clearfix {display: block;}
+* html .clearfix {height: 1%;}
+
+/* Link Styles */
+a {color: #528036;}
+a:link, a:visited {text-decoration: none;}
+a:hover {color: #000;text-decoration: none;}
+a:active {text-decoration: none;}
+
+/* Headings */
+h1, h2, h3, h4, h5, h6 {color: #2f332a;font-weight: bold;font-family: Helvetica, Arial, simsun, sans-serif;padding-bottom: 5px;}
+h1 {font-size: 36px;line-height: 44px;}
+h2 {font-size: 20px;line-height: 20px;}
+h3 {font-size: 14px;line-height: 14px;}
+h4 {font-size: 14px;font-weight: normal;line-height: 25px;}
+
+/* Wraps */
+.header_wrap {position: relative;min-width: 940px;padding: 100px 30px 0 30px;}
+.content_wrap {position: relative;min-width: 940px;padding: 0 30px 50px 30px;}
+.footer_wrap {bottom: 0;height: 47px;width: 100%;background-color: #1b1b1b;border-top: 1px solid #749e58;}
+
+/* Header */
+.header {position: relative;width: 940px;margin: 0 auto;height: 160px;border: 1px solid white;background: transparent url(img/header-bg.png) repeat-x 0 -50px;}
+.header-text {padding: 40px 25px 15px 120px;font-size: 18px;line-height: 24px;color: #747d67;font-family: Helvetica, sans-serif;}
+.header-text img {padding-bottom: 5px;}
+.shortcuts {white-space: nowrap;text-align: right;position: absolute;top: -45px;right: 5px;}
+.shortcuts.language {top: -85px;right:0px;}
+.shortcuts li {display: inline;font-size: 18px;line-height: 28px;font-family: Helvetica, Arial, simsun, sans-serif;padding-bottom: 5px;margin-left: 30px;cursor: pointer;}
+.shortcuts li button {cursor: pointer;}
+.shortcuts li span {border-bottom: 1px dotted white;}
+.shortcuts li span.selected {padding: 2px;background-color: #528036;}
+.shortcuts li a {color: #fff;}
+.ieSuggest {display:none;font-size: 12px;color: silver;position: absolute;left: 10px;top: 2px;}
+.google_plus {position: absolute;right: 10px; top:10px;}
+.light-bulb {position: absolute;left: -20px;bottom: -35px;width:116px;height:180px;background-image:url(img/lightbulb.png);background-repeat: no-repeat;}
+
+/* Content */
+.content {position: relative;width: 940px;margin: 0 auto;}
+.nav_section {position: relative;height: 20px;font-family: "Myriad Pro", "Trebuchet MS", sans-serif;font-size: 15px;color: #253;padding: 20px 0;}
+.nav_section ul {position: absolute;right: 10px;}
+.nav_section ul li {display: inline;line-height: 20px;margin: 0 5px 0 20px;border-bottom: 1px dotted white;}
+.nav_section ul li.noline {border-bottom: 0;}
+.nav_section ul li a {color: #fff;}
+.nav_section ul li a.selected {padding: 2px;background-color: #528036;}
+.nav_section ul li.first {border: none;}
+.content .title {margin: 50px 30px 20px 70px;}
+.content li {margin-bottom: 5px;}
+.contentBox {position: relative;overflow: hidden;border: 1px solid white;min-height: 200px;line-height: 25px;background: transparent url(img/contact-bg.png) repeat-x 0 0;}
+
+.zTreeInfo {display:none;width: 940px;position: absolute;}
+.zTreeInfo p {padding-bottom: 50px;}
+.zTreeInfo-left {float: left;width: 280px;height:300px;padding: 0 50px 60px 75px;background:url(img/zTreeIntroduction.jpg) no-repeat 30px 30px;}
+.zTreeInfo-right {position: relative;float: right;width: 475px;padding: 0 50px 60px 0;}
+.zTreeInfo-right li {font-size: 12px;list-style-type: disc;}
+
+.license {display:none;width: 940px;position: absolute;}
+
+.donateInfo {display:block;width: 940px;position: absolute;}
+
+.links {display:none;width: 940px;position: absolute;}
+.links .content {float: left;width: 160px;height:200px;padding: 0 10px 10px 2px;text-align: center;}
+.links .content.first {margin-left: 30px;}
+
+.contact {display:none;width: 940px;position: absolute;}
+.contact .myhome { position: absolute; top:10px; left:620px; width:300px; height:266px; background: transparent url(img/myhome.gif) scroll no-repeat 0 0;}
+
+.siteTag {position: absolute;left: -16px;top: 109px;z-index: 10;width: 65px;height: 46px;padding:0;margin:0 10px 0 0;
+ vertical-align:middle;border:0 none;background: transparent url(img/siteTag.png) scroll no-repeat 0 0;}
+.siteTag.tag_zTreeInfo {background-position: 0 0}
+.siteTag.tag_license {background-position: 0 -46px}
+.siteTag.tag_donate {background-position: 0 -92px}
+.siteTag.tag_contact {background-position: 0 -138px}
+
+.apiContent {width: 940px;}
+.apiContent .right {float: right;padding-right: 100px;}
+.apiContent .left {float: left;padding-right: 20px;border-right: 1px dotted silver;}
+.api_setting {position: relative;margin:20px 0 20px 20px;}
+.api_function {position: relative;margin:20px 0 20px 30px;padding-right: 10px;}
+.api_content_title {text-align: center;font-weight: bold;}
+
+.demoContent {width: 940px;}
+.demoContent .right {float: right;padding: 20px;width: 600px;}
+.demoContent .left {float: left;padding: 20px;}
+.demoContent iframe {width:600px;min-height: 530px;}
+
+.faqContent {width: 940px;}
+.faqContent .right {float: right;padding: 20px;width: 600px;}
+.faqContent .left {float: left;padding: 20px;}
+.faqContent iframe {width:600px;min-height: 300px;}
+
+.baby_overlay_tmp {position: absolute;top:0; left:-5000px;display:block;visibility: hidden;width:640px;font-size:11px;}
+.baby_overlay_tmp .details {padding: 20px;}
+.baby_overlay {display:none;position:absolute;z-index:99;left:0; top:0;width:640px;color:#fff;font-size:11px;}
+.baby_overlay .content {width:100%; height:100px;overflow: hidden;background: transparent url(img/overlay_bg.png) scroll repeat 0 0;}
+.baby_overlay .details {padding:0 20px 20px 20px;}
+.baby_overlay .close {background-image:url(img/close.png);position:absolute; right:5px; top:5px;cursor:pointer;height:36px;width:36px;}
+.baby_overlay_arrow {background-image:url(img/overlay_arrow.png);background-position:0 0;position:absolute;height:40px;width:40px;left: -40px;}
+.baby_overlay_arrow.reverse {background-position:0 -40px;}
+
+/* Footer */
+.footer {position: relative;min-width: 1000px;font: 14px/24px arial, helvetica, sans-serif;}
+.footer ul {position:absolute;left: 0px;border:1px solid #393939;background:#262626;padding:12px 0px;line-height: 18px;display: none;list-style: none;}
+.footer ul li a {display:block;padding: 2px 15px;color: #9c9c9c;text-indent: 0;}
+.footer ul li a:hover {text-decoration:none;color: #fff;}
+.footer-logo {position:absolute;margin: 10px 0 0 30px;width:122px; height:24px;top:0; left:0;background: transparent url(img/footer-logo.png) no-repeat 0 0;}
+.footer_mii {position: absolute;right: 558px;top: 8px;z-index: 10;padding: 4px 0;}
+.footer_mii a {font-size:10px;color:#649140}
+.footer_mii a:hover {color:#B6D76F}
+.footer_siteMap {position: absolute;right: 358px;top: 8px;width: 155px;z-index: 10;padding: 4px 0;}
+.footer_siteMap .footer_siteMap_header {width:155px;text-indent: -9999px;background: transparent url(img/footer_siteMap.gif) no-repeat 0 0;}
+.footer_siteMap ul {top:-202px;width:180px;}
+.footer_siteMap:hover ul {left: 0}
+.footer_contact {position: absolute;right: 193px;top: 8px;width: 155px;z-index: 10;padding: 4px 0;}
+.footer_contact .footer_contact_header {width:155px;text-indent: -9999px;background: transparent url(img/footer_contact.gif) no-repeat 0px 0px;}
+.footer_contact ul {top:-113px;width:153px;}
+.footer_contact:hover ul {left: 0}
+.footer_download {position: absolute;right: 60px;top: 8px;width: 123px;z-index: 10;padding: 4px 0;}
+.footer_download .footer_download_header {width:123px;text-indent: -9999px;background: transparent url(img/footer_download.png) no-repeat 0px 0px;}
+.footer_download ul {top:-113px;width:140px;}
+.footer_download:hover ul {left: 0}
+
+/* button icon */
+button {vertical-align:middle;border:0 none;background: transparent no-repeat 0 0 scroll;}
+
+.shortcuts button.ico {width:24px; height:24px;padding:0; margin:0 10px 0 0;background-image:url(img/menuIcon.png)}
+.shortcuts button.home {background-position: 0 0}
+.shortcuts button.demo {background-position: 0 -24px}
+.shortcuts button.api {background-position: 0 -48px}
+.shortcuts button.faq {background-position: 0 -72px}
+.shortcuts button.donate {background-position: 0 -144px}
+.shortcuts button.download {background-position: 0 -96px}
+.shortcuts button.face {background-position: 0 -120px}
+.shortcuts button.cn {width:48px; height:24px;padding:0; margin:0 10px 0 0;background-image:url(img/chinese.png)}
+.shortcuts button.en {width:48px; height:24px;padding:0; margin:0 10px 0 0;background-image:url(img/english.png)}
+
+.content button.ico {width:24px; height:24px;padding:0; margin:0 10px 0 0;}
+
+.content button.ico16 {width:16px; height:16px;padding:0; margin:0 5px 0 0;background-image:url("img/apiMenu.png");}
+button.z_core {margin-top: -4px;background-position:0 0;}
+button.z_check {margin-top: -4px;background-position:0 -16px;}
+button.z_edit {margin-top: -4px;background-position:0 -32px;}
+button.z_hide {margin-top: -4px;background-position:0 -64px;}
+button.z_search {margin-top: -4px;background-position:0 -48px;}
+button.searchPrev {margin-top: -4px;background-position:-16px 0;cursor:pointer}
+button.searchNext {margin-top: -4px;background-position:-16px -16px;cursor:pointer}
+button.searchPrev.disabled {margin-top: -4px;background-position:-16px -32px;cursor:auto}
+button.searchNext.disabled {margin-top: -4px;background-position:-16px -48px;cursor:auto}
+input.search {margin:0;padding:2px 0; border:0;}
+input.searchKey {width:150px;}
+input.searchResult {margin-left:-3px;width:65px;text-align:right;background-color:white;color:#707070}
+input.searchResult.noResult {background-color:#ff6666;color:black}
+.baby_overlay div.overlaySearch {text-align:right;padding-right:50px;padding-top:12px;}
+
+/* api overlay*/
+.apiDetail .topLine {border-top: 1px dashed #376B29;margin-top: 5px;padding-top: 5px;}
+.apiDetail .highlight_red {color:#A60000;}
+.apiDetail .highlight_green {color:#A7F43D;}
+.apiDetail h1, .apiDetail h2, .apiDetail h3, .apiDetail h4, .apiDetail h5, .apiDetail h6 {color: white;padding: 0;}
+.apiDetail h2 {color: #A7F43D;margin: 5px auto;padding: 5px;font-size: 20px;}
+.apiDetail h2 span {font-size: 14px;float: right;font-weight: normal;margin: 2px 20px 0 0;vertical-align: bottom;}
+.apiDetail h2 span.path {float: left;margin: 2px 0 0 0;vertical-align: bottom;}
+.apiDetail h3 {margin: 5px auto;padding: 5px;font-size: 14px;font-weight: normal;}
+.apiDetail h3 span.h3_info {margin-left: 20px;font-size: 12px;}
+.apiDetail h4 {margin: 0 auto;padding: 0 5px;font-size: 12px;font-weight: normal;line-height: 16px;}
+.apiDetail .desc h4 {color: black;}
+.apiDetail h4 b{width: 150px;display:inline-block;}
+.apiDetail h4 span{width: 230px;display:inline-block;}
+
+.apiDetail pre, .apiDetail .desc {background: #E8FCD6;color: black;margin: 10px;padding: 10px;display: block;}
+.apiDetail pre {word-wrap: break-word;}
+.apiDetail p{margin-left: 5px;padding: 0;}
+.apiDetail .longdesc {margin-top: 5px;}
+.apiDetail .longdesc p{font-size: 12px;line-height:1.5;margin:3px 0;}
+.apiDetail .longdesc b{font-size: 14px;}
+.apiDetail table {border-collapse:collapse;}
+.apiDetail table td {border:1px solid silver;text-align: center;vertical-align: middle;}
+.apiDetail table thead td {font-weight: bold}
+
+.apiDetail button {width:16px; height:16px; vertical-align:middle; border:0 none; cursor: pointer;
+ background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
+ background-image:url("zTreeStyle/img/zTreeStandard.png");}
+
+.apiDetail button.chk {width:13px; height:13px; margin:0 3px 2px 0; cursor: auto}
+.apiDetail button.chk.checkbox_false_full {background-position:0 0}
+.apiDetail button.chk.checkbox_false_full_focus {background-position:0 -14px}
+.apiDetail button.chk.checkbox_false_part {background-position:0 -28px}
+.apiDetail button.chk.checkbox_false_part_focus {background-position:0 -42px}
+.apiDetail button.chk.checkbox_true_full {background-position:-14px 0}
+.apiDetail button.chk.checkbox_true_full_focus {background-position:-14px -14px}
+.apiDetail button.chk.checkbox_true_part {background-position:-14px -28px}
+.apiDetail button.chk.checkbox_true_part_focus {background-position:-14px -42px}
+.apiDetail button.chk.radio_false_full {background-position:-28px 0}
+.apiDetail button.chk.radio_false_full_focus {background-position:-28px -14px}
+.apiDetail button.chk.radio_false_part {background-position:-28px -28px}
+.apiDetail button.chk.radio_false_part_focus {background-position:-28px -42px}
+.apiDetail button.chk.radio_true_full {background-position:-42px 0}
+.apiDetail button.chk.radio_true_full_focus {background-position:-42px -14px}
+.apiDetail button.chk.radio_true_part {background-position:-42px -28px}
+.apiDetail button.chk.radio_true_part_focus {background-position:-42px -42px}
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/common_ie6.css b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/common_ie6.css
new file mode 100644
index 00000000..aacaf59c
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/common_ie6.css
@@ -0,0 +1,23 @@
+* html{
+/* background-image:url(about:blank);*/
+ background-attachment:fixed;
+}
+html pre {word-wrap: break-word}
+.header {background-image: none;background-color: #F0F6E4;}
+
+.ieSuggest {display:block;}
+.shortcuts button.cn {filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='apiCss/img/chinese.png');background-image: none;}
+.shortcuts button.en {filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='apiCss/img/english.png');background-image: none;}
+
+.light-bulb {filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='apiCss/img/lightbulb.png');background-image: none;}
+.contentBox {background-image: none;background-color: #F0F6E4;}
+.zTreeInfo {background-image: none;background-color: #F0F6E4;}
+
+.content button.ico16 {*background-image:url("img/apiMenu.gif")}
+.siteTag {background-image: none;}
+.apiContent .right {float: right;padding-right: 50px;}
+
+div.baby_overlay {background-color: #3C6E31;background-image:none;color:#fff;}
+div.baby_overlay .close {filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='apiCss/img/overlay_close_IE6.gif');background-image: none;}
+.baby_overlay_arrow {background-image:url(img/overlay_arrow.gif);}
+.apiDetail button {background-image:url("img/zTreeStandard.gif")}
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/apiMenu.gif b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/apiMenu.gif
new file mode 100644
index 00000000..066fc8e4
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/apiMenu.gif differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/apiMenu.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/apiMenu.png
new file mode 100644
index 00000000..9acec505
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/apiMenu.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/background.jpg b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/background.jpg
new file mode 100644
index 00000000..003375ff
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/background.jpg differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/chinese.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/chinese.png
new file mode 100644
index 00000000..d3b57fc1
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/chinese.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/close.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/close.png
new file mode 100644
index 00000000..69e41e38
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/close.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/contact-bg.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/contact-bg.png
new file mode 100644
index 00000000..a3d7a5f1
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/contact-bg.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/english.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/english.png
new file mode 100644
index 00000000..2ad2d7d5
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/english.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/header-bg.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/header-bg.png
new file mode 100644
index 00000000..a2baacf2
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/header-bg.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/lightbulb.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/lightbulb.png
new file mode 100644
index 00000000..c99357a3
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/lightbulb.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_arrow.gif b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_arrow.gif
new file mode 100644
index 00000000..e7c3e6d4
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_arrow.gif differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_arrow.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_arrow.png
new file mode 100644
index 00000000..d790a115
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_arrow.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_bg.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_bg.png
new file mode 100644
index 00000000..5f81ee69
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_bg.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_close_IE6.gif b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_close_IE6.gif
new file mode 100644
index 00000000..42cb8d83
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/overlay_close_IE6.gif differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/zTreeStandard.gif b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/zTreeStandard.gif
new file mode 100644
index 00000000..3f69a5b1
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/zTreeStandard.gif differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/zTreeStandard.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/zTreeStandard.png
new file mode 100644
index 00000000..33c9e844
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/img/zTreeStandard.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/jquery-1.6.2.min.js b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/jquery-1.6.2.min.js
new file mode 100644
index 00000000..8cdc80eb
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/api/apiCss/jquery-1.6.2.min.js
@@ -0,0 +1,18 @@
+/*!
+ * jQuery JavaScript Library v1.6.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Jun 30 14:16:56 2011 -0400
+ */
+(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;ca ",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j =0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.
+shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c ",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML=" ",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="
";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/",""],legend:[1,""," "],thead:[1,""],tr:[2,""],td:[3,""],col:[2,""],area:[1,""," "],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1>$2>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j
+)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1>$2>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/
+
+
+
+
+
+
+
+一次性加载大数据量
+[ 文件路径: bigdata/common.html ]
+
+
+
+
+ 1、大数据量加载说明
+
+ 1)、zTree v3.x 针对大数据量一次性加载进行了更深入的优化,实现了延迟加载功能,即不展开的节点不创建子节点的 DOM。
+ 2)、对于每级节点最多一百左右,但总节点数几千甚至几万,且不是全部展开的数据,一次性加载的效果最明显,速度非常快。
+ 3)、对于某一级节点数就多达几千的情况 延迟加载无效,这种情况建议考虑分页异步加载。
+ 4)、对于全部节点都展开显示的情况,延迟加载无效,这种情况建议不要全部展开。
+ 5)、显示 checkbox / radio 会造成一定程度的性能下降。
+ 6)、利用 addDiyDom 功能增加自定义控件会影响速度,影响程度受节点数量而定。
+ 7)、利用 onNodeCreated 事件回调函数对节点 DOM 进行操作会影响速度,影响程度受节点数量而定。
+
+
+ 2、setting 配置信息说明
+
+
+ 3、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/bigdata/diy_async.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/bigdata/diy_async.html
new file mode 100644
index 00000000..c4a8bfba
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/bigdata/diy_async.html
@@ -0,0 +1,157 @@
+
+
+
+ ZTREE DEMO - big data async
+
+
+
+
+
+
+
+
+
+
+
+分批异步加载大数据量
+[ 文件路径: bigdata/diy_async.html ]
+
+
+
+ 此 Demo 专门用于测试分批异步加载,每次展开节点都要重新进行异步加载。
+
+
+
+
+
+ 1、大数据量加载说明
+
+ 1)、对于某一级节点数多达几千个的时候,zTree 默认的延迟加载是无效的,此 Demo 演示了一种原先 zTree v2.6 时的分批加载节点的方法。
+ 2)、此方法适用于1、2千个节点必须全部显示的需求。
+ 3)、此方法并不能解决加载慢的问题,相反只会让最终结果出现的更慢,只是可以有限度的避免浏览器假死,而且显示的节点越多就越慢。
+ 4)、对于某一级节点数至少几千个的情况,另一个解决方案是:分页异步加载。
+ async load log:
+
+
+
+ 2、setting 配置信息说明
+
+ 需要设置 setting.async 异步加载部分的参数
+ 建议关闭动画效果 setting.view.expandSpeed = "";
+ 其他不需要进行特殊的配置,根据自己的需求自行设置
+
+
+ 3、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/bigdata/page.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/bigdata/page.html
new file mode 100644
index 00000000..ef3c763b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/bigdata/page.html
@@ -0,0 +1,150 @@
+
+
+
+ ZTREE DEMO - big data page
+
+
+
+
+
+
+
+
+
+
+
+
+分页加载大数据量
+[ 文件路径: bigdata/page.html ]
+
+
+
+
+ 1、大数据量加载说明
+
+ 1)、分页方案可以有效解决某一级节点数据超大的情况。
+ 2)、分页按钮通过自定义控件的方法实现。
+ 3)、分页方案对于 checkbox 的关联关系无能为力,只能每次翻页后进行修正。由于时间关系,Demo 中不对 checkbox 的关联进行任何修正处理。
+ 4)、分页方案中,从 zTree 得到的节点数据只有当前页的节点数据,可以在每次翻页后自行保存每页的数据,作为缓存,具体情况要根据实际需求来决定。
+
+ 2、setting 配置信息说明
+
+ 需要设置 setting.async 异步加载部分的参数
+ 其他不需要进行特殊的配置,根据自己的需求自行设置
+
+
+ 3、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/async.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/async.html
new file mode 100644
index 00000000..3f2a0d90
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/async.html
@@ -0,0 +1,70 @@
+
+
+
+ ZTREE DEMO - Async
+
+
+
+
+
+
+
+
+
+
+异步加载节点数据的树
+[ 文件路径: core/async.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 使用异步加载,必须设置 setting.async 中的各个属性,详细请参见 API 文档中的相关内容
+
+
+ 2、treeNode 节点数据说明
+
+ 异步加载功能对于 treeNode 节点数据没有特别要求,如果采用简单 JSON 数据,请设置 setting.data.simple 中的属性
+ 如果异步加载每次都只返回单层的节点数据,那么可以不设置简单 JSON 数据模式
+
+
+ 3、其他说明
+
+ 观察 autoParam 和 otherParam 请使用 firebug 或 浏览器的开发人员工具
+ 此 Demo 只能加载到第 4 级节点(level=3)
+ 此 Demo 利用 dataFilter 对节点的 name 进行了修改
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/async_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/async_fun.html
new file mode 100644
index 00000000..3571b24b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/async_fun.html
@@ -0,0 +1,144 @@
+
+
+
+ ZTREE DEMO - reAsyncChildNodes
+
+
+
+
+
+
+
+
+
+
+
+用 zTree 方法异步加载节点数据
+[ 文件路径: core/async_fun.html ]
+
+
+
+
+ 1、reAsyncChildNodes 方法操作说明
+
+
+ 2、setting 配置信息说明
+
+ 使用 zTree 提供的 reAsyncChildNodes 方法也必须设置 setting.async 中的各个属性,详细请参见 API 文档中的相关内容
+
+
+ 3、treeNode 节点数据说明
+
+
+ 4、其他说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/click.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/click.html
new file mode 100644
index 00000000..50367d97
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/click.html
@@ -0,0 +1,107 @@
+
+
+
+ ZTREE DEMO - beforeClick / onClick
+
+
+
+
+
+
+
+
+
+
+单击节点控制
+[ 文件路径: core/click.html ]
+
+
+
+
+ 1、beforeClick / onClick 事件回调函数控制
+
+
+ 2、setting 配置信息说明
+
+ 需要设置 setting.callback.beforeClick 和 setting.callback.onClick 属性, 详细请参见 API 文档中的相关内容
+
+
+ 3、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_font.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_font.html
new file mode 100644
index 00000000..e9f901ec
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_font.html
@@ -0,0 +1,67 @@
+
+
+
+ ZTREE DEMO - Custom Font
+
+
+
+
+
+
+
+
+
+
+显示自定义字体的树
+[ 文件路径: core/custom_font.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 1、节点自定义字体请设置 setting.view.fontCss 属性,详细请参见 API 文档中的相关内容
+ 2、setting.view.nameIsHTML 属性可以允许节点名称支持 HTML 内容,详细请参见 API 文档中的相关内容
+
+
+ 2、treeNode 节点数据说明
+
+ 设置字体不需要 treeNode 设置特殊数据,但如果用于为了区别不同类型的节点,设置不同的样式,可以对相应的数据设置自定义属性
+ 此 Demo 利用 treeNode 保存样式定义
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_icon.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_icon.html
new file mode 100644
index 00000000..7c8de189
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_icon.html
@@ -0,0 +1,74 @@
+
+
+
+ ZTREE DEMO - Custom Icon
+
+
+
+
+
+
+
+
+
+
+自定义图标 -- icon 属性
+[ 文件路径: core/custom_icon.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 自定义图标不需要对 setting 进行特殊配置
+
+
+ 2、treeNode 节点数据说明
+
+ 利用 节点数据的 icon / iconOpen / iconClose 属性实现自定义图标
+ 详细请参见 API 文档中的相关内容
+
+
+ 3、其他说明
+
+ 由于时间关系,例子直接采用 png 图片,如果需要解决 ie6 下 png 图片的透明问题,请针对 ie6 制作特殊的 gif 图片或者利用 css filter 解决
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_iconSkin.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_iconSkin.html
new file mode 100644
index 00000000..cde753a5
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/custom_iconSkin.html
@@ -0,0 +1,84 @@
+
+
+
+ ZTREE DEMO - Custom Icon Skin
+
+
+
+
+
+
+
+
+
+
+
+自定义图标 -- iconSkin 属性
+[ 文件路径: core/custom_iconSkin.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 自定义图标不需要对 setting 进行特殊配置
+
+
+ 2、treeNode 节点数据说明
+
+ 利用 节点数据的 iconSkin 属性 配合 css 实现自定义图标
+ 详细请参见 API 文档中的相关内容
+
+
+ 3、其他说明
+
+ 由于时间关系,例子直接采用 png 图片,如果需要解决 ie6 下 png 图片的透明问题,请针对 ie6 制作特殊的 gif 图片或者利用 css filter 解决
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/expand.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/expand.html
new file mode 100644
index 00000000..15b03b5d
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/expand.html
@@ -0,0 +1,185 @@
+
+
+
+ ZTREE DEMO - beforeExpand / onExpand && beforeCollapse / onCollapse
+
+
+
+
+
+
+
+
+
+
+展开 / 折叠父节点控制
+[ 文件路径: core/expand.html ]
+
+
+
+
+ 1、beforeCollapse / onCollapse beforeExpand / onExpand 事件回调函数控制
+
+
+ 2、setting 配置信息说明
+
+ 事件回调函数的使用,详细请参见 API 文档中 setting.callback 的相关内容
+ 如果需要调整 展开 / 折叠 的动画效果, 详细请参见 API 文档中 setting.view.expandSpeed 的相关内容
+
+
+ 3、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/noicon.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/noicon.html
new file mode 100644
index 00000000..9562163b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/noicon.html
@@ -0,0 +1,93 @@
+
+
+
+ ZTREE DEMO - noIcon
+
+
+
+
+
+
+
+
+
+
+
+不显示节点图标的树
+[ 文件路径: core/noicon.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 此 Demo 利用 Function 设置了使父节点不显示图标的规则
+ 是否显示节点图标请设置 setting.view.showIcon 属性,详细请参见 API 文档中的相关内容
+
+
+ 2、treeNode 节点数据说明
+
+ 是否显示图标,不需要 treeNode 节点数据提供特殊设置,但如果用户需要根据不同节点动态设置,可以对节点数据增加特殊属性,用于判别
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/noline.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/noline.html
new file mode 100644
index 00000000..212d97bb
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/noline.html
@@ -0,0 +1,88 @@
+
+
+
+ ZTREE DEMO - noLine
+
+
+
+
+
+
+
+
+
+
+
+不显示连接线的树
+[ 文件路径: core/noline.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 是否显示连接线请设置 setting.view.showLine 属性,详细请参见 API 文档中的相关内容
+
+
+ 2、treeNode 节点数据说明
+
+ 是否显示连线,不需要 treeNode 节点数据提供特殊设置
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/otherMouse.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/otherMouse.html
new file mode 100644
index 00000000..2dff4e41
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/otherMouse.html
@@ -0,0 +1,132 @@
+
+
+
+ ZTREE DEMO - Other Mouse Event
+
+
+
+
+
+
+
+
+
+
+其他鼠标事件监听
+[ 文件路径: core/otherMouse.html ]
+
+
+
+
+ 1、mousedown / mouseup / rightClick 事件回调函数控制
+
+
+ 2、setting 配置信息说明
+
+ 事件回调函数的使用,详细请参见 API 文档中 setting.callback 的相关内容
+
+
+ 3、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/searchNodes.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/searchNodes.html
new file mode 100644
index 00000000..28c2e3ba
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/searchNodes.html
@@ -0,0 +1,173 @@
+
+
+
+ ZTREE DEMO - getNodeByParam / getNodesByParam / getNodesByParamFuzzy
+
+
+
+
+
+
+
+
+
+
+根据参数查找节点
+[ 文件路径: core/searchNodes.html ]
+
+
+
+
+ 1、getNodeByParam / getNodesByParam / getNodesByParamFuzzy 方法操作说明
+
+
+ 2、setting 配置信息说明
+
+
+ 3、treeNode 节点数据说明
+
+ 请注意各个方法使用时保证传入查找的参数类型与设定要查找的属性的类型一致
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/simpleData.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/simpleData.html
new file mode 100644
index 00000000..6d2dcb89
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/simpleData.html
@@ -0,0 +1,100 @@
+
+
+
+ ZTREE DEMO - Simple Data
+
+
+
+
+
+
+
+
+
+
+最简单的树 -- 简单 JSON 数据
+[ 文件路径: core/simpleData.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 必须设置 setting.data.simple 内的属性,详细请参见 API 文档中的相关内容
+ 与显示相关的内容请参考 API 文档中 setting.view 内的配置信息
+ name、children、title 等属性定义更改请参考 API 文档中 setting.data.key 内的配置信息
+
+
+ 2、treeNode 节点数据说明
+
+ 简单模式的 JSON 数据需要使用 id / pId 表示节点的父子包含关系,如使用其他属性设置父子关联关系请参考 setting.data.simple 内各项说明
+ 例如:
+var nodes = [
+ {id:1, pId:0, name: "父节点1"},
+ {id:11, pId:1, name: "子节点1"},
+ {id:12, pId:1, name: "子节点2"}
+];
+ 默认展开的节点,请设置 treeNode.open 属性
+ 无子节点的父节点,请设置 treeNode.isParent 属性
+ 其他属性说明请参考 API 文档中 "treeNode 节点数据详解"
+
+
+ 3、其他说明
+
+ Demo 中绝大部分都采用简单 JSON 数据模式,以便于大家学习
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/standardData.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/standardData.html
new file mode 100644
index 00000000..582a6e16
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/standardData.html
@@ -0,0 +1,106 @@
+
+
+
+ ZTREE DEMO - Standard Data
+
+
+
+
+
+
+
+
+
+
+最简单的树 -- 标准 JSON 数据
+[ 文件路径: core/standardData.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 普通使用,无必须设置的参数
+ 与显示相关的内容请参考 API 文档中 setting.view 内的配置信息
+ name、children、title 等属性定义更改请参考 API 文档中 setting.data.key 内的配置信息
+
+
+ 2、treeNode 节点数据说明
+
+ 标准的 JSON 数据需要嵌套表示节点的父子包含关系
+ 例如:
+var nodes = [
+ {name: "父节点1", children: [
+ {name: "子节点1"},
+ {name: "子节点2"}
+ ]}
+];
+
+ 默认展开的节点,请设置 treeNode.open 属性
+ 无子节点的父节点,请设置 treeNode.isParent 属性
+ 其他属性说明请参考 API 文档中 "treeNode 节点数据详解"
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/update_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/update_fun.html
new file mode 100644
index 00000000..918ed82b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/update_fun.html
@@ -0,0 +1,143 @@
+
+
+
+ ZTREE DEMO - updateNode
+
+
+
+
+
+
+
+
+
+
+
+用 zTree 方法 更新 节点数据
+[ 文件路径: core/update_fun.html ]
+
+
+
+
+ 1、updateNode 方法操作说明
+
+
+ 2、setting 配置信息说明
+
+ 不需要对 setting 进行特殊设置
+ 配合 setting.view.fontCss 可以修改节点文字样式
+
+
+ 3、treeNode 节点数据说明
+
+ zTreeObj.updateNode 方法的 API 文档中有详细说明
+
+
+ 4、其他说明
+
+ 此 Demo 是针对 核心 js 包 core 制作的,因此不包括 checkbox 的更新操作
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/url.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/url.html
new file mode 100644
index 00000000..8550d287
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/core/url.html
@@ -0,0 +1,63 @@
+
+
+
+ ZTREE DEMO - url
+
+
+
+
+
+
+
+
+
+
+超链接演示
+[ 文件路径: core/url.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 普通使用,无必须设置的参数
+ 如果需要灵活控制超链接跳转,请利用 onClick 事件回调函数进行控制,详细请参见 API 文档中的相关内容
+
+
+ 2、treeNode 节点数据说明
+
+ 1、url 属性用于设置 页面跳转的路径
+ 2、target 属性用于设置 页面跳转的窗口目标
+ 3、click 属性用于设置简单的 onClick 事件
+ 其他属性说明请参考 API 文档中 "treeNode 节点数据详解"
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox.html
new file mode 100644
index 00000000..01c5d5c9
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox.html
@@ -0,0 +1,107 @@
+
+
+
+ ZTREE DEMO - checkbox
+
+
+
+
+
+
+
+
+
+
+
+Checkbox 勾选操作
+[ 文件路径: excheck/checkbox.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+
+ 2、treeNode 节点数据说明
+
+ 1)、如果需要初始化默认节点被勾选,请设置 treeNode.checked 属性,详细请参见 API 文档中的相关内容
+ 2)、如果某节点禁用 checkbox,请设置 treeNode.chkDisabled 属性,详细请参见 API 文档中的相关内容 和 'chkDisabled 演示'
+ 3)、如果某节点不显示 checkbox,请设置 treeNode.nocheck 属性,详细请参见 API 文档中的相关内容 和 'nocheck 演示'
+ 4)、如果更换 checked 属性,请参考 API 文档中 setting.data.key.checked 的详细说明
+ 5)、其他请参考 API 文档中 treeNode.checkedOld / getCheckStatus / check_Child_State / check_Focus 的详细说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_chkDisabled.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_chkDisabled.html
new file mode 100644
index 00000000..c3ad4664
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_chkDisabled.html
@@ -0,0 +1,112 @@
+
+
+
+ ZTREE DEMO - Checkbox chkDisabled
+
+
+
+
+
+
+
+
+
+
+
+Checkbox chkDisabled 演示
+[ 文件路径: excheck/checkbox_chkDisabled.html ]
+
+
+
+
+ 1、setChkDisabled 方法操作说明
+
+
+ 2、setting 配置信息说明
+
+ 这个 Demo 只简单演示 chkDisabled 的使用方法,详细配置信息请参考 ‘Checkbox 勾选操作’
+
+
+ 3、treeNode 节点数据说明
+
+ 1)、如果某节点禁用 checkbox,请设置 treeNode.chkDisabled 属性,详细请参见 API 文档中的相关内容
+ 2)、其他请参考 ‘Checkbox 勾选操作’
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_count.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_count.html
new file mode 100644
index 00000000..c0d84ba1
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_count.html
@@ -0,0 +1,126 @@
+
+
+
+ ZTREE DEMO - getChangeCheckedNodes / getCheckedNodes
+
+
+
+
+
+
+
+
+
+
+
+checkbox 勾选统计
+[ 文件路径: excheck/checkbox_count.html ]
+
+
+
+
+ 1、getChangeCheckedNodes / getCheckedNodes 方法操作说明
+
+
+ 2、setting 配置信息说明
+
+ 同 "checkbox 勾选操作" 中的说明
+
+
+ 3、treeNode 节点数据说明
+
+ 同 "checkbox 勾选操作" 中的说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_fun.html
new file mode 100644
index 00000000..c8a0b12c
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_fun.html
@@ -0,0 +1,172 @@
+
+
+
+ ZTREE DEMO - beforeCheck / onCheck
+
+
+
+
+
+
+
+
+
+
+
+用 zTree 方法 勾选 checkbox
+[ 文件路径: excheck/checkbox_fun.html ]
+
+
+
+
+ 1、beforeCheck / onCheck 事件回调函数控制
+
+
+ 2、setting 配置信息说明
+
+ 同 "checkbox 勾选操作" 中的说明
+
+
+ 3、treeNode 节点数据说明
+
+ 同 "checkbox 勾选操作" 中的说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_halfCheck.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_halfCheck.html
new file mode 100644
index 00000000..21de1e07
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_halfCheck.html
@@ -0,0 +1,108 @@
+
+
+
+ ZTREE DEMO - Checkbox halfCheck
+
+
+
+
+
+
+
+
+
+
+
+Checkbox halfCheck 演示
+[ 文件路径: excheck/checkbox_halfCheck.html ]
+
+
+
+
+ 1、演示说明
+
+ 这个演示式实现了 异步加载模式 下简单的勾选操作
+ 1)、加载子节点后,父节点的 halfCheck 立刻失效
+ 2)、勾选父节点,可以影响之后加载的子节点的勾选状态
+ 3)、勾选父节点,可以让其子节点的 halfCheck 属性失效
+
+
+ 2、setting 配置信息说明
+
+ 实现半勾选功能,不需要配置任何参数。但半勾选功能属于辅助功能,无法单独使用,因此也需要根据自己的需求配置特定参数。
+
+
+ 3、treeNode 节点数据说明
+
+ 1)、请在节点初始化之前,设置 treeNode.halfCheck 属性,详细请参见 API 文档中的相关内容
+ 2)、其他请参考 ‘Checkbox 勾选操作’
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_nocheck.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_nocheck.html
new file mode 100644
index 00000000..5e990155
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/checkbox_nocheck.html
@@ -0,0 +1,96 @@
+
+
+
+ ZTREE DEMO - no checkbox
+
+
+
+
+
+
+
+
+
+
+
+Checkbox nocheck 演示
+[ 文件路径: excheck/checkbox_nocheck.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 这个 Demo 只简单演示 nocheck 的使用方法,详细配置信息请参考 ‘Checkbox 勾选操作’
+ setting.check.nocheckInherit 可以设置子节点自动继承父节点的 nocheck 属性,详细请参见 API 文档中的相关内容
+ 试试看:
+ [ 隐藏 ]
+ [ 显示 ]
+
+
+
+ 2、treeNode 节点数据说明
+
+ 1)、如果某节点不显示 checkbox,请设置 treeNode.nocheck 属性,详细请参见 API 文档中的相关内容
+ 2)、其他请参考 ‘Checkbox 勾选操作’
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio.html
new file mode 100644
index 00000000..9cd589a5
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio.html
@@ -0,0 +1,97 @@
+
+
+
+ ZTREE DEMO - radio
+
+
+
+
+
+
+
+
+
+
+
+Radio 勾选操作
+[ 文件路径: excheck/radio.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+
+ 2、treeNode 节点数据说明
+
+ 1)、如果需要初始化默认节点被勾选,请设置 treeNode.checked 属性,详细请参见 API 文档中的相关内容
+ 2)、如果某节点不显示 radio,请设置 treeNode.nocheck 属性,详细请参见 API 文档中的相关内容
+ 3)、如果更换 checked 属性,请参考 API 文档中 setting.data.key.checked 的详细说明
+ 4)、其他请参考 API 文档中 treeNode.checkedOld / getCheckStatus / check_Child_State / check_Focus 的详细说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_chkDisabled.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_chkDisabled.html
new file mode 100644
index 00000000..6d330f3b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_chkDisabled.html
@@ -0,0 +1,101 @@
+
+
+
+ ZTREE DEMO - Radio chkDisabled
+
+
+
+
+
+
+
+
+
+
+
+Radio chkDisabled 演示
+[ 文件路径: excheck/radio_chkDisabled.html ]
+
+
+
+
+ 1、setChkDisabled 方法操作说明
+
+ setChkDisabled 方法可以设置节点 checkbox / radio 禁用 或 取消禁用,详细请参见 API 文档中的相关内容
+ 试试看:
+ [ 禁用 ]
+ [ 取消禁用 ]
+
+
+
+ 2、setting 配置信息说明
+
+ 这个 Demo 只简单演示 chkDisabled 的使用方法,详细配置信息请参考 ‘Radio 勾选操作’
+
+
+ 3、treeNode 节点数据说明
+
+ 1)、如果某节点不显示 radio,请设置 treeNode.chkDisabled 属性,详细请参见 API 文档中的相关内容
+ 2)、其他请参考 ‘Radio 勾选操作’
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_fun.html
new file mode 100644
index 00000000..b9b52401
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_fun.html
@@ -0,0 +1,151 @@
+
+
+
+ ZTREE DEMO - beforeCheck / onCheck
+
+
+
+
+
+
+
+
+
+
+
+用 zTree 方法 勾选 radio
+[ 文件路径: excheck/radio_fun.html ]
+
+
+
+
+ 1、beforeCheck / onCheck 事件回调函数控制
+
+
+ 2、setting 配置信息说明
+
+
+ 3、treeNode 节点数据说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_halfCheck.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_halfCheck.html
new file mode 100644
index 00000000..91f25cff
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_halfCheck.html
@@ -0,0 +1,93 @@
+
+
+
+ ZTREE DEMO - Radio halfCheck
+
+
+
+
+
+
+
+
+
+
+
+Radio halfCheck 演示
+[ 文件路径: excheck/radio_halfCheck.html ]
+
+
+
+
+ 1、演示说明
+
+ 这个演示式实现了 异步加载模式 下简单的勾选操作
+ 1)、加载子节点后,父节点的 halfCheck 立刻失效
+ 2)、勾选父节点,不会影响子节点的勾选状态
+ 3)、勾选父节点,不会让其子节点的 halfCheck 属性失效
+
+
+ 2、setting 配置信息说明
+
+ 实现半勾选功能,不需要配置任何参数。但半勾选功能属于辅助功能,无法单独使用,因此也需要根据自己的需求配置特定参数。
+
+
+ 3、treeNode 节点数据说明
+
+ 1)、请在节点初始化之前,设置 treeNode.halfCheck 属性,详细请参见 API 文档中的相关内容
+ 2)、其他请参考 ‘Radio 勾选操作’
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_nocheck.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_nocheck.html
new file mode 100644
index 00000000..cc648c17
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/excheck/radio_nocheck.html
@@ -0,0 +1,77 @@
+
+
+
+ ZTREE DEMO - no radio
+
+
+
+
+
+
+
+
+
+
+
+Radio nocheck 演示
+[ 文件路径: excheck/radio_nocheck.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+ 这个 Demo 只简单演示 nocheck 的使用方法,详细配置信息请参考 ‘Radio 勾选操作’
+ setting.check.nocheckInherit 可以设置子节点自动继承父节点的 nocheck 属性,详细请参见 API 文档中的相关内容
+
+
+ 2、treeNode 节点数据说明
+
+ 1)、如果某节点不显示 radio,请设置 treeNode.nocheck 属性,详细请参见 API 文档中的相关内容
+ 2)、其他请参考 ‘Radio 勾选操作’
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/async_edit.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/async_edit.html
new file mode 100644
index 00000000..eb577fbc
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/async_edit.html
@@ -0,0 +1,120 @@
+
+
+
+ ZTREE DEMO - async & edit
+
+
+
+
+
+
+
+
+
+
+
+
+异步加载 & 编辑功能 共存
+[ 文件路径: exedit/async_edit.html ]
+
+
+
+
+ 1、异步加载 & 编辑功能 共存说明
+
+ 1)、此 Demo 是基于 "高级 增 / 删 / 改 节点" 修改的,并且开放了拖拽功能,可与其进行对比
+ 2)、只需要同时开启编辑模式和异步加载模式即可实现共存
+ 3)、zTree 完善了异步加载模式下的编辑功能,对于未加载子节点的父节点,增加子节点时,会首先进行异步加载。
+
+
+ 2、setting 配置信息说明
+
+ 1)、使用 编辑功能,请参考 "拖拽节点基本控制" 和 "基本 增 / 删 / 改 节点" Demo 中关于 setting 的配置要求
+ 2)、使用 异步加载功能,请参考 "异步加载 节点数据" Demo 中关于 setting 的配置要求
+
+
+ 3、treeNode 节点数据说明
+
+ 两种模式共存,对数据无特殊要求,请分别参考 "异步加载 节点数据" & "拖拽 节点 基本控制" & "基本 增 / 删 / 改 节点" 中的相关说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag.html
new file mode 100644
index 00000000..a19da79e
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag.html
@@ -0,0 +1,136 @@
+
+
+
+ ZTREE DEMO - drag & drop
+
+
+
+
+
+
+
+
+
+
+
+拖拽节点基本控制
+[ 文件路径: exedit/drag.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+
+ 2、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag_fun.html
new file mode 100644
index 00000000..963068de
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag_fun.html
@@ -0,0 +1,180 @@
+
+
+
+ ZTREE DEMO - copyNode / moveNode
+
+
+
+
+
+
+
+
+
+
+
+
+用 zTree 方法 移动 / 复制节点
+[ 文件路径: exedit/drag_fun.html ]
+
+
+
+
+ 1、copyNode / moveNode 方法操作说明
+
+
+ 2、setting 配置信息说明
+
+
+ 3、treeNode 节点数据说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag_super.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag_super.html
new file mode 100644
index 00000000..668746f7
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/drag_super.html
@@ -0,0 +1,210 @@
+
+
+
+ ZTREE DEMO - beforeDrag / onDrag / beforeDrop / onDrop
+
+
+
+
+
+
+
+
+
+
+
+拖拽节点高级控制
+[ 文件路径: exedit/drag_super.html ]
+
+
+
+
+ 1、beforeDrag / onDrag beforeDrop / onDrop 事件回调函数控制
+
+
+ 2、setting 配置信息说明
+
+
+ 3、treeNode 节点数据说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit.html
new file mode 100644
index 00000000..ed787d76
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit.html
@@ -0,0 +1,115 @@
+
+
+
+ ZTREE DEMO - edit
+
+
+
+
+
+
+
+
+
+
+
+基本 增 / 删 / 改 节点
+[ 文件路径: exedit/edit.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+
+ 2、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit_fun.html
new file mode 100644
index 00000000..6201683c
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit_fun.html
@@ -0,0 +1,194 @@
+
+
+
+ ZTREE DEMO - addNodes / editName / removeNode / removeChildNodes
+
+
+
+
+
+
+
+
+
+
+
+用 zTree 方法 增 / 删 / 改 节点
+[ 文件路径: exedit/edit_fun.html ]
+
+
+
+
+ 1、addNodes / editName / removeNode / removeChildNodes 方法操作说明
+
+
+ 2、setting 配置信息说明
+
+ 同 "基本 增 / 删 / 改 节点"
+ 保持 父 / 叶子 节点状态,需要设置 setting.data.keep.parent / leaf 属性,详细请参见 API 文档中的相关内容
+
+
+ 3、treeNode 节点数据说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit_super.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit_super.html
new file mode 100644
index 00000000..fb56de22
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/edit_super.html
@@ -0,0 +1,183 @@
+
+
+
+ ZTREE DEMO - beforeEditName / beforeRemove / onRemove / beforeRename / onRename
+
+
+
+
+
+
+
+
+
+
+
+
+高级 增 / 删 / 改 节点
+[ 文件路径: exedit/edit_super.html ]
+
+
+
+
+ 1、beforeEditName beforeRemove / onRemove beforeRename / onRename 事件回调函数控制
+
+
+ 2、setting 配置信息说明
+
+
+ 3、treeNode 节点数据说明
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/multiTree.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/multiTree.html
new file mode 100644
index 00000000..a4736a2a
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exedit/multiTree.html
@@ -0,0 +1,94 @@
+
+
+
+ ZTREE DEMO - multiTree
+
+
+
+
+
+
+
+
+
+
+
+多棵树之间 的 数据交互
+[ 文件路径: exedit/multiTree.html ]
+
+
+
+ 1、setting 配置信息说明
+
+ zTree 对于多棵树之间拖拽的操作非常简单,只需要创建两棵可拖拽的树即可,同时可根据 各种事件回调函数 以及 zTree 的方法配合实现较复杂的操作规则,这里只是基本演示。
+ 关于配置信息请参考拖拽、编辑等 Demo 的详细说明
+
+
+ 2、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/checkbox.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/checkbox.html
new file mode 100644
index 00000000..bf730818
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/checkbox.html
@@ -0,0 +1,161 @@
+
+
+
+ ZTREE DEMO - Hide With Checkbox Mode
+
+
+
+
+
+
+
+
+
+
+
+
+配合 checkbox 的隐藏
+[ 文件路径: exhide/checkbox.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+
+ 2、treeNode 节点数据说明
+
+
+ 3、其他说明
+
+ 隐藏节点操作,会影响 isFirstNode 和 isLastNode 属性,但是对于 getPreNode() 和 getNextNode() 方法无影响
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/common.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/common.html
new file mode 100644
index 00000000..600d1c03
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/common.html
@@ -0,0 +1,126 @@
+
+
+
+ ZTREE DEMO - Hide Ordinary Node
+
+
+
+
+
+
+
+
+
+
+
+隐藏普通节点
+[ 文件路径: exhide/common.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+
+ 2、treeNode 节点数据说明
+
+
+ 3、其他说明
+
+ 隐藏节点操作,会影响 isFirstNode 和 isLastNode 属性,但是对于 getPreNode() 和 getNextNode() 方法无影响
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/radio.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/radio.html
new file mode 100644
index 00000000..5273b73a
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/exhide/radio.html
@@ -0,0 +1,162 @@
+
+
+
+ ZTREE DEMO - Hide With Radio Mode
+
+
+
+
+
+
+
+
+
+
+
+
+配合 radio 的隐藏
+[ 文件路径: exhide/radio.html ]
+
+
+
+
+ 1、setting 配置信息说明
+
+
+ 2、treeNode 节点数据说明
+
+
+ 3、其他说明
+
+ 隐藏节点操作,会影响 isFirstNode 和 isLastNode 属性,但是对于 getPreNode() 和 getNextNode() 方法无影响
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/index.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/index.html
new file mode 100644
index 00000000..551eebcd
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/index.html
@@ -0,0 +1,157 @@
+
+
+
+ ZTREE DEMO
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/asyncForAll.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/asyncForAll.html
new file mode 100644
index 00000000..8a38d986
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/asyncForAll.html
@@ -0,0 +1,191 @@
+
+
+
+ ZTREE DEMO - async for All
+
+
+
+
+
+
+
+
+
+
+异步加载模式下全部展开
+[ 文件路径: super/asyncForAll.html ]
+
+
+
+
+ 实现方法说明
+
+ 利用 onAsyncSuccess / onAsyncError 回调函数 和 reAsyncChildNodes 或 expandNode 方法可以实现全部功能。
+ 如果父节点数量很大,请注意利用延时进行控制,避免异步进程过多。
+ 建议:演示时请利用调试工具查看 network 的 ajax 加载过程。
+ 演示操作
+
+ [ 全部展开 ]
+ [ 后台自动全部加载 ]
+ [ Reset zTree ]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/checkbox_radio.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/checkbox_radio.html
new file mode 100644
index 00000000..66a3b035
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/checkbox_radio.html
@@ -0,0 +1,116 @@
+
+
+
+ ZTREE DEMO - checkbox & radio
+
+
+
+
+
+
+
+
+
+
+
+checkbox / radio 共存
+[ 文件路径: super/checkbox_radio.html ]
+
+
+
+
+ 实现方法说明
+
+ zTree 默认的 checkbox 和 radio 是无法共存的,但可以利用 自定义控件 的功能实现此需求,具体需求可以参考 "添加 自定义控件" 的详细说明
+ 对于 checkbox / radio 的联动规则,请根据需求制定自己的规则
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/diydom.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/diydom.html
new file mode 100644
index 00000000..1980ff9f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/diydom.html
@@ -0,0 +1,177 @@
+
+
+
+ ZTREE DEMO - addHoverDom / removeHoverDom / addDiyDom
+
+
+
+
+
+
+
+
+
+
+
+
+添加自定义控件
+[ 文件路径: super/diydom.html ]
+
+
+
+
+ 1、实现方法说明
+
+ 利用 setting.view.addHoverDom / removeHoverDom / addDiyDom 这几个参数的配置可以很容易的实现自定义控件的功能
+ 添加自定义控件,请务必掌握 zTree 节点对象的命名规则,以保证正常添加 DOM 控件
+ 如果添加标准的 select / checkbox / radio 等,请注意适当调整 zTree 的布局 css,保证 zTree 能正常显示
+
+
+ 2、setting 配置信息说明
+
+ 使用 setting.view.addHoverDom / removeHoverDom / addDiyDom 属性,详细请参见 API 文档中的相关内容
+
+
+ 3、treeNode 节点数据说明
+
+ 对 节点数据 没有特殊要求,用户可以根据自己的需求添加自定义属性
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/dragWithOther.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/dragWithOther.html
new file mode 100644
index 00000000..04a5b7c4
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/dragWithOther.html
@@ -0,0 +1,235 @@
+
+
+
+ ZTREE DEMO - drag with other DOM
+
+
+
+
+
+
+
+
+
+
+
+
+与其他 DOM 拖拽互动
+[ 文件路径: super/dragWithOther.html ]
+
+
+
+
+ 实现方法说明
+
+ zTree v3.2 版本修正了 onDrag/onDrop 中的 event 对象,因此可以较容易的控制将节点拖拽到其他 DOM
+ 将其他 DOM 拖拽到 zTree 需要自己制作相关的拖拽代码
+ 这仅仅是一个最简单的演示,如果需要更炫的效果,需要制作更复杂的代码
+
+
+ 请拖拽下面内容到 树节点上
+
+ 也可以把二级节点拖拽到以上图层
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menu.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menu.html
new file mode 100644
index 00000000..4b9941a2
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menu.html
@@ -0,0 +1,152 @@
+
+
+
+ ZTREE DEMO - left_menu
+
+
+
+
+
+
+
+
+
+
+
+左侧菜单
+[ 文件路径: super/left_menu.html ]
+
+
+
+
+ 实现方法说明
+
+ 在 “Demo 演示”中的左侧菜单就是用 zTree 实现的,主要是靠 css 样式负责排版,利用 setting 中的配置进行功能制作,本 Demo 对实现 左侧菜单进行简单的介绍,你也可以通过查看 “Demo 演示”页面的源码深入了解。
+ 1、关于 css 完全可以根据自己的需要进行个性化调整,例如:此 Demo 的菜单样式 就 与 “Demo 演示”页面的不一样,仅供对比参考
+ 2、主要用到的配置有:
+ setting.view.showIcon / showLine / selectedMulti / dblClickExpand
+ setting.callback.onNodeCreated / beforeClick / onClick
+
+ 3、是否需要限制单一路径展开,完全由你的需求而定,实现代码可参考 "保持展开单一路径"
+ 4、其他辅助规则,请根据实际情况自行编写
+
+
+
+
+
+
+
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.gif b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.gif
new file mode 100644
index 00000000..c252f0f8
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.gif differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.html
new file mode 100644
index 00000000..bf6eb30c
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.html
@@ -0,0 +1,136 @@
+
+
+
+ ZTREE DEMO - left_menu for Outlook
+
+
+
+
+
+
+
+
+
+
+OutLook 样式的左侧菜单
+[ 文件路径: super/left_menuForOutLook.html ]
+
+
+
+
+ 实现方法说明
+
+ 帮朋友用 zTree 实现了一个貌似 Outlook.com 的菜单,特拿出来分享给大家
+ 1、请注意本页面源码中的 css 部分
+ 2、请查看源码中 js 的使用,特别是利用 addDiyDom 回调将 展开按钮 转移到 <a> 标签内
+ 3、利用添加 zTree 的 class 实现展开按钮的隐藏、显示
+ 4、其他辅助规则,请根据实际情况自行编写
+ 5、当前规则说明:
+ 单击根节点可以展开、折叠;
+ 非根节点只有点击 箭头 图标才可以展开、折叠;
+
+
+
+
+
+
+
+
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.png
new file mode 100644
index 00000000..36e8acfa
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/left_menuForOutLook.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/oneclick.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/oneclick.html
new file mode 100644
index 00000000..93e32a40
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/oneclick.html
@@ -0,0 +1,87 @@
+
+
+
+ ZTREE DEMO - one click
+
+
+
+
+
+
+
+
+
+
+
+单击展开/折叠节点
+[ 文件路径: super/oneclick.html ]
+
+
+
+
+ 实现方法说明
+
+ zTree 默认不提供单击展开节点的功能,但可以利用 onClick 事件回调函数轻松实现此功能
+ 为了避免与双击功能冲突,建议关闭双击展开节点的功能,请设置 setting.view.dblClickExpand = false
+ 可以将所有的 +/- 开关隐藏,请查看本页面源码,查看 css 设置
+ 由于 +/- 开关与 节点连接线是配套的,所以如果不显示 +/- 开关的话,那么请设置 setting.view.showLine = false 隐藏连接线
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/oneroot.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/oneroot.html
new file mode 100644
index 00000000..6624e7bb
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/oneroot.html
@@ -0,0 +1,79 @@
+
+
+
+ ZTREE DEMO - one root
+
+
+
+
+
+
+
+
+
+
+
+冻结根节点
+[ 文件路径: super/oneroot.html ]
+
+
+
+
+ 实现方法说明
+
+ 对于这种只有一个根节点,且不显示 +/- 开关的需求,需要利用 css、setting 配合使用
+ zTree v3.x 可以针对指定的 level,进行样式设定,请查看本页面源码,查看 css 设置
+ 设置 setting.view.dblClickExpand 指向 Function,可以只针对根节点关闭双击展开的操作
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/rightClickMenu.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/rightClickMenu.html
new file mode 100644
index 00000000..9e5b4045
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/rightClickMenu.html
@@ -0,0 +1,168 @@
+
+
+
+ ZTREE DEMO - select menu
+
+
+
+
+
+
+
+
+
+
+
+
+右键菜单的实现
+[ 文件路径: super/rightClickMenu.html ]
+
+
+
+
+ 实现方法说明
+
+ 利用 beforeRightClick / onRightClick 事件回调函数简单实现的右键菜单
+ Demo 中的菜单比较简陋,你完全可以配合其他自定义样式的菜单图层混合使用
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu.html
new file mode 100644
index 00000000..67787c66
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu.html
@@ -0,0 +1,124 @@
+
+
+
+ ZTREE DEMO - select menu
+
+
+
+
+
+
+
+
+
+
+
+下拉菜单
+[ 文件路径: super/select_menu.html ]
+
+
+
+
+ 实现方法说明
+
+ 用 zTree 实现这种下拉菜单,应该说是比较容易的,你只需要控制 zTree 所在容器的隐藏/显示,以及位置即可。
+ zTree v3.x 实现了多点选中功能,因此对于需要多选的下拉菜单也易如反掌。
+ 利用 setting 的各项配置参数,完全可以满足大部分的功能需求。
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu_checkbox.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu_checkbox.html
new file mode 100644
index 00000000..529b01dd
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu_checkbox.html
@@ -0,0 +1,126 @@
+
+
+
+ ZTREE DEMO - checkbox select menu
+
+
+
+
+
+
+
+
+
+
+
+
+带 checkbox 的多选下拉菜单 -- zTree
+[ 文件路径: super/select_menu_checkbox.html ]
+
+
+
+
+ 实现方法说明
+
+ 用 checkbox 也可以轻松实现多选的下拉菜单
+ 此 Demo 主要用于 和 其他下拉菜单进行对比
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu_radio.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu_radio.html
new file mode 100644
index 00000000..cc1d4395
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/select_menu_radio.html
@@ -0,0 +1,127 @@
+
+
+
+ ZTREE DEMO - radio select menu
+
+
+
+
+
+
+
+
+
+
+
+
+带 radio 的单选下拉菜单 -- zTree
+[ 文件路径: super/select_menu_radio.html ]
+
+
+
+
+ 实现方法说明
+
+ 用 radio 也可以轻松实现单选的下拉菜单
+ 单选其实没有必要使用 radio, 此 Demo 主要用于 和 其他下拉菜单进行对比
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/singlepath.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/singlepath.html
new file mode 100644
index 00000000..90efb649
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/cn/super/singlepath.html
@@ -0,0 +1,150 @@
+
+
+
+ ZTREE DEMO - single path
+
+
+
+
+
+
+
+
+
+
+
+保持展开单一路径
+[ 文件路径: super/singlepath.html ]
+
+
+
+
+ 实现方法说明
+
+ 此 Demo 是在 "单击展开/折叠节点" 基础上改造而来,树节点保持始终只展开一条路径。
+ 利用 setting.callback.beforeExpand / onExpand 事件回调函数实现展开规则
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/asyncData/getNodes.php b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/asyncData/getNodes.php
new file mode 100644
index 00000000..3294d9a1
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/asyncData/getNodes.php
@@ -0,0 +1,38 @@
+
+[]
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/asyncData/getNodesForBigData.php b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/asyncData/getNodesForBigData.php
new file mode 100644
index 00000000..91d002e1
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/asyncData/getNodesForBigData.php
@@ -0,0 +1,24 @@
+
+[]
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/common.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/common.html
new file mode 100644
index 00000000..6e1728da
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/common.html
@@ -0,0 +1,190 @@
+
+
+
+ ZTREE DEMO - big data common
+
+
+
+
+
+
+
+
+
+
+
+One-time Large Data Loading
+[ File Path: bigdata/common.html ]
+
+
+
+ Adjust the total number of nodes to the test load speed:
+
+
+
+
+
+
+
+
+ 1, Explanation of large data load
+
+ 1) zTree v3.x optimized for one-time large data loading capacity, using a lazy loading technique, which does not expand the node does not create child nodes of the DOM.
+ 2) If a maximum of 100 nodes each, but a total number of several thousand or even tens of thousands of nodes, and the parent node is collapsed by default to optimize the most obvious effect, very fast.
+ 3) For the next level there are thousands of sub-node case, the optimization of lazy loading is invalid, proposal to consider asynchronous page loading.
+ 4) if you set the data to all the parent nodes are expanded, the optimization of lazy loading is invalid, proposal to not expand all parent node when you initialize zTree.
+ 5) set the display checkbox / radio will affect some of the performance.
+ 6) DOM generated using addDiyDom function will affect the speed, the number of nodes determines the number of impact situations.
+ 7) Using 'onNodeCreated' callback function to manipulate nodes object will affect the speed, the degree of influence determined by the number of nodes.
+
+
+ 2, Explanation of setting
+
+ No special configuration.
+
+
+ 3, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/diy_async.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/diy_async.html
new file mode 100644
index 00000000..13e43aa3
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/diy_async.html
@@ -0,0 +1,157 @@
+
+
+
+ ZTREE DEMO - big data async
+
+
+
+
+
+
+
+
+
+
+
+Loading Data in Batches
+[ File Path: bigdata/diy_async.html ]
+
+
+
+ Demo for testing load data in batches, each node needs to re-start to load.
+
+
+
+
+
+ 1, Explanation of large data load
+
+ 1) If has nodes for as many as thousands in one level, lazy loading is invalid, this demo shows how to load data in batches.
+ 2) This method applies to thousands of nodes must all display needs.
+ 3) This method doesn't solve the problem of slow loading, it will only make the final result appear more slowly, but can be limited to avoid browser suspended animation, and more nodes displayed slower.
+ 4) For at least several thousand nodes in one level case, another solution is to: pagination loading.
+ async load log:
+
+
+
+ 2, Explanation of setting
+
+ Need to set the parameters in setting.async
+ Advised to turn off animation effects: setting.view.expandSpeed = "";
+ No other special configuration, the user can set their own requirements.
+
+
+ 3, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/page.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/page.html
new file mode 100644
index 00000000..f8a4963f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/bigdata/page.html
@@ -0,0 +1,150 @@
+
+
+
+ ZTREE DEMO - big data page
+
+
+
+
+
+
+
+
+
+
+
+
+Loading Data By Pagination
+[ File Path: bigdata/page.html ]
+
+
+
+
+ 1, Explanation of large data load
+
+ 1) Pagination can be an effective solution to the large number of child nodes of the situation.
+ 2) Using custom method to display the pagination button.
+ 3) Pagination lead to the association of checkbox can not be achieved, only correction after show each page . Because of the time limitation, Demo does not deal with association of checkbox.
+ 4) The use of pagination, you can only get the current page node data from zTree. You can save the data after each page loading as the cache, according to the demand to determine the specific methods.
+
+ 2, Explanation of setting
+
+ Need to set the parameters in setting.async
+ No other special configuration, the user can set their own requirements.
+
+
+ 3, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/async.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/async.html
new file mode 100644
index 00000000..9668fe66
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/async.html
@@ -0,0 +1,70 @@
+
+
+
+ ZTREE DEMO - Async
+
+
+
+
+
+
+
+
+
+
+Dynamic Tree with Ajax
+[ File Path: core/async.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ For create dynamic tree with ajax, you need to set attributes in setting.async, see the API documentation for more related contents.
+
+
+ 2, Explanation of treeNode
+
+ Dynamic tree with ajax doesn't need to make special treeNode node data, if use simple JSON data model, please set the attributes in setting.data.simple.
+ If ajax only to return node data in single level, you don't need to use simple JSON data model.
+
+
+ 3、Other explanation
+
+ Monitoring autoParam and otherParam use firebug or the developer tools in browser.
+ This Demo only loading 4 level nodes (level = 3).
+ This Demo use 'dataFilter' to modify the name of the node.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/async_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/async_fun.html
new file mode 100644
index 00000000..37aea062
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/async_fun.html
@@ -0,0 +1,144 @@
+
+
+
+ ZTREE DEMO - reAsyncChildNodes
+
+
+
+
+
+
+
+
+
+
+
+Dynamic Tree - zTree methods
+[ File Path: core/async_fun.html ]
+
+
+
+
+ 1, Explanation of reAsyncChildNodes
+
+ How to use zTreeObj.reAsyncChildNodes method, please see the API documentation.
+ This Demo can only select one parent node
+ Try: [ reload ] [ append ]
+
+ 'isSilent' parameter is only for the collapsed state of the parent node.
+ Try: [ Quietly reloaded ] [ Quietly append ]
+ async log:
+
+
+
+
+ 2, Explanation of setting
+
+ For useing reAsyncChildNodes method, you need to set attributes in setting.async, see the API documentation for more related contents.
+
+
+ 3, Explanation of treeNode
+
+ Same as 'Dynamic Tree with Ajax'
+
+
+ 4、Other explanation
+
+ Same as 'Dynamic Tree with Ajax'
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/click.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/click.html
new file mode 100644
index 00000000..4f0fa80e
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/click.html
@@ -0,0 +1,107 @@
+
+
+
+ ZTREE DEMO - beforeClick / onClick
+
+
+
+
+
+
+
+
+
+
+Control of Click Node
+[ File Path: core/click.html ]
+
+
+
+
+ 1, 'beforeClick / onClick' callback function
+
+
+ 2, Explanation of setting
+
+ You need to set attributes about setting.callback.beforeClick and setting.callback.onClick, see the API documentation for more related contents.
+
+
+ 3, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_font.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_font.html
new file mode 100644
index 00000000..e00253b2
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_font.html
@@ -0,0 +1,67 @@
+
+
+
+ ZTREE DEMO - Custom Font
+
+
+
+
+
+
+
+
+
+
+Custom Fonts
+[ File Path: core/custom_font.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ 1. For display custom fonts, you need to set setting.view.fontCss attribute, see the API documentation for more related contents.
+ 2. If you need the node name support the HTML, please set the 'setting.view.nameIsHTML' attribute, see the API documentation for more related contents.
+
+
+ 2, Explanation of treeNode
+
+ Set the font does not require to special node data , but if used to differentiate between different types of nodes, the corresponding data can set custom attributes.
+ The css style of data stored in the nodes within this demo
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_icon.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_icon.html
new file mode 100644
index 00000000..17084e57
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_icon.html
@@ -0,0 +1,74 @@
+
+
+
+ ZTREE DEMO - Custom Icon
+
+
+
+
+
+
+
+
+
+
+Custom Icon - icon
+[ File Path: core/custom_icon.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ Custom icon does not require special configuration setting.
+
+
+ 2, Explanation of treeNode
+
+ Use node data’s icon / iconOpen / iconClose attribute show custom icon.
+ See the API documentation for more related contents.
+
+
+ 3、Other explanation
+
+ Because of the time limitation, demo uses png images, if you need transparent images in IE6 browser, make a gif image, or use a special 'css filter' on ie6 browser.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_iconSkin.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_iconSkin.html
new file mode 100644
index 00000000..3e099300
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/custom_iconSkin.html
@@ -0,0 +1,85 @@
+
+
+
+ ZTREE DEMO - Custom Icon Skin
+
+
+
+
+
+
+
+
+
+
+
+Custom Icon - iconSkin
+[ File Path: core/custom_iconSkin.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ Custom icon does not require special configuration setting.
+
+
+ 2, Explanation of treeNode
+
+ Use the node data iconSkin attributes and css show custom icon.
+ See the API documentation for more related contents.
+
+
+ 3、Other explanation
+
+ Because of the time limitation, demo uses png images, if you need transparent images in IE6 browser, make a gif image, or use a special 'css filter' on ie6 browser.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/expand.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/expand.html
new file mode 100644
index 00000000..ca604741
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/expand.html
@@ -0,0 +1,186 @@
+
+
+
+ ZTREE DEMO - beforeExpand / onExpand && beforeCollapse / onCollapse
+
+
+
+
+
+
+
+
+
+
+Control of Expand Node
+[ File Path: core/expand.html ]
+
+
+
+
+ 1, 'beforeCollapse / onCollapse' 'beforeExpand / onExpand' callback function
+
+
+ 2, Explanation of setting
+
+ Set attributes about 'setting.callback.beforeCollapse / onCollapse / beforeExpand / onExpand', please see the API documentation for more related contents.
+ If you need to adjust the expand / collapse animation effects, please see the API documentation about setting.view.expandSpeed.
+
+
+ 3, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/noicon.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/noicon.html
new file mode 100644
index 00000000..19a66648
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/noicon.html
@@ -0,0 +1,93 @@
+
+
+
+ ZTREE DEMO - noIcon
+
+
+
+
+
+
+
+
+
+
+
+Don't Show Icon
+[ File Path: core/noicon.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ This Demo using Function set to the parent node does not display the icon.
+ Whether to display the icon, you need to set setting.view.showIcon attribute, see the API documentation for more related contents.
+
+
+ 2, Explanation of treeNode
+
+ TreeNode data does not require special settings. If you need some node display icon, and some nodes are not displayed, you can add special attributes to the node data for identification.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/noline.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/noline.html
new file mode 100644
index 00000000..d61792f7
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/noline.html
@@ -0,0 +1,88 @@
+
+
+
+ ZTREE DEMO - noLine
+
+
+
+
+
+
+
+
+
+
+
+Don't Show Line
+[ File Path: core/noline.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ Whether to display the line, you need to set setting.view.showLine attribute, see the API documentation for more related contents.
+
+
+ 2, Explanation of treeNode
+
+ TreeNode data does not require special settings
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/otherMouse.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/otherMouse.html
new file mode 100644
index 00000000..0e18a0c7
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/otherMouse.html
@@ -0,0 +1,132 @@
+
+
+
+ ZTREE DEMO - Other Mouse Event
+
+
+
+
+
+
+
+
+
+
+Other Mouse Events for zTree
+[ File Path: core/otherMouse.html ]
+
+
+
+
+ 1, 'beforeMousedown / onMousedown / beforeMouseup / onMouseup / beforeRightClick / onRightClick' callback function
+
+
+ 2, Explanation of setting
+
+ Set attributes about 'setting.callback.beforeMousedown / onMousedown / beforeMouseup / onMouseup / beforeRightClick / onRightClick', please see the API documentation for more related contents.
+
+
+ 3, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/searchNodes.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/searchNodes.html
new file mode 100644
index 00000000..1d0695aa
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/searchNodes.html
@@ -0,0 +1,173 @@
+
+
+
+ ZTREE DEMO - getNodeByParam / getNodesByParam / getNodesByParamFuzzy
+
+
+
+
+
+
+
+
+
+
+Search Nodes
+[ File Path: core/searchNodes.html ]
+
+
+
+
+ 1, Explanation of getNodeByParam / getNodesByParam / getNodesByParamFuzzy
+
+
+ 2, Explanation of setting
+
+ Do not need to set the setting.
+
+
+ 3, Explanation of treeNode
+
+ Note: Please ensure that the incoming value type is the same as type of attribute.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/simpleData.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/simpleData.html
new file mode 100644
index 00000000..bf07831e
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/simpleData.html
@@ -0,0 +1,100 @@
+
+
+
+ ZTREE DEMO - Simple Data
+
+
+
+
+
+
+
+
+
+
+Simple JSON Data
+[ File Path: core/simpleData.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ Must set setting.data.simple attributes, see the API documentation for more related contents.
+ The setting.view in API documentation is associated with the display of the zTree.
+ To change the 'name', 'children', 'title' attribute, please refer to the API documentation about 'setting.data.key'.
+
+
+ 2, Explanation of treeNode
+
+ Id / pId that include parent-child relationship between nodes is needed if you use Simple model of the JSON data, and other attributes please refer to the instructions within setting.data.simple.
+ For example:
+var nodes = [
+ {id:1, pId:0, name: "pNode 01"},
+ {id:11, pId:1, name: "child 01"},
+ {id:12, pId:1, name: "child 02"}
+];
+ To set nodes expanded by default, set treeNode.open attribute.
+ To get parent node which has no child node, set treeNode.isParent attribute.
+ Please refer to the API documentation "treeNode data details" to view other attributes description.
+
+
+ 3、Other explanation
+
+ The simple JSON data model has been used almost everywhere, which can be learned easily.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/standardData.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/standardData.html
new file mode 100644
index 00000000..909a63b0
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/standardData.html
@@ -0,0 +1,106 @@
+
+
+
+ ZTREE DEMO - Standard Data
+
+
+
+
+
+
+
+
+
+
+Standard JSON Data
+[ File Path: core/standardData.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ No extrally setting needed for basic functions.
+ The setting.view in API documentation is associated with the display of the zTree.
+ To change the 'name', 'children', 'title' attribute, please refer to the API documentation about 'setting.data.key'.
+
+
+ 2, Explanation of treeNode
+
+ Need to use nested JSON data that include parent-child relationship between nodes
+ For example:
+var nodes = [
+ {name: "pNode 01", children: [
+ {name: "child 01"},
+ {name: "child 02"}
+ ]}
+];
+
+ To set nodes expanded by default, set treeNode.open attribute.
+ No child nodes of parent node, set treeNode.isParent attribute.
+ Please refer to the API documentation "treeNode data details" to view other attributes description.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/update_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/update_fun.html
new file mode 100644
index 00000000..0909a34c
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/update_fun.html
@@ -0,0 +1,143 @@
+
+
+
+ ZTREE DEMO - updateNode
+
+
+
+
+
+
+
+
+
+
+
+Update Node - zTree methods
+[ File Path: core/update_fun.html ]
+
+
+
+
+ 1, Explanation of updateNode
+
+
+ 2, Explanation of setting
+
+ Do not need to set the setting.
+ You can modify the node style with setting.view.fontCss.
+
+
+ 3, Explanation of treeNode
+
+ In the API documentation has detail description about the 'zTreeObj.updateNode method'.
+
+
+ 4、Other explanation
+
+ This demo only made by the core js, so does not include the checkbox update.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/url.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/url.html
new file mode 100644
index 00000000..788b03f1
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/core/url.html
@@ -0,0 +1,63 @@
+
+
+
+ ZTREE DEMO - url
+
+
+
+
+
+
+
+
+
+
+Hyperlinks Demo
+[ File Path: core/url.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ No extrally setting needed for basic functions.
+ If you need flexible control of hyperlinks to jump, please use the 'onClick' callback. See the API documentation for more related contents.
+
+
+ 2, Explanation of treeNode
+
+ 1. the 'url' attribute is used to save the hyperlink's path.
+ 2. the 'target' attribute is used to save the hyperlink's target.
+ 3. the 'click' attribute is used to save the hyperlink's 'onclick' attribute.
+ Please refer to the API documentation "treeNode data details" to view other attributes description.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox.html
new file mode 100644
index 00000000..f2a1408a
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox.html
@@ -0,0 +1,107 @@
+
+
+
+ ZTREE DEMO - checkbox
+
+
+
+
+
+
+
+
+
+
+
+Checkbox Operation
+[ File Path: excheck/checkbox.html ]
+
+
+
+
+ 1, Explanation of setting
+
+
+ 2, Explanation of treeNode
+
+ 1), If you need to initialize the node is checked, please set treeNode.checked attribute. See the API documentation for more related contents.
+ 2), If you need to initialize the node's checkbox is disabled, please set treeNode.chkDisabled attribute. See the API documentation for more related contents and 'chkDisabled Demo'.
+ 3), If you need to initialize the node don't show checkbox, please set treeNode.nocheck attribute. See the API documentation for more related contents and 'nocheck Demo'.
+ 4), If you need to change 'checked' to other attribute, please set setting.data.key.checked attribute. See the API documentation for more related contents.
+ 5), By the way, please see the API documentation for 'treeNode.checkedOld / getCheckStatus / check_Child_State / check_Focus'.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_chkDisabled.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_chkDisabled.html
new file mode 100644
index 00000000..1296e13b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_chkDisabled.html
@@ -0,0 +1,111 @@
+
+
+
+ ZTREE DEMO - Checkbox chkDisabled
+
+
+
+
+
+
+
+
+
+
+
+Checkbox chkDisabled Demo
+[ File Path: excheck/checkbox_chkDisabled.html ]
+
+
+
+
+ 1, Explanation of 'setChkDisabled' method
+
+
+ 2, Explanation of setting
+
+ This Demo only shows how use the ‘chkDisabled’ attribute, detailed configuration information, please refer to 'Checkbox Operation'
+
+
+ 3, Explanation of treeNode
+
+ 1), If you need to initialize the node's checkbox is disabled, please set treeNode.chkDisabled attribute. See the API documentation for more related contents.
+ 2), By the way, please see the 'Checkbox Operation' Demo.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_count.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_count.html
new file mode 100644
index 00000000..ae443905
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_count.html
@@ -0,0 +1,127 @@
+
+
+
+ ZTREE DEMO - getChangeCheckedNodes / getCheckedNodes
+
+
+
+
+
+
+
+
+
+
+
+Statistics Checkbox is Checked
+[ File Path: excheck/checkbox_count.html ]
+
+
+
+
+ 1, Explanation of getChangeCheckedNodes / getCheckedNodes
+
+
+ 2, Explanation of setting
+
+ Same as 'Checkbox Operation'
+
+
+ 3, Explanation of treeNode
+
+ Same as 'Checkbox Operation'
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_fun.html
new file mode 100644
index 00000000..0562fe64
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_fun.html
@@ -0,0 +1,172 @@
+
+
+
+ ZTREE DEMO - beforeCheck / onCheck
+
+
+
+
+
+
+
+
+
+
+
+Checkbox - zTree methods
+[ File Path: excheck/checkbox_fun.html ]
+
+
+
+
+ 1, 'beforeCheck / onCheck' callback function
+
+
+ 2, Explanation of setting
+
+ Same as 'Checkbox Operation'
+
+
+ 3, Explanation of treeNode
+
+ Same as 'Checkbox Operation'
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_halfCheck.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_halfCheck.html
new file mode 100644
index 00000000..368cd5a9
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_halfCheck.html
@@ -0,0 +1,108 @@
+
+
+
+ ZTREE DEMO - Checkbox halfCheck
+
+
+
+
+
+
+
+
+
+
+
+Checkbox halfCheck Demo
+[ File Path: excheck/checkbox_halfCheck.html ]
+
+
+
+
+ 1, Explanation of Demo
+
+ This demo implements the check operation of the dynamic tree
+ 1). After loaded child nodes, the parent node's 'halfCheck' attribute will be invalid immediately.
+ 2). If you check the parent node, so will effect the child nodes's check status.
+ 3). If you check the parent node, it's child node's 'halfCheck' attribute will be invalid.
+
+
+ 2, Explanation of setting
+
+ Half-checked functional don't need to configure any parameter. But it is accessibility, can not used alone. So please configure the parameters which you need.
+
+
+ 3, Explanation of treeNode
+
+ 1), Please set treeNode.halfCheck attribute, before zTree initialize. See the API documentation for more related contents.
+ 2), By the way, please see the 'Checkbox Operation' Demo.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_nocheck.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_nocheck.html
new file mode 100644
index 00000000..8a20b842
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/checkbox_nocheck.html
@@ -0,0 +1,95 @@
+
+
+
+ ZTREE DEMO - no checkbox
+
+
+
+
+
+
+
+
+
+
+
+Checkbox nocheck Demo
+[ File Path: excheck/checkbox_nocheck.html ]
+
+
+
+
+ 1, Explanation of setting
+
+
+ 2, Explanation of treeNode
+
+ 1), If you need to initialize the node don't show checkbox, please set treeNode.nocheck attribute. See the API documentation for more related contents.
+ 2), By the way, please see the 'Checkbox Operation' Demo.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio.html
new file mode 100644
index 00000000..f21c5bca
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio.html
@@ -0,0 +1,97 @@
+
+
+
+ ZTREE DEMO - radio
+
+
+
+
+
+
+
+
+
+
+
+Radio Operation
+[ File Path: excheck/radio.html ]
+
+
+
+
+ 1, Explanation of setting
+
+
+ 2, Explanation of treeNode
+
+ 1), If you need to initialize the node is checked, please set treeNode.checked attribute. See the API documentation for more related contents.
+ 2), If you need to initialize the node don't show radio, please set treeNode.nocheck attribute. See the API documentation for more related contents.
+ 3), If you need to change 'checked' to other attribute, please set setting.data.key.checked attribute. See the API documentation for more related contents.
+ 4), By the way, please see the API documentation for 'treeNode.checkedOld / getCheckStatus / check_Child_State / check_Focus'.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_chkDisabled.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_chkDisabled.html
new file mode 100644
index 00000000..08282d75
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_chkDisabled.html
@@ -0,0 +1,101 @@
+
+
+
+ ZTREE DEMO - Radio chkDisabled
+
+
+
+
+
+
+
+
+
+
+
+Radio chkDisabled Demo
+[ File Path: excheck/radio_chkDisabled.html ]
+
+
+
+
+ 1, Explanation of 'setChkDisabled' method
+
+
+ 2, Explanation of setting
+
+ This Demo only shows how use the ‘nocheck’ attribute, detailed configuration information, please refer to 'Radio Operation'
+
+
+ 3, Explanation of treeNode
+
+ 1), If you need to initialize the node don't show radio, please set treeNode.chkDisabled attribute. See the API documentation for more related contents.
+ 2), By the way, please see the 'Radio Operation' Demo.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_fun.html
new file mode 100644
index 00000000..9a32fc3d
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_fun.html
@@ -0,0 +1,151 @@
+
+
+
+ ZTREE DEMO - beforeCheck / onCheck
+
+
+
+
+
+
+
+
+
+
+
+Radio - zTree methods
+[ File Path: excheck/radio_fun.html ]
+
+
+
+
+ 1, 'beforeCheck / onCheck' callback function
+
+
+ 2, Explanation of setting
+
+ Same as 'Radio Operation'
+
+
+ 3, Explanation of treeNode
+
+ Same as 'Radio Operation'
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_halfCheck.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_halfCheck.html
new file mode 100644
index 00000000..932e8376
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_halfCheck.html
@@ -0,0 +1,93 @@
+
+
+
+ ZTREE DEMO - Radio halfCheck
+
+
+
+
+
+
+
+
+
+
+
+Radio halfCheck Demo
+[ File Path: excheck/radio_halfCheck.html ]
+
+
+
+
+ 1, Explanation of Demo
+
+ This demo implements the check operation of the dynamic tree
+ 1). After loaded child nodes, the parent node's 'halfCheck' attribute will be invalid immediately.
+ 2). If you check the parent node, will not effect the child nodes's check status.
+ 3). If you check the parent node, it's child node's 'halfCheck' attribute will still be valid.
+
+
+ 2, Explanation of setting
+
+ Half-checked functional don't need to configure any parameter. But it is accessibility, can not used alone. So please configure the parameters which you need.
+
+
+ 3, Explanation of treeNode
+
+ 1), Please set treeNode.halfCheck attribute, before zTree initialize. See the API documentation for more related contents.
+ 2), By the way, please see the 'Radio Operation' Demo.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_nocheck.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_nocheck.html
new file mode 100644
index 00000000..e9e3b48b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/excheck/radio_nocheck.html
@@ -0,0 +1,77 @@
+
+
+
+ ZTREE DEMO - no radio
+
+
+
+
+
+
+
+
+
+
+
+Radio nocheck Demo
+[ File Path: excheck/radio_nocheck.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ This Demo only shows how use the ‘nocheck’ attribute, detailed configuration information, please refer to 'Radio Operation'
+ setting.check.nocheckInherit can set the child nodes to automatically inherit the parent node's 'nocheck' attribute. See the API documentation for more related contents.
+
+
+ 2, Explanation of treeNode
+
+ 1), If you need to initialize the node don't show radio, please set treeNode.nocheck attribute. See the API documentation for more related contents.
+ 2), By the way, please see the 'Radio Operation' Demo.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/async_edit.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/async_edit.html
new file mode 100644
index 00000000..26c6db9d
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/async_edit.html
@@ -0,0 +1,120 @@
+
+
+
+ ZTREE DEMO - async & edit
+
+
+
+
+
+
+
+
+
+
+
+
+Editing Dynamic Tree
+[ File Path: exedit/async_edit.html ]
+
+
+
+
+ 1, Explanation of editing dynamic tree
+
+ 1) This Demo is based on the "Advanced Edit Nodes" to modify, and open to drag and drop functionality, can be compared with that demo.
+ 2) At the same time set the editing mode and dynamic mode can be achieved editing dynamic tree.
+ 3) zTree improved editing capabilities in dynamic mode, if the parent node hasn‘t loaded the child nodes, it will first load the child nodes before it add child node.
+
+
+ 2, Explanation of setting
+
+ 1) Use editing features, please refer to "Normal Drag Node Operation" & "Basic Edit Nodes" demo of the instructions.
+ 2) Use dynamic loading, please refer to "Dynamic Tree with Ajax" demo of the instructions.
+
+
+ 3, Explanation of treeNode
+
+ No special requirements on the node data, please refer to "Dynamic Tree with Ajax" & "Normal Drag Node Operation" & "Basic Edit Nodes" demo of the instructions
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag.html
new file mode 100644
index 00000000..30aff420
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag.html
@@ -0,0 +1,136 @@
+
+
+
+ ZTREE DEMO - drag & drop
+
+
+
+
+
+
+
+
+
+
+
+Normal Drag Node Operation
+[ File Path: exedit/drag.html ]
+
+
+
+
+ 1, Explanation of setting
+
+
+ 2, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag_fun.html
new file mode 100644
index 00000000..1215ba28
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag_fun.html
@@ -0,0 +1,180 @@
+
+
+
+ ZTREE DEMO - copyNode / moveNode
+
+
+
+
+
+
+
+
+
+
+
+
+Move / Copy - zTree methods
+[ File Path: exedit/drag_fun.html ]
+
+
+
+
+ 1, Explanation of 'copyNode / moveNode' method
+
+
+ 2, Explanation of setting
+
+ Same as 'Normal Drag Node Operation'
+
+
+ 3, Explanation of treeNode
+
+ Same as 'Normal Drag Node Operation'
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag_super.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag_super.html
new file mode 100644
index 00000000..559f1ffe
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/drag_super.html
@@ -0,0 +1,210 @@
+
+
+
+ ZTREE DEMO - beforeDrag / onDrag / beforeDrop / onDrop
+
+
+
+
+
+
+
+
+
+
+
+Advanced Drag Node Operation
+[ File Path: exedit/drag_super.html ]
+
+
+
+
+ 1, 'beforeDrag / onDrag' 'beforeDrop / onDrop' callback function
+
+
+ 2, Explanation of setting
+
+ Same as 'Normal Drag Node Operation'
+
+
+ 3, Explanation of treeNode
+
+ Same as 'Normal Drag Node Operation'
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit.html
new file mode 100644
index 00000000..fd60f054
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit.html
@@ -0,0 +1,115 @@
+
+
+
+ ZTREE DEMO - edit
+
+
+
+
+
+
+
+
+
+
+
+Basic Edit Nodes
+[ File Path: exedit/edit.html ]
+
+
+
+
+ 1, Explanation of setting
+
+
+ 2, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit_fun.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit_fun.html
new file mode 100644
index 00000000..09c471ea
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit_fun.html
@@ -0,0 +1,194 @@
+
+
+
+ ZTREE DEMO - addNodes / editName / removeNode / removeChildNodes
+
+
+
+
+
+
+
+
+
+
+
+Edit Nodes - zTree methods
+[ File Path: exedit/edit_fun.html ]
+
+
+
+
+ 1, Explanation of 'addNodes / editName / removeNode / removeChildNodes' method
+
+
+ 2, Explanation of setting
+
+ Same as 'Basic Edit Nodes'
+ Lock the parent / leaf node status, need to set setting.data.keep.parent / leaf attribute, see the API documentation for more related contents
+
+
+ 3, Explanation of treeNode
+
+ Same as 'Basic Edit Nodes'
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit_super.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit_super.html
new file mode 100644
index 00000000..c00286db
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/edit_super.html
@@ -0,0 +1,183 @@
+
+
+
+ ZTREE DEMO - beforeEditName / beforeRemove / onRemove / beforeRename / onRename
+
+
+
+
+
+
+
+
+
+
+
+
+Advanced Edit Nodes
+[ File Path: exedit/edit_super.html ]
+
+
+
+
+ 1, 'beforeDrag / onDrag' 'beforeEditName' 'beforeRemove / onRemove' 'beforeRename / onRename' callback function
+
+
+ 2, Explanation of setting
+
+ Same as 'Basic Edit Nodes'
+
+
+ 3, Explanation of treeNode
+
+ Same as 'Basic Edit Nodes'
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/multiTree.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/multiTree.html
new file mode 100644
index 00000000..b078f1b7
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exedit/multiTree.html
@@ -0,0 +1,94 @@
+
+
+
+ ZTREE DEMO - multiTree
+
+
+
+
+
+
+
+
+
+
+
+Multiple Trees
+[ File Path: exedit/multiTree.html ]
+
+
+
+ 1, Explanation of setting
+
+ zTree enables drag and drop nodes between multiple trees, only need to create two draggable tree can be. And you can use the callback function and zTree method achieve a more complex operating rules, here is only the basic demo.
+ On the 'setting' configuration information please refer to drag and drop, edit, etc. demo's instructions
+
+
+ 2, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/checkbox.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/checkbox.html
new file mode 100644
index 00000000..759701ac
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/checkbox.html
@@ -0,0 +1,161 @@
+
+
+
+ ZTREE DEMO - Hide With Checkbox Mode
+
+
+
+
+
+
+
+
+
+
+
+
+Hide With Checkbox Mode
+[ File Path: exhide/checkbox.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ Use the freature which to hide nodes, you don't need to set any attributes.
+
+
+ 2, Explanation of treeNode
+
+
+ 3、Other explanation
+
+ Hide nodes will effect the 'isFirstNode' and 'isLastNode' attributes, but will not effect the 'getPreNode()' and 'getNextNode()' method.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/common.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/common.html
new file mode 100644
index 00000000..9d660a90
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/common.html
@@ -0,0 +1,126 @@
+
+
+
+ ZTREE DEMO - Hide Ordinary Node
+
+
+
+
+
+
+
+
+
+
+
+Hide Ordinary Node
+[ File Path: exhide/common.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ Use this freature, you don't need to set any attributes.
+
+
+ 2, Explanation of treeNode
+
+
+ 3、Other explanation
+
+ Hide nodes will effect the 'isFirstNode' and 'isLastNode' attributes, but will not effect the 'getPreNode()' and 'getNextNode()' method.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/radio.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/radio.html
new file mode 100644
index 00000000..639d69c2
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/exhide/radio.html
@@ -0,0 +1,162 @@
+
+
+
+ ZTREE DEMO - Hide With Radio Mode
+
+
+
+
+
+
+
+
+
+
+
+
+Hide With Radio Mode
+[ File Path: exhide/radio.html ]
+
+
+
+
+ 1, Explanation of setting
+
+ Use the freature which to hide nodes, you don't need to set any attributes.
+
+
+ 2, Explanation of treeNode
+
+
+ 3、Other explanation
+
+ Hide nodes will effect the 'isFirstNode' and 'isLastNode' attributes, but will not effect the 'getPreNode()' and 'getNextNode()' method.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/index.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/index.html
new file mode 100644
index 00000000..1d10fddb
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/index.html
@@ -0,0 +1,157 @@
+
+
+
+ ZTREE DEMO
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/asyncForAll.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/asyncForAll.html
new file mode 100644
index 00000000..eea4a9bc
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/asyncForAll.html
@@ -0,0 +1,192 @@
+
+
+
+ ZTREE DEMO - async for All
+
+
+
+
+
+
+
+
+
+
+Expand All Nodes with Async
+[ File Path: super/asyncForAll.html ]
+
+
+
+
+ Explanation of implementation method
+
+ Using 'onAsyncSuccess' / 'onAsyncError' callback and 'reAsyncChildNodes' or 'expandNode' method, you will achieve all functionalities.
+ Note: If there are large amount parent nodes, please use delay to avoid excessive asynchronous process.
+ Recommendation: please use the debugging tools to view the ajax loading process with network.
+ Demonstrate operation
+
+ [ Expand All Nodes ]
+ [ Load all nodes (don't expand) ]
+ [ Reset zTree ]
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/checkbox_radio.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/checkbox_radio.html
new file mode 100644
index 00000000..87ce15d6
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/checkbox_radio.html
@@ -0,0 +1,116 @@
+
+
+
+ ZTREE DEMO - checkbox & radio
+
+
+
+
+
+
+
+
+
+
+
+Checkbox / Radio Coexistence
+[ File Path: super/checkbox_radio.html ]
+
+
+
+
+ Explanation of implementation method
+
+ zTree default checkbox and radio can not coexist, but can be used the custom DOM features to achieve this requirement, refer to the "Adding Custom DOM".
+ For checkbox / radio association rules of the parent nodes and child nodes, according to the needs to develop its own rules.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/diydom.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/diydom.html
new file mode 100644
index 00000000..07649d2f
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/diydom.html
@@ -0,0 +1,177 @@
+
+
+
+ ZTREE DEMO - addHoverDom / removeHoverDom / addDiyDom
+
+
+
+
+
+
+
+
+
+
+
+
+Adding Custom DOM
+[ File Path: super/diydom.html ]
+
+
+
+
+ 1, Explanation of implementation method
+
+ Use 'setting.view.addHoverDom / removeHoverDom / addDiyDom' attributes can easily display custom DOM.
+ Adding custom DOM, is important to understand zTree node object naming rules to ensure the normal controls add DOM.
+ If you add the browser's default ’select / checkbox / radio‘ DOM, please adjust the css, to ensure correctly display zTree.
+
+
+ 2, Explanation of setting
+
+ Set 'setting.view.addHoverDom / removeHoverDom / addDiyDom' attributes, see the API documentation for more related contents.
+
+
+ 3, Explanation of treeNode
+
+ No special requirements on the node data, the user can add custom attributes.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/dragWithOther.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/dragWithOther.html
new file mode 100644
index 00000000..31426c3c
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/dragWithOther.html
@@ -0,0 +1,234 @@
+
+
+
+ ZTREE DEMO - drag with other DOM
+
+
+
+
+
+
+
+
+
+
+
+
+Drag With Other DOMs
+[ File Path: super/dragWithOther.html ]
+
+
+
+
+ Explanation of implementation method
+
+ zTree v3.2 modified the event object in onDrag/onDrop, thus the nodes can be draged to other DOMs easily.
+ To drag other DOMS to zTree, you need to build the drag codes by yourself.
+ This is just a simple Demo. If you need more features, please build more complicated codes.
+
+
+ Please drag and drop the following content to the tree node
+
+
Tree Grass Flower
+
+
Tiger Bear Lion
+
+ You can drag the nodes of level 2 to the above layer.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menu.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menu.html
new file mode 100644
index 00000000..7dfe588d
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menu.html
@@ -0,0 +1,152 @@
+
+
+
+ ZTREE DEMO - left_menu
+
+
+
+
+
+
+
+
+
+
+
+Left Menu
+[ File Path: super/left_menu.html ]
+
+
+
+
+ Explanation of implementation method
+
+ The menu in the Demo's Web page is to use zTree to achieve, is to use css set interface, use the configuration 'setting' to achieve features, this demo achieve a simple example of the left menu, you can view the source code of Demo's Web Page for in-depth study.
+ 1. According to your needs to modify the css, for example: the menu style on this Demo and Demo's Web page not the same.
+ 2. Main configuration:
+ setting.view.showIcon / showLine / selectedMulti / dblClickExpand
+ setting.callback.onNodeCreated / beforeClick / onClick
+
+ 3. If need to keep single path, please refer to the source code of "Keep Single Path" Demo.
+ 4. Other auxiliary rules, please write code based on your needs.
+
+
+
+
+
+
+
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.gif b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.gif
new file mode 100644
index 00000000..c252f0f8
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.gif differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.html
new file mode 100644
index 00000000..8f7ddff0
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.html
@@ -0,0 +1,136 @@
+
+
+
+ ZTREE DEMO - left_menu for Outlook
+
+
+
+
+
+
+
+
+
+
+Left Menu Like OutLook Style
+[ File Path: super/left_menuForOutLook.html ]
+
+
+
+
+ Explanation of implementation method
+
+ I make one left menu which resembles outlook.com, so share to everyone.
+ 1. Please note that the css code of the source of this page.
+ 2. Please note that the javascript code of the source of this page, especially the switch button is moved into the <a> tag when 'addDiyDom' callback be called
+ 3. Use the method of add zTree class name to implement the switch button be hidden or showing.
+ 4. Other auxiliary rules, please write code based on your needs.
+ 5. The current rule description:
+ Click the root node can control the parent node be expanded or collapsed;
+ The parent nodes without root only are only clicked the arrow icon can control be expanded or collapsed;br/>
+
+
+
+
+
+
+
+
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.png b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.png
new file mode 100644
index 00000000..36e8acfa
Binary files /dev/null and b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/left_menuForOutLook.png differ
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/oneclick.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/oneclick.html
new file mode 100644
index 00000000..9679ec33
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/oneclick.html
@@ -0,0 +1,87 @@
+
+
+
+ ZTREE DEMO - one click
+
+
+
+
+
+
+
+
+
+
+
+Click to Expand Node
+[ File Path: super/oneclick.html ]
+
+
+
+
+ Explanation of implementation method
+
+ zTree not provide a default click to expand node feature, but can use the onClick callback functions to easily implement this feature.
+ In order to avoid conflict with the double-clicking, I recommended to turn off double-clicking expand node function, please set setting.view.dblClickExpand = false
+ You can hide all the +/- switch, please see the page source, see the css.
+ If you do not show + / - switch, then please set 'setting.view.showLine = false' to hide line.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/oneroot.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/oneroot.html
new file mode 100644
index 00000000..9d7eb53b
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/oneroot.html
@@ -0,0 +1,79 @@
+
+
+
+ ZTREE DEMO - one root
+
+
+
+
+
+
+
+
+
+
+
+Freeze the Root Node
+[ File Path: demo/super/oneroot.html ]
+
+
+
+
+ Explanation of implementation method
+
+ For only one root, and do not show +/- switch needs need to modify the css, and set the setting.
+ zTree v3.x can be for a given level, set the style, check out the page source, see the css.
+ Set setting.view.dblClickExpand to Function, you can turn off double-clicking for expand the root node.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/rightClickMenu.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/rightClickMenu.html
new file mode 100644
index 00000000..06e640cc
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/rightClickMenu.html
@@ -0,0 +1,168 @@
+
+
+
+ ZTREE DEMO - select menu
+
+
+
+
+
+
+
+
+
+
+
+
+Right-click Menu
+[ File Path: super/rightClickMenu.html ]
+
+
+
+
+ Explanation of implementation method
+
+ Use 'beforeRightClick / onRightClick' callback function achieve right-click menu.
+ Demo's menu is ugly, you can use a custom menu fine style.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu.html
new file mode 100644
index 00000000..6423f6ec
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu.html
@@ -0,0 +1,124 @@
+
+
+
+ ZTREE DEMO - select menu
+
+
+
+
+
+
+
+
+
+
+
+Drop-down Menu
+[ File Path: super/select_menu.html ]
+
+
+
+
+ Explanation of implementation method
+
+ Achieve this drop-down menu, it is relatively easy, you only need to control zTree Hide / Show, and location.
+ Use zTree v3.x, you can select multiple nodes, so the multi-selection is also easy.
+ Use the configuration parameters setting, fully meet the needs of most of the functionality.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu_checkbox.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu_checkbox.html
new file mode 100644
index 00000000..06b50d29
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu_checkbox.html
@@ -0,0 +1,126 @@
+
+
+
+ ZTREE DEMO - checkbox select menu
+
+
+
+
+
+
+
+
+
+
+
+
+Drop-down Menu with checkbox
+[ File Path: super/select_menu_checkbox.html ]
+
+
+
+
+ Explanation of implementation method
+
+ You can achieve multi-selected drop-down menu with checkbox.
+ This Demo is mainly used for compare with other drop-down menu.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu_radio.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu_radio.html
new file mode 100644
index 00000000..7e9e9d1a
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/select_menu_radio.html
@@ -0,0 +1,127 @@
+
+
+
+ ZTREE DEMO - radio select menu
+
+
+
+
+
+
+
+
+
+
+
+
+Drop-down Menu with radio
+[ File Path: super/select_menu_radio.html ]
+
+
+
+
+ Explanation of implementation method
+
+ You can achieve single-selected drop-down menu with radio.
+ This Demo is mainly used for compare with other drop-down menu.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/singlepath.html b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/singlepath.html
new file mode 100644
index 00000000..f48499d9
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/demo/en/super/singlepath.html
@@ -0,0 +1,150 @@
+
+
+
+ ZTREE DEMO - single path
+
+
+
+
+
+
+
+
+
+
+
+Keep Single Path
+[ File Path: super/singlepath.html ]
+
+
+
+
+ Explanation of implementation method
+
+ This Demo is the transformation from "Click to Expand Node" demo, tree only expand single path.
+ Use 'setting.callback.beforeExpand / onExpand' callback function to achieve rules about expand
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/js/jquery-1.4.4.min.js b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/js/jquery-1.4.4.min.js
new file mode 100644
index 00000000..8f3ca2e2
--- /dev/null
+++ b/shiro-example-chapter23-server/src/main/webapp/WEB-INF/static/JQuery zTree v3.5.15/js/jquery-1.4.4.min.js
@@ -0,0 +1,167 @@
+/*!
+ * jQuery JavaScript Library v1.4.4
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Nov 11 19:04:53 2010 -0500
+ */
+(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
+h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;kd)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
+"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
+e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
+"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
+s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
+j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
+toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
+-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
+if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
+b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
+!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
+l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;Ha ";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
+k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
+scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML=" ";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
+1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
+"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
+c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
+else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";
+if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},
+attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&
+b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0};
+c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,
+arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid=
+d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+
+c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
+8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k===
+"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+
+d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired=
+B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type===
+"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]===
+0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
+(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3];
+break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr,
+q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h=
+l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
+m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
+true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
+g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]-
+0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
+i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]];
+if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m,
+g);else if(typeof g.length==="number")for(var p=g.length;n
";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g);
+n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML=" ";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&&
+function(){var g=k,i=t.createElement("div");i.innerHTML="
";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F||
+p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g=
+t.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition?
+function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h=
+h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):
+c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,
+2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,
+b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&
+e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/\s]+\/)>/g,P={option:[1,
+""," "],legend:[1,""," "],thead:[1,""],tr:[2,""],td:[3,""],col:[2,""],area:[1,""," "],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null;
+else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>$2>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",
+prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument||
+b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>$2>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length-
+1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
+d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
+jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
+zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
+h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
+if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
+d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
+e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/
+
+
+
+