迁移samples代码
diff --git a/leaveschool/client/pom.xml b/leaveschool/client/pom.xml
new file mode 100644
index 0000000..589e5af
--- /dev/null
+++ b/leaveschool/client/pom.xml
@@ -0,0 +1,150 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.leaveschool</groupId>
+    <artifactId>leaveschool-parent</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.supwisdom.leaveschool</groupId>
+  <artifactId>leaveschool-client</artifactId>
+  <packaging>jar</packaging>
+
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <!-- 微服务 健康监控 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-mvc</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-object-mapper</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-i18n</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-lang</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-pinyin</artifactId>
+    </dependency>
+     -->
+
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-security</artifactId>
+    </dependency>
+
+    <!-- <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-cas</artifactId>
+    </dependency> -->
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-thymeleaf</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.thymeleaf.extras</groupId>
+      <artifactId>thymeleaf-extras-springsecurity4</artifactId>
+    </dependency>
+
+
+
+    <!-- <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+    </dependency> -->
+  
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-openfeign</artifactId>
+    </dependency>
+  
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.json</groupId>
+      <artifactId>json</artifactId>
+    </dependency>
+
+
+    <!-- Test things -->
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/ClientApplication.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/ClientApplication.java
new file mode 100644
index 0000000..4d83c0d
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/ClientApplication.java
@@ -0,0 +1,38 @@
+package com.supwisdom.leaveschool.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
+import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
+
+import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
+import com.supwisdom.infras.security.EnableInfrasWebSecurity;
+
+@SpringBootApplication
+@EnableCircuitBreaker
+@EnableFeignClients(value = "com.supwisdom.leaveschool.client.service")
+@EnableHystrixDashboard
+@EnableInfrasWebSecurity
+public class ClientApplication {
+
+  public static void main(String[] args) {
+    SpringApplication.run(ClientApplication.class, args);
+  }
+  
+  
+  @Bean
+  public ServletRegistrationBean<HystrixMetricsStreamServlet> hystrixMetricsStreamServlet() {
+    HystrixMetricsStreamServlet hystrixMetricsStreamServlet = new HystrixMetricsStreamServlet();
+    
+    ServletRegistrationBean<HystrixMetricsStreamServlet> servletRegistrationBean = new ServletRegistrationBean<HystrixMetricsStreamServlet>(hystrixMetricsStreamServlet);
+    servletRegistrationBean.setName("hystrixMetricsStreamServlet");
+    servletRegistrationBean.setLoadOnStartup(1);
+    servletRegistrationBean.addUrlMappings("/hystrix.stream");
+    
+    return servletRegistrationBean;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/Test.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/Test.java
new file mode 100644
index 0000000..6915296
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/Test.java
@@ -0,0 +1,9 @@
+package com.supwisdom.leaveschool.client;
+
+public class Test {
+
+  public static void main(String[] args) {
+    
+    
+  }
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/FilterConfig.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/FilterConfig.java
new file mode 100644
index 0000000..4ee7f3c
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/FilterConfig.java
@@ -0,0 +1,14 @@
+package com.supwisdom.leaveschool.client.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+public class FilterConfig extends WebMvcConfigurerAdapter{
+
+	 @Override
+	    public void addCorsMappings(CorsRegistry registry) {
+	        registry.addMapping("/**");
+	    }
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/InfrasFilterSecurityInterceptorConfig.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/InfrasFilterSecurityInterceptorConfig.java
new file mode 100644
index 0000000..7f8ed14
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/InfrasFilterSecurityInterceptorConfig.java
@@ -0,0 +1,44 @@
+package com.supwisdom.leaveschool.client.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+
+import com.supwisdom.infras.security.web.access.intercept.InfrasFilterSecurityInterceptor;
+import com.supwisdom.leaveschool.client.security.web.access.MyAccessDecisionManager;
+import com.supwisdom.leaveschool.client.security.web.access.intercept.MyFilterInvocationSecurityMetadataSource;
+import com.supwisdom.leaveschool.client.security.web.access.intercept.MyFilterSecurityInterceptor;
+
+@Configuration
+public class InfrasFilterSecurityInterceptorConfig {
+
+  private static final Logger logger = LoggerFactory.getLogger(InfrasFilterSecurityInterceptorConfig.class);
+  
+  @Bean
+  public FilterInvocationSecurityMetadataSource securityMetadataSource() {
+    MyFilterInvocationSecurityMetadataSource securityMetadataSource = new MyFilterInvocationSecurityMetadataSource();
+    logger.debug("InfrasFilterSecurityInterceptorConfig securityMetadataSource is {}", securityMetadataSource);
+
+    return securityMetadataSource;
+  }
+  
+  @Bean
+  public AccessDecisionManager accessDecisionManager() {
+    MyAccessDecisionManager accessDecisionManager = new MyAccessDecisionManager();
+    logger.debug("InfrasFilterSecurityInterceptorConfig accessDecisionManager is {}", accessDecisionManager);
+
+    return accessDecisionManager;
+  }
+  
+  @Bean
+  public InfrasFilterSecurityInterceptor infrasFilterSecurityInterceptor() throws Exception {
+    MyFilterSecurityInterceptor myFilterSecurityInterceptor = new MyFilterSecurityInterceptor();
+    logger.debug("InfrasFilterSecurityInterceptorConfig infrasFilterSecurityInterceptor is {}", myFilterSecurityInterceptor);
+
+    return myFilterSecurityInterceptor;
+  }
+  
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/PasswordEncoderConfig.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/PasswordEncoderConfig.java
new file mode 100644
index 0000000..4957c3c
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/PasswordEncoderConfig.java
@@ -0,0 +1,31 @@
+package com.supwisdom.leaveschool.client.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@SuppressWarnings("deprecation")
+@Configuration
+public class PasswordEncoderConfig {
+  
+  private static final Logger logger = LoggerFactory.getLogger(PasswordEncoderConfig.class);
+  
+  @Bean
+  public PasswordEncoder passwordEncoder() {
+    
+    PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+
+    if (passwordEncoder instanceof DelegatingPasswordEncoder) {
+      ((DelegatingPasswordEncoder)passwordEncoder).setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance());
+    }
+
+    logger.debug("PasswordEncoderConfig passwordEncoder is {}", passwordEncoder);
+    return passwordEncoder;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/UserDetailsServiceConfig.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/UserDetailsServiceConfig.java
new file mode 100644
index 0000000..fa91e80
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/config/UserDetailsServiceConfig.java
@@ -0,0 +1,33 @@
+package com.supwisdom.leaveschool.client.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+import com.supwisdom.leaveschool.client.security.core.userdetails.InMemeryUserDetailsService;
+import com.supwisdom.leaveschool.client.security.core.userdetails.MyUserDetailsService;
+
+@Configuration
+public class UserDetailsServiceConfig {
+
+  private static final Logger logger = LoggerFactory.getLogger(UserDetailsServiceConfig.class);
+  
+//  @Bean
+//  public UserDetailsService userDetailsService() throws Exception {
+//    MyUserDetailsService myUserDetailsService = new MyUserDetailsService();
+//    logger.debug("UserDetailsServiceConfig myUserDetailsService is {}", myUserDetailsService);
+//
+//    return myUserDetailsService;
+//  }
+
+  @Bean
+  public UserDetailsService userDetailsService() throws Exception {
+    InMemeryUserDetailsService inMemeryUserDetailsService = new InMemeryUserDetailsService();
+    logger.debug("UserDetailsServiceConfig inMemeryUserDetailsService is {}", inMemeryUserDetailsService);
+
+    return inMemeryUserDetailsService;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/api/HelloController.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/api/HelloController.java
new file mode 100644
index 0000000..3c0857d
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/api/HelloController.java
@@ -0,0 +1,32 @@
+package com.supwisdom.leaveschool.client.controller.api;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.leaveschool.client.security.core.userdetails.MyUser;
+import com.supwisdom.leaveschool.client.service.SampleUser1AdminUserRemoteService;
+import com.supwisdom.leaveschool.client.util.MyUserUtil;
+import com.supwisdom.leaveschool.proxy.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.proxy.user.domain.User;
+
+@RestController
+@RequestMapping("/api/hello")
+public class HelloController {
+	
+	@Autowired
+	SampleUser1AdminUserRemoteService sampleUser1AdminUserRemoteService;
+	
+	@GetMapping(path = "/{name}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+	public PagerResponseModel hello(@PathVariable("name") String name) {
+		MyUser user = MyUserUtil.getCurrentMyUser();
+		PagerResponseModel<User> users = sampleUser1AdminUserRemoteService.list();
+		
+		return users;
+	}
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/web/MainController.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/web/MainController.java
new file mode 100644
index 0000000..217f944
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/web/MainController.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2002-2016 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.supwisdom.leaveschool.client.controller.web;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@Controller
+public class MainController {
+
+	@RequestMapping("/")
+	public String root() {
+		return "redirect:/web/index";
+	}
+
+	@RequestMapping("/web/index")
+	public String index() {
+		return "web/index";
+	}
+
+	@RequestMapping(value = "/web/login")
+	public String login() {
+		return "web/login";
+	}
+
+	@RequestMapping(value = "/web/login", method = RequestMethod.POST)
+	public String postLogin() {
+		// TODO Enable form login with Spring Security (trigger error for now)
+		return "redirect:/web/login-error";
+	}
+
+	@RequestMapping("/web/login-error")
+	public String loginError(Model model) {
+		model.addAttribute("loginError", true);
+		return "web/login";
+	}
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/web/admin/WebAdminUserController.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/web/admin/WebAdminUserController.java
new file mode 100644
index 0000000..8acd62d
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/controller/web/admin/WebAdminUserController.java
@@ -0,0 +1,45 @@
+package com.supwisdom.leaveschool.client.controller.web.admin;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import com.supwisdom.leaveschool.client.service.SampleUser1AdminUserRemoteService;
+import com.supwisdom.leaveschool.client.util.AuthenticationUtil;
+import com.supwisdom.leaveschool.proxy.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.proxy.user.domain.User;
+
+@Controller
+@RequestMapping("/web/admin/user")
+public class WebAdminUserController {
+  
+  private static final Logger logger = LoggerFactory.getLogger(WebAdminUserController.class);
+
+  @Autowired
+  SampleUser1AdminUserRemoteService sampleUser1AdminUserRemoteService;
+
+  @RequestMapping("/index")
+  public String userIndex() {
+    
+    logger.debug(AuthenticationUtil.currentUsername());
+    
+    PagerResponseModel<User> users = sampleUser1AdminUserRemoteService.list();
+    
+    logger.debug("list is {}, {}", users==null?"":users.getClass().getName(), users);
+    
+    /*
+    Map<String, Object> map = userRemoteService.greeting("abcd");
+    if (map != null) {
+      logger.debug("message is " + map.get("message"));
+    } else {
+      logger.debug("message is null");
+    }
+    */
+    
+    return "web/admin/user/index";
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/InMemeryUserDetailsService.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/InMemeryUserDetailsService.java
new file mode 100644
index 0000000..7f3cf63
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/InMemeryUserDetailsService.java
@@ -0,0 +1,39 @@
+package com.supwisdom.leaveschool.client.security.core.userdetails;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+public class InMemeryUserDetailsService implements UserDetailsService {
+  
+  private static final Logger logger = LoggerFactory.getLogger(InMemeryUserDetailsService.class);
+  
+  @Autowired
+  PasswordEncoder passwordEncoder;
+  
+  @Override
+  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+    
+    logger.debug("InMemeryUserDetailsService.loadUserByUsername({})", username);
+    
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
+    authorities.add(new SimpleGrantedAuthority("administrator"));
+    authorities.add(new SimpleGrantedAuthority("user"));
+    
+    MyUser myUser = new MyUser("admin", passwordEncoder.encode("111111"), authorities);
+    logger.debug("myUser is {}", myUser);
+    
+    return myUser;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/MyUser.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/MyUser.java
new file mode 100644
index 0000000..dc3a27b
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/MyUser.java
@@ -0,0 +1,30 @@
+package com.supwisdom.leaveschool.client.security.core.userdetails;
+
+import java.util.Collection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+
+public class MyUser extends User {
+  
+  //private static final Log logger = LogFactory.getLog(MyUser.class);
+  private static final Logger logger = LoggerFactory.getLogger(MyUser.class);
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5438858716934315373L;
+  
+  public MyUser(String username, String password,
+      Collection<? extends GrantedAuthority> authorities) {
+    this(username, password, true, true, true, true, authorities);
+  }
+
+  public MyUser(String username, String password, boolean enabled, boolean accountNonExpired,
+      boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
+    super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/MyUserDetailsService.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/MyUserDetailsService.java
new file mode 100644
index 0000000..be7c59f
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/core/userdetails/MyUserDetailsService.java
@@ -0,0 +1,52 @@
+package com.supwisdom.leaveschool.client.security.core.userdetails;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import com.supwisdom.leaveschool.client.service.SampleUser1SecurityUserRemoteService;
+import com.supwisdom.leaveschool.proxy.user.domain.Role;
+import com.supwisdom.leaveschool.proxy.user.model.SecurityUser;
+
+public class MyUserDetailsService implements UserDetailsService {
+  
+  private static final Logger logger = LoggerFactory.getLogger(MyUserDetailsService.class);
+  
+  @Autowired
+  PasswordEncoder passwordEncoder;
+  
+  @Autowired
+  SampleUser1SecurityUserRemoteService sampleUser1SecurityUserRemoteService;
+  
+  @Override
+  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+    
+    logger.debug("MyUserDetailsService.loadUserByUsername({})", username);
+    
+    SecurityUser securityUser = sampleUser1SecurityUserRemoteService.loadUserByUsername(username);
+    if (securityUser == null) {
+      throw new UsernameNotFoundException(String.format("%s not found", username));
+    }
+    
+    // TODO: 从数据库获取
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    for (Role role : securityUser.getRoles()) {
+      authorities.add(new SimpleGrantedAuthority(role.getCode()));
+    }
+    
+    MyUser myUser = new MyUser(securityUser.getUser().getUsername(), securityUser.getUser().getPassword(), authorities);
+    logger.debug("myUser is {}", myUser);
+    
+    return myUser;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/MyAccessDecisionManager.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/MyAccessDecisionManager.java
new file mode 100644
index 0000000..003410a
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/MyAccessDecisionManager.java
@@ -0,0 +1,48 @@
+package com.supwisdom.leaveschool.client.security.web.access;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+
+public class MyAccessDecisionManager implements AccessDecisionManager {
+
+  @Override
+  public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
+      throws AccessDeniedException, InsufficientAuthenticationException {
+
+    if (null == configAttributes || configAttributes.size() <= 0) {
+      return;
+    }
+    
+    ConfigAttribute ca;
+    String needRole;
+    for (Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext();) {
+      ca = iter.next();
+      needRole = ca.getAttribute();
+      for (GrantedAuthority ga : authentication.getAuthorities()) { // authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
+        if (needRole.trim().equals(ga.getAuthority())) {
+          return;
+        }
+      }
+    }
+    
+    throw new AccessDeniedException("no right");
+  }
+
+  @Override
+  public boolean supports(ConfigAttribute attribute) {
+    return true;
+  }
+
+  @Override
+  public boolean supports(Class<?> clazz) {
+    return true;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/intercept/MyFilterInvocationSecurityMetadataSource.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/intercept/MyFilterInvocationSecurityMetadataSource.java
new file mode 100644
index 0000000..596f8a5
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/intercept/MyFilterInvocationSecurityMetadataSource.java
@@ -0,0 +1,92 @@
+package com.supwisdom.leaveschool.client.security.web.access.intercept;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+
+import com.supwisdom.leaveschool.client.service.SampleUser1SecurityUserRemoteService;
+
+public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
+  
+  @Autowired
+  SampleUser1SecurityUserRemoteService sampleUser1SecurityUserRemoteService;
+  
+  private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = null;
+  //private Map<String, Collection<ConfigAttribute>> permissionRoles;
+  
+  private void loadRequestMap() {
+    if (requestMap == null) {
+      requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
+      
+      //sampleUser1SecurityUserRemoteService.loadPermissionsByAppcode()
+
+      AntPathRequestMatcher requestMatcher0 = new AntPathRequestMatcher("/api/**");
+      Collection<ConfigAttribute> attributes0 = new ArrayList<ConfigAttribute>();  // FIXME: 返回当前请求的url 对应的 角色代码
+      attributes0.add(new SecurityConfig("user"));
+      requestMap.put(requestMatcher0, attributes0);
+
+      
+      AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/web/**");
+      
+      Collection<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();  // FIXME: 返回当前请求的url 对应的 角色代码
+      attributes.add(new SecurityConfig("user"));
+      
+      requestMap.put(requestMatcher, attributes);
+    }
+  }
+
+  /**
+   * 获取当前请求关联的所有角色code {@link SecurityConfig} 
+   * 用于和用户拥有的角色code 进行比对
+   */
+  @Override
+  public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
+    
+    if (requestMap == null) {
+      loadRequestMap();
+    }
+
+    HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
+    
+    RequestMatcher requestMatcher;
+    for(Iterator<RequestMatcher> iter = requestMap.keySet().iterator(); iter.hasNext(); ) {
+      requestMatcher = iter.next();
+      
+      if(requestMatcher.matches(request)) {
+        return requestMap.get(requestMatcher);
+      }
+    }
+    
+    return null;
+
+//    Collection<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();  // FIXME: 返回当前请求的url 对应的 角色代码
+//    attributes.add(new SecurityConfig("administrator"));
+//
+//    return attributes;
+  }
+
+  @Override
+  public Collection<ConfigAttribute> getAllConfigAttributes() {
+
+    return null;
+  }
+
+  @Override
+  public boolean supports(Class<?> clazz) {
+
+    return true;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/intercept/MyFilterSecurityInterceptor.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/intercept/MyFilterSecurityInterceptor.java
new file mode 100644
index 0000000..32e0232
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/security/web/access/intercept/MyFilterSecurityInterceptor.java
@@ -0,0 +1,80 @@
+package com.supwisdom.leaveschool.client.security.web.access.intercept;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.access.SecurityMetadataSource;
+import org.springframework.security.access.intercept.InterceptorStatusToken;
+//import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+
+import com.supwisdom.infras.security.web.access.intercept.InfrasFilterSecurityInterceptor;
+
+public class MyFilterSecurityInterceptor extends InfrasFilterSecurityInterceptor {
+  
+  private static final Logger logger = LoggerFactory.getLogger(MyFilterSecurityInterceptor.class);
+
+  @Autowired
+  private FilterInvocationSecurityMetadataSource securityMetadataSource;
+
+  @Autowired
+  public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
+
+    super.setAccessDecisionManager(accessDecisionManager);
+  }
+
+//  @Autowired
+//  public void setAuthenticationManager(AuthenticationManager newManager) {
+//    
+//    super.setAuthenticationManager(newManager);
+//  };
+
+  @Override
+  public void invoke(FilterInvocation fi) throws IOException, ServletException {
+    
+    Set<String> noneSecurityUrl = new HashSet<String>();  // FIXME: 对无须访问控制的url,支持可配置
+    noneSecurityUrl.add("/web/login");
+    noneSecurityUrl.add("/web/logout");
+    noneSecurityUrl.add("/web/index");
+
+    if (fi.getRequest() != null) {
+      String requestUrl = fi.getRequestUrl(); logger.debug("MyFilterSecurityInterceptor invoke requestUrl: {}", requestUrl);
+      if (noneSecurityUrl.contains(requestUrl)) {
+        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+        return;
+      }
+    }
+
+    // fi里面有一个被拦截的url
+    // 里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
+    // 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
+    InterceptorStatusToken token = super.beforeInvocation(fi);
+    try {
+      // 执行下一个拦截器
+      fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+    } finally {
+      super.afterInvocation(token, null);
+    }
+  }
+
+  @Override
+  public Class<?> getSecureObjectClass() {
+
+    return FilterInvocation.class;
+  }
+
+  @Override
+  public SecurityMetadataSource obtainSecurityMetadataSource() {
+
+    return this.securityMetadataSource;
+  }
+  
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/DemoUserRemoteService.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/DemoUserRemoteService.java
new file mode 100644
index 0000000..2fa6624
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/DemoUserRemoteService.java
@@ -0,0 +1,23 @@
+package com.supwisdom.leaveschool.client.service;
+
+import java.util.Map;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.supwisdom.leaveschool.client.service.fallback.DemoUserRemoteHystrix;
+
+@FeignClient(
+    name = "sample-user", 
+    url = "${sample-user.api.url}/api/demo/users", 
+    fallback = DemoUserRemoteHystrix.class
+)
+public interface DemoUserRemoteService {
+
+  @RequestMapping(method = RequestMethod.GET, value = "/greeting/{name}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  Map<String, Object> greeting(@PathVariable("name") String name);
+  
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminGroupRemoteService.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminGroupRemoteService.java
new file mode 100644
index 0000000..bd98ff5
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminGroupRemoteService.java
@@ -0,0 +1,38 @@
+package com.supwisdom.leaveschool.client.service;
+
+import java.util.Map;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.supwisdom.leaveschool.client.service.fallback.SampleUser1AdminGroupRemoteHystrix;
+import com.supwisdom.leaveschool.proxy.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.proxy.user.domain.Group;
+
+@FeignClient(
+    name = "sample-user-1-admin-group", 
+    url = "${sample-user.api.url}/api/v1/admin/groups", 
+    fallback = SampleUser1AdminGroupRemoteHystrix.class
+)
+public interface SampleUser1AdminGroupRemoteService {
+
+  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public PagerResponseModel<Group> list();
+
+  @RequestMapping(path = "/{id}", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Group get(@PathVariable("id") String id);
+
+  @RequestMapping(method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> create(@RequestBody Group user);
+  
+  @RequestMapping(method = RequestMethod.PUT, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> update(@RequestBody Group user);
+  
+  @RequestMapping(path = "/{id}", method = RequestMethod.DELETE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> delete(@PathVariable("id") String id);
+  
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminRoleRemoteService.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminRoleRemoteService.java
new file mode 100644
index 0000000..669675a
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminRoleRemoteService.java
@@ -0,0 +1,38 @@
+package com.supwisdom.leaveschool.client.service;
+
+import java.util.Map;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.supwisdom.leaveschool.client.service.fallback.SampleUser1AdminRoleRemoteHystrix;
+import com.supwisdom.leaveschool.proxy.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.proxy.user.domain.Role;
+
+@FeignClient(
+    name = "sample-user-1-admin-role", 
+    url = "${sample-user.api.url}/api/v1/admin/roles", 
+    fallback = SampleUser1AdminRoleRemoteHystrix.class
+)
+public interface SampleUser1AdminRoleRemoteService {
+  
+  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public PagerResponseModel<Role> list();
+
+  @RequestMapping(path = "/{id}", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Role get(@PathVariable("id") String id);
+
+  @RequestMapping(method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> create(@RequestBody Role user);
+  
+  @RequestMapping(method = RequestMethod.PUT, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> update(@RequestBody Role user);
+  
+  @RequestMapping(path = "/{id}", method = RequestMethod.DELETE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> delete(@PathVariable("id") String id);
+  
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminUserRemoteService.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminUserRemoteService.java
new file mode 100644
index 0000000..74012c0
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1AdminUserRemoteService.java
@@ -0,0 +1,38 @@
+package com.supwisdom.leaveschool.client.service;
+
+import java.util.Map;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import com.supwisdom.leaveschool.client.service.fallback.SampleUser1AdminUserRemoteHystrix;
+import com.supwisdom.leaveschool.proxy.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.proxy.user.domain.User;
+
+@FeignClient(
+    name = "sample-user-1-admin-user", 
+    url = "${sample-user.api.url}/api/v1/admin/users", 
+    fallback = SampleUser1AdminUserRemoteHystrix.class
+)
+public interface SampleUser1AdminUserRemoteService {
+  
+  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public PagerResponseModel<User> list();
+
+  @RequestMapping(path = "/{id}", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public User get(@PathVariable("id") String id);
+
+  @RequestMapping(method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> create(@RequestBody User user);
+  
+  @RequestMapping(method = RequestMethod.PUT, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> update(@RequestBody User user);
+  
+  @RequestMapping(path = "/{id}", method = RequestMethod.DELETE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> delete(@PathVariable("id") String id);
+  
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1SecurityUserRemoteService.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1SecurityUserRemoteService.java
new file mode 100644
index 0000000..9a55516
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/SampleUser1SecurityUserRemoteService.java
@@ -0,0 +1,36 @@
+package com.supwisdom.leaveschool.client.service;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.util.MimeTypeUtils;
+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 com.supwisdom.leaveschool.client.service.fallback.SampleUser1SecurityUserRemoteHystrix;
+import com.supwisdom.leaveschool.proxy.user.model.SecurityUser;
+
+@FeignClient(
+    name = "sample-user-1-security-user", 
+    url = "${sample-user.api.url}/api/v1/security/users", 
+    fallback = SampleUser1SecurityUserRemoteHystrix.class
+)
+public interface SampleUser1SecurityUserRemoteService {
+
+  @RequestMapping(path = "/{username}", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public SecurityUser loadUserByUsername(@PathVariable("username") String username);
+
+  /**
+   * 
+   * @param username
+   * @param applicationCode
+   * @param type 
+   * @return
+   */
+  @RequestMapping(path = "/{username}/{applicationCode}", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public SecurityUser loadPermissionsByUsernameAppcode(
+      @PathVariable("username") String username, 
+      @PathVariable("applicationCode") String applicationCode, 
+      @RequestParam(value = "type", required = false) String type);
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/DemoUserRemoteHystrix.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/DemoUserRemoteHystrix.java
new file mode 100644
index 0000000..00dd73d
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/DemoUserRemoteHystrix.java
@@ -0,0 +1,22 @@
+package com.supwisdom.leaveschool.client.service.fallback;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.supwisdom.leaveschool.client.service.DemoUserRemoteService;
+
+@Component
+public class DemoUserRemoteHystrix implements DemoUserRemoteService {
+  
+  private static final Logger logger = LoggerFactory.getLogger(DemoUserRemoteHystrix.class);
+
+  @Override
+  public Map<String, Object> greeting(String name) {
+    logger.debug("greeting failure!");
+    return null;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminGroupRemoteHystrix.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminGroupRemoteHystrix.java
new file mode 100644
index 0000000..dd985e0
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminGroupRemoteHystrix.java
@@ -0,0 +1,43 @@
+package com.supwisdom.leaveschool.client.service.fallback;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.supwisdom.leaveschool.client.service.SampleUser1AdminGroupRemoteService;
+import com.supwisdom.leaveschool.proxy.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.proxy.user.domain.Group;
+
+@Component
+public class SampleUser1AdminGroupRemoteHystrix implements SampleUser1AdminGroupRemoteService {
+  
+  private static final Logger logger = LoggerFactory.getLogger(SampleUser1AdminGroupRemoteHystrix.class);
+
+  @Override
+  public PagerResponseModel<Group> list() {
+    return null;
+  }
+
+  @Override
+  public Group get(String id) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> create(Group user) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> update(Group user) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> delete(String id) {
+    return null;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminRoleRemoteHystrix.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminRoleRemoteHystrix.java
new file mode 100644
index 0000000..dcb8cc5
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminRoleRemoteHystrix.java
@@ -0,0 +1,43 @@
+package com.supwisdom.leaveschool.client.service.fallback;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.supwisdom.leaveschool.client.service.SampleUser1AdminRoleRemoteService;
+import com.supwisdom.leaveschool.proxy.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.proxy.user.domain.Role;
+
+@Component
+public class SampleUser1AdminRoleRemoteHystrix implements SampleUser1AdminRoleRemoteService {
+  
+  private static final Logger logger = LoggerFactory.getLogger(SampleUser1AdminRoleRemoteHystrix.class);
+
+  @Override
+  public PagerResponseModel<Role> list() {
+    return null;
+  }
+
+  @Override
+  public Role get(String id) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> create(Role user) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> update(Role user) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> delete(String id) {
+    return null;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminUserRemoteHystrix.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminUserRemoteHystrix.java
new file mode 100644
index 0000000..0f4f9a8
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1AdminUserRemoteHystrix.java
@@ -0,0 +1,43 @@
+package com.supwisdom.leaveschool.client.service.fallback;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.supwisdom.leaveschool.client.service.SampleUser1AdminUserRemoteService;
+import com.supwisdom.leaveschool.proxy.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.proxy.user.domain.User;
+
+@Component
+public class SampleUser1AdminUserRemoteHystrix implements SampleUser1AdminUserRemoteService {
+  
+  private static final Logger logger = LoggerFactory.getLogger(SampleUser1AdminUserRemoteHystrix.class);
+
+  @Override
+  public PagerResponseModel<User> list() {
+    return null;
+  }
+
+  @Override
+  public User get(String id) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> create(User user) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> update(User user) {
+    return null;
+  }
+
+  @Override
+  public Map<String, Object> delete(String id) {
+    return null;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1SecurityUserRemoteHystrix.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1SecurityUserRemoteHystrix.java
new file mode 100644
index 0000000..a2cbaaf
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/service/fallback/SampleUser1SecurityUserRemoteHystrix.java
@@ -0,0 +1,21 @@
+package com.supwisdom.leaveschool.client.service.fallback;
+
+import org.springframework.stereotype.Component;
+
+import com.supwisdom.leaveschool.client.service.SampleUser1SecurityUserRemoteService;
+import com.supwisdom.leaveschool.proxy.user.model.SecurityUser;
+
+@Component
+public class SampleUser1SecurityUserRemoteHystrix implements SampleUser1SecurityUserRemoteService {
+
+  @Override
+  public SecurityUser loadUserByUsername(String username) {
+    return null;
+  }
+
+  @Override
+  public SecurityUser loadPermissionsByUsernameAppcode(String username, String applicationCode, String type) {
+    return null;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/util/AuthenticationUtil.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/util/AuthenticationUtil.java
new file mode 100644
index 0000000..438477b
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/util/AuthenticationUtil.java
@@ -0,0 +1,44 @@
+package com.supwisdom.leaveschool.client.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.supwisdom.leaveschool.client.security.core.userdetails.MyUser;
+
+public class AuthenticationUtil {
+
+  private static final Logger logger = LoggerFactory.getLogger(AuthenticationUtil.class);
+
+  public static String currentUsername() {
+
+    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+    if (authentication == null) {
+      logger.error("authentication is null");
+      return null;
+    }
+
+    logger.debug("authentication is {}", authentication.getPrincipal());
+
+    if (!authentication.isAuthenticated()) {
+      logger.error("authentication is not authenticated");
+      return null;
+    }
+
+    if (authentication.getPrincipal() == null) {
+      logger.error("authentication's principal is null");
+      return null;
+    }
+
+    logger.debug("authentication's principal is {}", authentication.getPrincipal());
+
+    if (authentication.getPrincipal() instanceof MyUser) {
+      return ((MyUser) authentication.getPrincipal()).getUsername();
+    }
+
+    return null;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/util/MyUserUtil.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/util/MyUserUtil.java
new file mode 100644
index 0000000..3cd0a58
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/client/util/MyUserUtil.java
@@ -0,0 +1,49 @@
+package com.supwisdom.leaveschool.client.util;
+
+
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.supwisdom.leaveschool.client.security.core.userdetails.MyUser;
+
+/**
+ * @author Sheng.Wang
+ *
+ */
+public class MyUserUtil {
+
+	public static MyUser getCurrentMyUser(){
+		Authentication au = SecurityContextHolder.getContext().getAuthentication();
+		if (au == null) return null;
+		if (au.getPrincipal() == null) return null;
+		if (au != null && au.getPrincipal() instanceof MyUser){
+			return (MyUser)au.getPrincipal();
+		}else {
+			MyUser MyUser = new MyUser(au.getPrincipal().toString(),au.getCredentials() == null?null:au.getCredentials().toString(),null);
+			return MyUser;
+		}
+	}
+	public static String getCurrentMyUserName(){
+		MyUser MyUser = getCurrentMyUser();
+		if (MyUser == null) return "";
+		return MyUser.getUsername();
+	}
+	public static boolean isLogged(){
+		Authentication au = SecurityContextHolder.getContext().getAuthentication();
+		if (au == null) return false;
+		if (au.getPrincipal() == null) return false;
+		if (au.getPrincipal() instanceof MyUser) return true;
+		return false;
+	}
+	
+	public static String getMyUserTpye(){
+		Authentication au = SecurityContextHolder.getContext().getAuthentication();
+		if (au == null) return "";
+		if (au.getPrincipal() == null) return "";
+		if (au.getPrincipal() instanceof MyUser) return ((MyUser)au.getPrincipal()).getClass().getSimpleName();
+		return "";
+	}
+	
+	
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/domain/ABaseDomain.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/domain/ABaseDomain.java
new file mode 100644
index 0000000..166cc6b
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/domain/ABaseDomain.java
@@ -0,0 +1,109 @@
+package com.supwisdom.leaveschool.proxy.common.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public abstract class ABaseDomain implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4987240608338421556L;
+
+  private String id;
+
+  /**
+   * 获取主键
+   */
+  public String getId() {
+    return id;
+  }
+
+  /**
+   * 设置ID属性,主要用于人工指定键值
+   */
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  private String companyId = null;
+
+  private Boolean deleted = false;
+
+  private String addAccount = null;
+
+  private Date addTime = null;
+
+  private String editAccount = null;
+
+  private Date editTime = null;
+
+  private String deleteAccount = null;
+
+  private Date deleteTime = null;
+
+  public String getCompanyId() {
+    return companyId;
+  }
+
+  public void setCompanyId(String companyId) {
+    this.companyId = companyId;
+  }
+
+  public Boolean isDeleted() {
+    return deleted;
+  }
+
+  public void setDeleted(Boolean deleted) {
+    this.deleted = deleted;
+  }
+
+  public String getAddAccount() {
+    return addAccount;
+  }
+
+  public void setAddAccount(String addAccount) {
+    this.addAccount = addAccount;
+  }
+
+  public Date getAddTime() {
+    return addTime;
+  }
+
+  public void setAddTime(Date addTime) {
+    this.addTime = addTime;
+  }
+
+  public String getEditAccount() {
+    return editAccount;
+  }
+
+  public void setEditAccount(String editAccount) {
+    this.editAccount = editAccount;
+  }
+
+  public Date getEditTime() {
+    return editTime;
+  }
+
+  public void setEditTime(Date editTime) {
+    this.editTime = editTime;
+  }
+
+  public String getDeleteAccount() {
+    return deleteAccount;
+  }
+
+  public void setDeleteAccount(String deleteAccount) {
+    this.deleteAccount = deleteAccount;
+  }
+
+  public Date getDeleteTime() {
+    return deleteTime;
+  }
+
+  public void setDeleteTime(Date deleteTime) {
+    this.deleteTime = deleteTime;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/model/PagerRequestModel.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/model/PagerRequestModel.java
new file mode 100644
index 0000000..75007e0
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/model/PagerRequestModel.java
@@ -0,0 +1,42 @@
+package com.supwisdom.leaveschool.proxy.common.model;
+
+import java.io.Serializable;
+import java.util.Map;
+
+public class PagerRequestModel implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 6099787167570502127L;
+
+  private int pageIndex = 0;
+  private int pageSize = 20;
+
+  public int getPageIndex() {
+    return pageIndex;
+  }
+
+  public void setPageIndex(int pageIndex) {
+    this.pageIndex = pageIndex;
+  }
+
+  public int getPageSize() {
+    return pageSize;
+  }
+
+  public void setPageSize(int pageSize) {
+    this.pageSize = pageSize;
+  }
+
+  private Map<String, Object> mapBean;
+
+  public Map<String, Object> getMapBean() {
+    return mapBean;
+  }
+
+  public void setMapBean(Map<String, Object> mapBean) {
+    this.mapBean = mapBean;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/model/PagerResponseModel.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/model/PagerResponseModel.java
new file mode 100644
index 0000000..7be5a93
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/common/model/PagerResponseModel.java
@@ -0,0 +1,71 @@
+package com.supwisdom.leaveschool.proxy.common.model;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+public class PagerResponseModel<T> extends PagerRequestModel implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3449591075961852442L;
+
+  private int currentItemCount;
+  
+  private int pageCount;
+  private long recordCount;
+
+  private Collection<T> items;
+
+  public PagerResponseModel() {
+
+  }
+
+  public PagerResponseModel(PagerRequestModel pagerRequestModel) {
+    super.setPageIndex(pagerRequestModel.getPageIndex());
+    super.setPageSize(pagerRequestModel.getPageSize());
+    super.setMapBean(pagerRequestModel.getMapBean());
+  }
+
+  public static <T> PagerResponseModel<T> of(PagerRequestModel pagerRequestModel) {
+    PagerResponseModel<T> pagerResponseModell = new PagerResponseModel<T>();
+    pagerResponseModell.setPageIndex(pagerRequestModel.getPageIndex());
+    pagerResponseModell.setPageSize(pagerRequestModel.getPageSize());
+    pagerResponseModell.setMapBean(pagerRequestModel.getMapBean());
+
+    return pagerResponseModell;
+  }
+
+  public int getCurrentItemCount() {
+    return currentItemCount;
+  }
+
+  public void setCurrentItemCount(int currentItemCount) {
+    this.currentItemCount = currentItemCount;
+  }
+
+  public int getPageCount() {
+    return pageCount;
+  }
+
+  public void setPageCount(int pageCount) {
+    this.pageCount = pageCount;
+  }
+
+  public long getRecordCount() {
+    return recordCount;
+  }
+
+  public void setRecordCount(long recordCount) {
+    this.recordCount = recordCount;
+  }
+
+  public Collection<T> getItems() {
+    return items;
+  }
+
+  public void setItems(Collection<T> items) {
+    this.items = items;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Group.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Group.java
new file mode 100644
index 0000000..1269f29
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Group.java
@@ -0,0 +1,64 @@
+package com.supwisdom.leaveschool.proxy.user.domain;
+
+import com.supwisdom.leaveschool.proxy.common.domain.ABaseDomain;
+
+public class Group extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4260326816456622523L;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Permission.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Permission.java
new file mode 100644
index 0000000..c856618
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Permission.java
@@ -0,0 +1,155 @@
+package com.supwisdom.leaveschool.proxy.user.domain;
+
+import com.supwisdom.leaveschool.proxy.common.domain.ABaseDomain;
+
+public class Permission extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8834200833972243635L;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 类型(1 应用,2 页面,3 操作)
+   */
+  private String type;
+
+  /**
+   * URL地址
+   */
+  private String url;
+
+  /**
+   * 父级ID
+   */
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  private String order;
+
+  /**
+   * 层次
+   */
+  private String level;
+
+  /**
+   * 左序
+   */
+  private int lft;
+
+  /**
+   * 右序
+   */
+  private int rgt;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public void setUrl(String url) {
+    this.url = url;
+  }
+
+  public String getParentId() {
+    return parentId;
+  }
+
+  public void setParentId(String parentId) {
+    this.parentId = parentId;
+  }
+
+  public String getOrder() {
+    return order;
+  }
+
+  public void setOrder(String order) {
+    this.order = order;
+  }
+
+  public String getLevel() {
+    return level;
+  }
+
+  public void setLevel(String level) {
+    this.level = level;
+  }
+
+  public int getLft() {
+    return lft;
+  }
+
+  public void setLft(int lft) {
+    this.lft = lft;
+  }
+
+  public int getRgt() {
+    return rgt;
+  }
+
+  public void setRgt(int rgt) {
+    this.rgt = rgt;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Role.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Role.java
new file mode 100644
index 0000000..207c3ef
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/Role.java
@@ -0,0 +1,64 @@
+package com.supwisdom.leaveschool.proxy.user.domain;
+
+import com.supwisdom.leaveschool.proxy.common.domain.ABaseDomain;
+
+public class Role extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5470129732727732514L;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/User.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/User.java
new file mode 100644
index 0000000..6298188
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/domain/User.java
@@ -0,0 +1,138 @@
+package com.supwisdom.leaveschool.proxy.user.domain;
+
+import com.supwisdom.leaveschool.proxy.common.domain.ABaseDomain;
+
+public class User extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -151087923960172281L;
+
+  /**
+   * 用户名
+   */
+  private String username;
+
+  /**
+   * 密码
+   */
+  private String password;
+
+  /**
+   * 是否可用,1 可用,0 不可用,默认:1
+   */
+  private Boolean enabled;
+  /**
+   * 账号未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean accountNonExpired;
+  /**
+   * 账号未锁定,1 未锁定,0 锁定,默认:1
+   */
+  private Boolean accountNonLocked;
+  /**
+   * 密码未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean credentialsNonExpired;
+
+  /**
+   * 姓名
+   */
+  private String name;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 登录手机
+   */
+  private String mobile;
+  /**
+   * 登录邮箱
+   */
+  private String email;
+
+  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 Boolean getEnabled() {
+    return enabled;
+  }
+
+  public void setEnabled(Boolean enabled) {
+    this.enabled = enabled;
+  }
+
+  public Boolean getAccountNonExpired() {
+    return accountNonExpired;
+  }
+
+  public void setAccountNonExpired(Boolean accountNonExpired) {
+    this.accountNonExpired = accountNonExpired;
+  }
+
+  public Boolean getAccountNonLocked() {
+    return accountNonLocked;
+  }
+
+  public void setAccountNonLocked(Boolean accountNonLocked) {
+    this.accountNonLocked = accountNonLocked;
+  }
+
+  public Boolean getCredentialsNonExpired() {
+    return credentialsNonExpired;
+  }
+
+  public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
+    this.credentialsNonExpired = credentialsNonExpired;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public String getMobile() {
+    return mobile;
+  }
+
+  public void setMobile(String mobile) {
+    this.mobile = mobile;
+  }
+
+  public String getEmail() {
+    return email;
+  }
+
+  public void setEmail(String email) {
+    this.email = email;
+  }
+
+}
diff --git a/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/model/SecurityUser.java b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/model/SecurityUser.java
new file mode 100644
index 0000000..798926a
--- /dev/null
+++ b/leaveschool/client/src/main/java/com/supwisdom/leaveschool/proxy/user/model/SecurityUser.java
@@ -0,0 +1,47 @@
+package com.supwisdom.leaveschool.proxy.user.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.supwisdom.leaveschool.proxy.user.domain.Permission;
+import com.supwisdom.leaveschool.proxy.user.domain.Role;
+import com.supwisdom.leaveschool.proxy.user.domain.User;
+
+public class SecurityUser implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1068580365294859071L;
+
+  private User user;
+
+  private List<Role> roles;
+
+  private List<Permission> permissions;
+
+  public User getUser() {
+    return user;
+  }
+
+  public void setUser(User user) {
+    this.user = user;
+  }
+
+  public List<Role> getRoles() {
+    return roles;
+  }
+
+  public void setRoles(List<Role> roles) {
+    this.roles = roles;
+  }
+
+  public List<Permission> getPermissions() {
+    return permissions;
+  }
+
+  public void setPermissions(List<Permission> permissions) {
+    this.permissions = permissions;
+  }
+
+}
diff --git a/leaveschool/client/src/main/resources/application.yml b/leaveschool/client/src/main/resources/application.yml
new file mode 100644
index 0000000..ad1efe4
--- /dev/null
+++ b/leaveschool/client/src/main/resources/application.yml
@@ -0,0 +1,60 @@
+server:
+  port: 8080
+
+## logging
+logging:
+  level:
+    root: INFO
+    org.springframework.web: INFO
+    org.springframework.cloud.openfeign: TRACE
+    com.supwisdom.infras.security: DEBUG
+    com.supwisdom.leaveschool: DEBUG
+
+spring:
+  application:
+    name: sample-client
+  thymeleaf:
+    cache: false
+
+feign:
+  client:
+    config:
+      default:
+        connectTimeout: 12000
+        readTimeout: 12000
+        loggerLevel: full
+  hystrix:
+    enabled: true
+
+hystrix:
+  command:
+    default:
+      execution:
+        timeout:
+          enabled: true
+        isolation:
+          thread:
+            timeoutInMilliseconds: 12000
+
+#infras.security.cas.enabled: true
+
+## CAS
+#应用访问地址
+app.server.host.url: http://localhost:8080
+#应用登录地址
+app.login.url: /web/caslogin
+#应用登出地址
+app.logout.url: /web/logout
+
+#CAS服务地址
+cas.server.host.url: http://101.231.81.202:9080/cas
+#CAS服务登录地址
+cas.server.host.login_url: ${cas.server.host.url}/login
+#CAS服务登出地址
+cas.server.host.logout_url: ${cas.server.host.url}/logout?service=${app.server.host.url}
+
+
+gateway.api.url: http://localhost:5555
+
+#sample-user.api.url: ${gateway.api.url}/sample-user
+sample-user.api.url: http://localhost:10010
diff --git a/leaveschool/client/src/main/resources/static/assets/css/CSS b/leaveschool/client/src/main/resources/static/assets/css/CSS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/leaveschool/client/src/main/resources/static/assets/css/CSS
diff --git a/leaveschool/client/src/main/resources/static/assets/css/main.css b/leaveschool/client/src/main/resources/static/assets/css/main.css
new file mode 100644
index 0000000..5e6687a
--- /dev/null
+++ b/leaveschool/client/src/main/resources/static/assets/css/main.css
@@ -0,0 +1,13 @@
+body {
+    font-family: sans;
+    font-size: 1em;
+}
+
+p.error {
+    font-weight: bold;
+    color: red;
+}
+
+div.logout {
+    float: right;
+}
\ No newline at end of file
diff --git a/leaveschool/client/src/main/resources/static/assets/images/IMAGES b/leaveschool/client/src/main/resources/static/assets/images/IMAGES
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/leaveschool/client/src/main/resources/static/assets/images/IMAGES
diff --git a/leaveschool/client/src/main/resources/static/assets/js/JS b/leaveschool/client/src/main/resources/static/assets/js/JS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/leaveschool/client/src/main/resources/static/assets/js/JS
diff --git a/leaveschool/client/src/main/resources/templates/web/admin/user/index.html b/leaveschool/client/src/main/resources/templates/web/admin/user/index.html
new file mode 100644
index 0000000..2326203
--- /dev/null
+++ b/leaveschool/client/src/main/resources/templates/web/admin/user/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
+<head>
+<title>Hello Spring Security</title>
+<meta charset="utf-8" />
+<link rel="stylesheet" href="/assets/css/main.css" th:href="@{/assets/css/main.css}" />
+</head>
+<body>
+  <div th:substituteby="web/index::logout"></div>
+  <h1>This is a secured page!</h1>
+  <p>
+    <a href="/web/index" th:href="@{/web/index}">Back to home page</a>
+  </p>
+</body>
+</html>
\ No newline at end of file
diff --git a/leaveschool/client/src/main/resources/templates/web/index.html b/leaveschool/client/src/main/resources/templates/web/index.html
new file mode 100644
index 0000000..b9dcbf4
--- /dev/null
+++ b/leaveschool/client/src/main/resources/templates/web/index.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<!-- <html xmlns:th="http://www.thymeleaf.org"> -->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
+<head>
+<title>Hello Spring Security</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<meta charset="utf-8" />
+<link rel="stylesheet" href="/assets/css/main.css" th:href="@{/assets/css/main.css}" />
+</head>
+<body>
+  <div th:fragment="logout" class="logout" sec:authorize="isAuthenticated()">
+    Logged in user: <span sec:authentication="name"></span> | 
+    Roles: <span sec:authentication="principal.authorities"></span>
+    <div>
+      <form action="#" th:action="@{/web/logout}" method="post">
+        <input type="submit" value="Logout" />
+      </form>
+    </div>
+  </div>
+  <h1>Hello Spring Security</h1>
+  <p>This is an unsecured page, but you can access the secured pages after authenticating.</p>
+  <ul>
+    <li>Go to the <a href="/web/admin/user/index" th:href="@{/web/admin/user/index}">secured pages</a></li>
+  </ul>
+</body>
+</html>
diff --git a/leaveschool/client/src/main/resources/templates/web/login.html b/leaveschool/client/src/main/resources/templates/web/login.html
new file mode 100644
index 0000000..1bc5e4d
--- /dev/null
+++ b/leaveschool/client/src/main/resources/templates/web/login.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
+    <head>
+        <title>Login page</title>
+        <meta charset="utf-8" />
+        <link rel="stylesheet" href="/assets/css/main.css" th:href="@{/assets/css/main.css}" />
+	</head>
+    <body>
+        <h1>Login page</h1>
+        <p>Example user: user / password</p>
+        <p th:if="${loginError}" class="error">Wrong user or password</p>
+        <div th:if="${param.error}" class="alert alert-error">Invalid username and password.</div>
+        <div th:if="${param.logout}" class="alert alert-success">You have been logged out.</div>
+        
+        <form th:action="@{/web/login}" method="post">
+            <label for="username">Username</label>:
+            <input type="text" id="username" name="username" autofocus="autofocus" /> <br />
+            <label for="password">Password</label>:
+            <input type="password" id="password" name="password" /> <br />
+            <input type="submit" value="Log in" />
+        </form>
+        <p><a href="/index" th:href="@{/index}">Back to home page</a></p>
+    </body>
+</html>
diff --git a/leaveschool/common/pom.xml b/leaveschool/common/pom.xml
new file mode 100644
index 0000000..67d0e78
--- /dev/null
+++ b/leaveschool/common/pom.xml
@@ -0,0 +1,74 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.leaveschool</groupId>
+    <artifactId>leaveschool-parent</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.supwisdom.leaveschool</groupId>
+  <artifactId>leaveschool-common</artifactId>
+  <packaging>jar</packaging>
+
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-data-jpa</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>commons-collections</groupId>
+      <artifactId>commons-collections</artifactId>
+    </dependency>
+
+    <!-- Test things -->
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/controller/api/CrudApiController.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/controller/api/CrudApiController.java
new file mode 100644
index 0000000..0f32960
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/controller/api/CrudApiController.java
@@ -0,0 +1,331 @@
+package com.supwisdom.leaveschool.common.controller.api;
+
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+import com.supwisdom.leaveschool.common.model.PagerRequestModel;
+import com.supwisdom.leaveschool.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.common.model.SuccessResponseModel;
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.DomainUtils;
+
+public abstract class CrudApiController<D extends ABaseDomain, R extends BaseJpaRepository<D>> {
+  
+  protected abstract R getRepository();
+  
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains' 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains?pageIndex=2&pageSize=50' 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains?pageIndex=0&pageSize=20&mapBean[code]=code&mapBean[name]=name&mapBean[status]=1' 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains?pageIndex=0&pageSize=20&mapBean[code]=code&mapBean[name]=name&mapBean[status]=0' 
+   * 
+   * response success: 
+   * 
+   * {
+   *   "pageIndex":0,
+   *   "pageSize":20,
+   *   "mapBean":null,
+   *   "pageCount":1,
+   *   "recordCount":1,
+   *   "items":[
+   *     {
+   *       "id":"ff80808164feb8990164feba0de50000",
+   *       "companyId":"1",
+   *       "deleted":false,
+   *       "addAccount":"group","addTime":"2018-08-03T07:39:23.000+0000",
+   *       "editAccount":null,"editTime":null,
+   *       "deleteAccount":null,"deleteTime":null,
+   *       "code":"test001",
+   *       "name":"测试001",
+   *       "memo":"测试001备注",
+   *       "status":"1"
+   *     }
+   *   ]
+   * }
+   * 
+   * response error 401:
+   * 
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/${API_PATH_PREFIX}/domains"
+   * }
+   * 
+   * @param pagerRequestModel
+   * @return
+   */
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public PagerResponseModel<D> list(PagerRequestModel pagerRequestModel) {
+    
+    Page<D> page = getRepository().selectPageList(pagerRequestModel.getPageIndex(), pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+    
+    PagerResponseModel<D> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setCurrentItemCount(page.getNumberOfElements());
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+    
+    return pagerResponseModel;
+  }
+  
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains/1' 
+   * 
+   * response success: 
+   * 
+   * {
+   *   "id":"ff80808164feb8990164feba0de50000",
+   *   "companyId":"1",
+   *   "deleted":false,
+   *   "addAccount":"group","addTime":"2018-08-03T07:39:23.000+0000",
+   *   "editAccount":null,"editTime":null,
+   *   "deleteAccount":null,"deleteTime":null,
+   *   "code":"test001",
+   *   "name":"测试001",
+   *   "memo":"测试001备注",
+   *   "status":"1"
+   * }
+   * 
+   * response error 401:
+   * 
+   * {
+   *   "timestamp":"2018-08-03T08:43:26.080+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/${API_PATH_PREFIX}/domains/ff80808164fecf640164fed269480000"
+   * }
+   * 
+   * response error 500:
+   * 
+   * {
+   *   "timestamp":"2018-08-03T07:44:07.963+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.get.domain.not.exist",
+   *   "path":"/${API_PATH_PREFIX}/domains/1"
+   * }
+   * 
+   * @param id
+   * @return
+   */
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public D get(@PathVariable("id") String id) {
+    
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty");  // FIXME: RestException
+    }
+    
+    D d = getRepository().selectById(id);
+    
+    if (d == null) {
+      throw new RuntimeException("exception.get.domain.not.exist");  // FIXME: RestException
+    }
+    
+    return d;
+  }
+  
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains' \
+   * -d '{"code":"test001","name":"测试001","memo":"测试001备注","status":"1","addAccount":"admin"}'
+   * 
+   * response success: 
+   * 
+   * {
+   *   "success":"info.create.success"
+   * }
+   * 
+   * response error 401:
+   * 
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/${API_PATH_PREFIX}/domains"
+   * }
+   * 
+   * response error: // FIXME: save error
+   * 
+   * {
+   *   "timestamp":"2018-08-03T07:45:43.436+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"org.springframework.dao.DataIntegrityViolationException",
+   *   "message":"could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
+   *   "path":"/${API_PATH_PREFIX}/domains"
+   * }
+   * 
+   * @param group
+   * @return
+   */
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public SuccessResponseModel create(@RequestBody D d) {
+    
+    @SuppressWarnings("unused")
+    D ret = getRepository().insert(d);
+    
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.create.success");
+    
+    return res;
+  }
+  
+  /**
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains' \
+   * -d '{"id":"1","status":"0"}'
+   * 
+   * response success:
+   * 
+   * {
+   *   "success":"info.update.success"
+   * }
+   * 
+   * response error 401:
+   * 
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/${API_PATH_PREFIX}/domains"
+   * }
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains' \
+   * -d '{"status":"0"}'
+   * 
+   * response error:
+   * 
+   * {
+   *   "timestamp":"2018-08-03T07:50:52.327+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.update.id.must.not.empty",
+   *   "path":"/${API_PATH_PREFIX}/domains"
+   * }
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains' \
+   * -d '{"id":"1","status":"0"}'
+   * 
+   * response error:
+   * 
+   * {
+   *   "timestamp":"2018-08-03T07:48:24.774+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.update.domain.not.exist",
+   *   "path":"/${API_PATH_PREFIX}/domains"
+   * }
+   * 
+   * @param group
+   * @return
+   */
+  @PutMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public SuccessResponseModel update(@RequestBody D d) {
+    
+    if (d.getId() == null || d.getId().length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty");  // FIXME: RestException
+    }
+    
+    D tmp = getRepository().selectById(d.getId());
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist");  // FIXME: RestException
+    }
+    
+    tmp = DomainUtils.merge(d, tmp);
+    
+    @SuppressWarnings("unused")
+    D ret = getRepository().update(tmp);
+    getRepository().flush();
+    
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.update.success");
+    
+    return res;
+  }
+  
+  /**
+   * 
+   * curl -i -s -X DELETE -H 'Accept:application/json' 'http://localhost:10010/${API_PATH_PREFIX}/domains/1'
+   * 
+   * response success: 
+   * 
+   * {
+   *   "success":"info.delete.success"
+   * }
+   * 
+   * response error 401:
+   * 
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/${API_PATH_PREFIX}/domains/1"
+   * }
+   * 
+   * response error 500: 
+   * 
+   * {
+   *   "timestamp":"2018-08-03T08:03:16.364+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.delete.domain.not.exist",
+   *   "path":"/${API_PATH_PREFIX}/domains/1"
+   * }
+   * 
+   * @param id
+   * @return
+   */
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public SuccessResponseModel delete(@PathVariable("id") String id) {
+    
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty");  // FIXME: RestException
+    }
+    
+    D tmp = getRepository().selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist");  // FIXME: RestException
+    }
+    
+    getRepository().delete(tmp);
+    
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.delete.success");
+    
+    return res;
+  }
+  
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/domain/ABaseDomain.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/domain/ABaseDomain.java
new file mode 100644
index 0000000..2c01d9a
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/domain/ABaseDomain.java
@@ -0,0 +1,138 @@
+package com.supwisdom.leaveschool.common.domain;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+import org.hibernate.annotations.GenericGenerator;
+
+@MappedSuperclass
+public abstract class ABaseDomain implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8023881887762314483L;
+
+  @Id
+  @GeneratedValue(generator = "uuidStrategy")
+  @GenericGenerator(name = "uuidStrategy", strategy = "uuid")
+  //@ApiModelProperty(value = "ID", required = false, example = "")
+  @Column(name = "ID")
+  private String id;
+
+  /**
+   * 获取主键
+   */
+  public String getId() {
+    return id;
+  }
+
+  /**
+   * 设置ID属性,主要用于人工指定键值
+   */
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  //@ApiModelProperty(value = "CompanyID", required = true, example = "1")
+  @Column(name = "COMPANY_ID")
+  private String companyId = null;
+
+  //@ApiModelProperty(value = "是否删除", hidden = true, example = "false")
+  @Column(name = "DELETED")
+  private Boolean deleted = false;
+
+  //@ApiModelProperty(value = "创建人", hidden = true)
+  @Column(name = "ADD_ACCOUNT")
+  private String addAccount = null;
+
+  //@ApiModelProperty(value = "创建时间", hidden = true)
+  @Column(name = "ADD_TIME")
+  private Date addTime = null;
+
+  //@ApiModelProperty(value = "修改人", hidden = true)
+  @Column(name = "EDIT_ACCOUNT")
+  private String editAccount = null;
+
+  //@ApiModelProperty(value = "修改时间", hidden = true)
+  @Column(name = "EDIT_TIME")
+  private Date editTime = null;
+
+  //@ApiModelProperty(value = "删除人", hidden = true)
+  @Column(name = "DELETE_ACCOUNT")
+  private String deleteAccount = null;
+
+  //@ApiModelProperty(value = "删除时间", hidden = true)
+  @Column(name = "DELETE_TIME")
+  private Date deleteTime = null;
+
+  public String getCompanyId() {
+    return companyId;
+  }
+
+  public void setCompanyId(String companyId) {
+    this.companyId = companyId;
+  }
+
+  public Boolean isDeleted() {
+    return deleted;
+  }
+
+  public void setDeleted(Boolean deleted) {
+    this.deleted = deleted;
+  }
+
+  public String getAddAccount() {
+    return addAccount;
+  }
+
+  public void setAddAccount(String addAccount) {
+    this.addAccount = addAccount;
+  }
+
+  public Date getAddTime() {
+    return addTime;
+  }
+
+  public void setAddTime(Date addTime) {
+    this.addTime = addTime;
+  }
+
+  public String getEditAccount() {
+    return editAccount;
+  }
+
+  public void setEditAccount(String editAccount) {
+    this.editAccount = editAccount;
+  }
+
+  public Date getEditTime() {
+    return editTime;
+  }
+
+  public void setEditTime(Date editTime) {
+    this.editTime = editTime;
+  }
+
+  public String getDeleteAccount() {
+    return deleteAccount;
+  }
+
+  public void setDeleteAccount(String deleteAccount) {
+    this.deleteAccount = deleteAccount;
+  }
+
+  public Date getDeleteTime() {
+    return deleteTime;
+  }
+
+  public void setDeleteTime(Date deleteTime) {
+    this.deleteTime = deleteTime;
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/PagerRequestModel.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/PagerRequestModel.java
new file mode 100644
index 0000000..75309f1
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/PagerRequestModel.java
@@ -0,0 +1,42 @@
+package com.supwisdom.leaveschool.common.model;
+
+import java.io.Serializable;
+import java.util.Map;
+
+public class PagerRequestModel implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 6099787167570502127L;
+
+  private int pageIndex = 0;
+  private int pageSize = 20;
+
+  public int getPageIndex() {
+    return pageIndex;
+  }
+
+  public void setPageIndex(int pageIndex) {
+    this.pageIndex = pageIndex;
+  }
+
+  public int getPageSize() {
+    return pageSize;
+  }
+
+  public void setPageSize(int pageSize) {
+    this.pageSize = pageSize;
+  }
+
+  private Map<String, Object> mapBean;
+
+  public Map<String, Object> getMapBean() {
+    return mapBean;
+  }
+
+  public void setMapBean(Map<String, Object> mapBean) {
+    this.mapBean = mapBean;
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/PagerResponseModel.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/PagerResponseModel.java
new file mode 100644
index 0000000..5df7315
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/PagerResponseModel.java
@@ -0,0 +1,71 @@
+package com.supwisdom.leaveschool.common.model;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+public class PagerResponseModel<T> extends PagerRequestModel implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3449591075961852442L;
+
+  private int currentItemCount;
+  
+  private int pageCount;
+  private long recordCount;
+
+  private Collection<T> items;
+
+  public PagerResponseModel() {
+
+  }
+
+  public PagerResponseModel(PagerRequestModel pagerRequestModel) {
+    super.setPageIndex(pagerRequestModel.getPageIndex());
+    super.setPageSize(pagerRequestModel.getPageSize());
+    super.setMapBean(pagerRequestModel.getMapBean());
+  }
+
+  public static <T> PagerResponseModel<T> of(PagerRequestModel pagerRequestModel) {
+    PagerResponseModel<T> pagerResponseModell = new PagerResponseModel<T>();
+    pagerResponseModell.setPageIndex(pagerRequestModel.getPageIndex());
+    pagerResponseModell.setPageSize(pagerRequestModel.getPageSize());
+    pagerResponseModell.setMapBean(pagerRequestModel.getMapBean());
+
+    return pagerResponseModell;
+  }
+
+  public int getCurrentItemCount() {
+    return currentItemCount;
+  }
+
+  public void setCurrentItemCount(int currentItemCount) {
+    this.currentItemCount = currentItemCount;
+  }
+
+  public int getPageCount() {
+    return pageCount;
+  }
+
+  public void setPageCount(int pageCount) {
+    this.pageCount = pageCount;
+  }
+
+  public long getRecordCount() {
+    return recordCount;
+  }
+
+  public void setRecordCount(long recordCount) {
+    this.recordCount = recordCount;
+  }
+
+  public Collection<T> getItems() {
+    return items;
+  }
+
+  public void setItems(Collection<T> items) {
+    this.items = items;
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/SuccessResponseModel.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/SuccessResponseModel.java
new file mode 100644
index 0000000..750e55c
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/model/SuccessResponseModel.java
@@ -0,0 +1,22 @@
+package com.supwisdom.leaveschool.common.model;
+
+import java.io.Serializable;
+
+public class SuccessResponseModel implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 280042050835200337L;
+  
+  private String success;
+
+  public String getSuccess() {
+    return success;
+  }
+
+  public void setSuccess(String success) {
+    this.success = success;
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/repository/ABaseJpaRepositoryImpl.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/repository/ABaseJpaRepositoryImpl.java
new file mode 100644
index 0000000..a245196
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/repository/ABaseJpaRepositoryImpl.java
@@ -0,0 +1,84 @@
+package com.supwisdom.leaveschool.common.repository;
+
+import java.util.Calendar;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.persistence.EntityManager;
+import javax.transaction.Transactional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.jpa.repository.support.JpaEntityInformation;
+import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
+import org.springframework.data.repository.NoRepositoryBean;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Transactional
+@NoRepositoryBean
+public class ABaseJpaRepositoryImpl<T extends ABaseDomain> extends SimpleJpaRepository<T, String> implements BaseJpaRepository<T> {
+
+  @SuppressWarnings("unused")
+  private final EntityManager em;
+
+  public ABaseJpaRepositoryImpl(Class<T> domainClass, EntityManager em) {
+    super(domainClass, em);
+    this.em = em;
+  }
+
+  public ABaseJpaRepositoryImpl(JpaEntityInformation<T, String> information, EntityManager em) {
+    super(information, em);
+    this.em = em;
+  }
+  
+  public Page<T> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<T> page = this.findAll(pageRequest);
+
+    return page;
+  }
+
+  public T selectById(String id) {
+
+    try {
+      Optional<T> entity = this.findById(id);
+
+      return entity.get();
+    } catch (RuntimeException e) {
+      System.out.println("RuntimeException:" + e.getMessage());
+    } catch (Exception e) {
+      System.out.println("Exception:" + e.getMessage());
+    }
+
+    return null;
+  }
+
+  public T insert(T entity) {
+
+    if (entity.getCompanyId() == null || entity.getCompanyId().isEmpty()) {
+      entity.setCompanyId("1");
+    }
+
+    entity.setDeleted(false);
+    //entity.setAddAccount(AuthUtil.getRemoteUser()); // FIXME: setAddAccount
+    entity.setAddTime(Calendar.getInstance().getTime());
+
+    T e = this.save(entity);
+
+    return e;
+  }
+
+  public T update(T entity) {
+
+    //entity.setEditAccount(AuthUtil.getRemoteUser()); // FIXME: setEditAccount
+    entity.setEditTime(Calendar.getInstance().getTime());
+
+    T e = this.save(entity);
+
+    return e;
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/repository/BaseJpaRepository.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/repository/BaseJpaRepository.java
new file mode 100644
index 0000000..e1e0531
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/repository/BaseJpaRepository.java
@@ -0,0 +1,66 @@
+package com.supwisdom.leaveschool.common.repository;
+
+import java.util.Calendar;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.repository.NoRepositoryBean;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@NoRepositoryBean
+public interface BaseJpaRepository<T extends ABaseDomain> extends JpaRepository<T, String> {
+  
+  public default Page<T> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<T> page = this.findAll(pageRequest);
+
+    return page;
+  }
+  
+  public default T selectById(String id) {
+
+    try {
+      Optional<T> entity = this.findById(id);
+
+      return entity.get();
+    } catch (RuntimeException e) {
+      System.out.println("RuntimeException:" + e.getMessage());
+    } catch (Exception e) {
+      System.out.println("Exception:" + e.getMessage());
+    }
+
+    return null;
+  }
+  
+  public default T insert(T entity) {
+
+    if (entity.getCompanyId() == null || entity.getCompanyId().isEmpty()) {
+      entity.setCompanyId("1");
+    }
+
+    entity.setDeleted(false);
+    //entity.setAddAccount(AuthUtil.getRemoteUser()); // FIXME: setAddAccount
+    entity.setAddTime(Calendar.getInstance().getTime());
+
+    T e = this.save(entity);
+
+    return e;
+  }
+  
+  public default T update(T entity) {
+
+    //entity.setEditAccount(AuthUtil.getRemoteUser()); // FIXME: setEditAccount
+    entity.setEditTime(Calendar.getInstance().getTime());
+
+    T e = this.save(entity);
+
+    return e;
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/DomainUtils.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/DomainUtils.java
new file mode 100644
index 0000000..f02d7e5
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/DomainUtils.java
@@ -0,0 +1,133 @@
+package com.supwisdom.leaveschool.common.util;
+
+import java.lang.reflect.Field;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+
+/**
+ * 对 domain 的操作 如:复制、合并、转换等
+ * 
+ * @author loie
+ *
+ */
+public class DomainUtils {
+
+  /**
+   * 合并 domain 中带有{@link Column}注解的字段值, 将 oldDomain 中的值,使用 newDomain 中不为null的值
+   * 进行覆盖
+   * 
+   * @param newDomain
+   *          ,
+   * @param oldDomain
+   *          ,被覆盖的实体
+   * @return 合并后的oldDomain
+   */
+  public static <T> T merge(T newDomain, T oldDomain) {
+
+    for (Class<?> clazz = oldDomain.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+      for (Field field : clazz.getDeclaredFields()) {
+        Column[] annotations = field.getAnnotationsByType(Column.class);
+        if (annotations == null || annotations.length == 0) {
+          Id[] idAnnotations = field.getAnnotationsByType(Id.class);
+          if (idAnnotations == null || idAnnotations.length == 0) {
+            continue;
+          }
+        }
+
+        String fieldName = field.getName();
+        Object newFieldValue = ReflectUtils.getFieldValue(newDomain, fieldName);
+
+        if (newFieldValue != null) {
+          ReflectUtils.setFieldValue(oldDomain, fieldName, newFieldValue);
+        }
+      }
+    }
+
+    return oldDomain;
+  }
+
+  public static <S, T> T copy(S sourceDomain, T targetDomain) {
+    
+    for (Class<?> clazz = targetDomain.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+      for (Field field : clazz.getDeclaredFields()) {
+        Column[] annotations = field.getAnnotationsByType(Column.class);
+        if (annotations == null || annotations.length == 0) {
+          Id[] idAnnotations = field.getAnnotationsByType(Id.class);
+          if (idAnnotations == null || idAnnotations.length == 0) {
+            continue;
+          }
+        }
+
+        String fieldName = field.getName();
+        Object sFieldValue = ReflectUtils.getFieldValue(sourceDomain, fieldName);
+
+        if (sFieldValue != null) {
+          ReflectUtils.setFieldValue(targetDomain, fieldName, sFieldValue);
+        }
+      }
+    }
+
+    return targetDomain;
+  }
+
+  public static void main(String[] args) {
+
+    Test target0 = new Test();
+    target0.setId("id0");
+    target0.setCode("code");
+    target0.setName("name");
+    target0.setDate(new Date());
+    target0.setEnabled(false);
+    target0.setStatus(1);
+
+    System.out.println("target0 == " + target0.toString());
+    System.out.println();
+
+    Test source1 = new Test();
+    // source1.setId("id1");
+    source1.setCode("code1");
+    // source1.setName("name");
+    // source1.setDate(new Date());
+    source1.setEnabled(true);
+    // source1.setStatus(1);
+    System.out.println("source1 == " + source1.toString());
+
+    Test target1 = DomainUtils.merge(source1, target0);
+    System.out.println("target0 == " + target0.toString());
+    System.out.println("target1 == " + target1.toString());
+    System.out.println();
+
+    Test source2 = new Test();
+    // source2.setId("id2");
+    source2.setCode("code2");
+    source2.setName("name2");
+    // source2.setDate(new Date());
+    // source2.setEnabled(true);
+    source2.setStatus(2);
+    System.out.println("source2 == " + source2.toString());
+
+    Test target2 = DomainUtils.merge(source2, target0);
+    System.out.println("target0 == " + target0.toString());
+    System.out.println("target2 == " + target2.toString());
+    System.out.println();
+
+
+    Test test = new Test();
+    test.setId("id0");
+    test.setCode("code");
+    test.setName("name");
+    test.setDate(new Date());
+    test.setEnabled(false);
+    test.setStatus(1);
+
+    Test2 test2 = new Test2();
+    test2 = DomainUtils.copy(test, test2);
+    System.out.println("test    == " + test.toString());
+    System.out.println("test2   == " + test2.toString());
+    System.out.println();
+
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/MapBeanUtils.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/MapBeanUtils.java
new file mode 100644
index 0000000..b536b29
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/MapBeanUtils.java
@@ -0,0 +1,150 @@
+package com.supwisdom.leaveschool.common.util;
+
+import java.util.Map;
+
+public class MapBeanUtils {
+
+  /**
+   * 判断 mapBean 中的 key 是否存在;若存在,则判断是否有值
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static boolean containsValue(Map<String, Object> mapBean, String key) {
+
+    if (!mapBean.containsKey(key)) {
+      return false;
+    }
+
+    if (mapBean.get(key) == null) {
+      return false;
+    }
+
+    if (String.valueOf(mapBean.get(key)).isEmpty()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 null
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static String getString(Map<String, Object> mapBean, String key) {
+
+    return getString(mapBean, key, null);
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+   * 
+   * @param mapBean
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public static String getString(Map<String, Object> mapBean, String key, String defaultValue) {
+
+    if (containsValue(mapBean, key)) {
+      return String.valueOf(mapBean.get(key));
+    }
+
+    return defaultValue;
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 false
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static Boolean getBoolean(Map<String, Object> mapBean, String key) {
+
+    return getBoolean(mapBean, key, null);
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+   * 
+   * @param mapBean
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public static Boolean getBoolean(Map<String, Object> mapBean, String key, Boolean defaultValue) {
+
+    if (containsValue(mapBean, key)) {
+      Boolean b = Boolean.valueOf(String.valueOf(mapBean.get(key)));
+      return b == null ? defaultValue : b;
+    }
+
+    return defaultValue;
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 -1
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static Integer getInteger(Map<String, Object> mapBean, String key) {
+
+    return getInteger(mapBean, key, null);
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+   * 
+   * @param mapBean
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public static Integer getInteger(Map<String, Object> mapBean, String key, Integer defaultValue) {
+
+    if (containsValue(mapBean, key)) {
+      Integer i = Integer.valueOf(String.valueOf(mapBean.get(key)));
+      return i == null ? defaultValue : i;
+    }
+
+    return defaultValue;
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 -1L
+   * 
+   * @param mapBean
+   * @param key
+   * @return
+   */
+  public static Long getLong(Map<String, Object> mapBean, String key) {
+
+    return getLong(mapBean, key, null);
+  }
+
+  /**
+   * 获取 mapBean 中 key 的 value,若不存在,则返回 defaultValue
+   * 
+   * @param mapBean
+   * @param key
+   * @param defaultValue
+   * @return
+   */
+  public static Long getLong(Map<String, Object> mapBean, String key, Long defaultValue) {
+
+    if (containsValue(mapBean, key)) {
+      Long l = Long.valueOf(String.valueOf(mapBean.get(key)));
+      return l == null ? defaultValue : l;
+    }
+
+    return defaultValue;
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/ReflectUtils.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/ReflectUtils.java
new file mode 100644
index 0000000..53cb150
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/ReflectUtils.java
@@ -0,0 +1,155 @@
+package com.supwisdom.leaveschool.common.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * 利用反射进行操作的一个工具类
+ * 
+ */
+public class ReflectUtils {
+
+  /**
+   * 利用反射获取指定对象里面的指定属性
+   * 
+   * @param obj
+   *          目标对象
+   * @param fieldName
+   *          目标属性
+   * @return 目标字段
+   */
+  private static Field getField(Object obj, String fieldName) {
+    Field field = null;
+    for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+      try {
+        field = clazz.getDeclaredField(fieldName);
+        break;
+      } catch (NoSuchFieldException e) {
+        // 这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
+      }
+    }
+    return field;
+  }
+
+  /**
+   * 利用反射获取指定对象的指定属性
+   * 
+   * @param obj
+   *          目标对象
+   * @param fieldName
+   *          目标属性
+   * @return 目标属性的值
+   */
+  public static Object getFieldValue(Object obj, String fieldName) {
+    Object result = null;
+    Field field = ReflectUtils.getField(obj, fieldName);
+    if (field != null) {
+      field.setAccessible(true);
+      try {
+        result = field.get(obj);
+      } catch (IllegalArgumentException e) {
+        e.printStackTrace();
+      } catch (IllegalAccessException e) {
+        e.printStackTrace();
+      }
+    }
+    return result;
+  }
+
+  /**
+   * 利用反射设置指定对象的指定属性为指定的值
+   * 
+   * @param obj
+   *          目标对象
+   * @param fieldName
+   *          目标属性
+   * @param fieldValue
+   *          目标值
+   */
+  public static void setFieldValue(Object obj, String fieldName, Object fieldValue) {
+    Field field = ReflectUtils.getField(obj, fieldName);
+    if (field != null) {
+      try {
+        field.setAccessible(true);
+
+        if (fieldValue instanceof String) {
+          Object value = typeConversion(field.getType(), String.valueOf(fieldValue));
+          field.set(obj, value);
+        } else {
+          field.set(obj, fieldValue);
+        }
+
+      } catch (IllegalArgumentException e) {
+        e.printStackTrace();
+      } catch (IllegalAccessException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * 反射调用对象的方法
+   * 
+   * @param obj
+   *          对象
+   * @param methodName
+   *          方法名称
+   * @param paramType
+   *          参数类型 new Class[]{int.class,double.class}
+   * @param params
+   *          参数值 new Object[]{2,3.5}
+   * @return
+   * @throws Exception
+   */
+  public static Object invokeObjMethod(Object obj, String methodName, Class<?>[] paramTypes, Object[] params)
+      throws Exception {
+    // 发现类型
+    Class<?> cls = obj.getClass();
+    // 发现方法
+    Method method = cls.getDeclaredMethod(methodName, paramTypes);
+    // 访问方法时,压制Java对访问修饰符的检查
+    method.setAccessible(true);
+    Object val = method.invoke(obj, params);
+    return val;
+  }
+
+  /**
+   * 类型转换
+   * 
+   * @param clazz
+   *          :目标类型
+   * @param source
+   *          :待转换对象
+   * @return :目标对象
+   */
+  public static Object typeConversion(Class<?> clazz, String source) {
+
+    if (clazz == null) {
+      throw new IllegalArgumentException("clazz should not be null");
+    }
+
+    Object targetObj = null;
+    String nameType = clazz.getName();
+
+    if ("java.lang.Integer".equals(nameType) || "int".equals(nameType)) {
+      targetObj = Integer.valueOf(source);
+    } else if ("java.lang.String".equals(nameType) || "string".equals(nameType)) {
+      targetObj = source;
+    } else if ("java.lang.Float".equals(nameType) || "float".equals(nameType)) {
+      targetObj = Float.valueOf(source);
+    } else if ("java.lang.Double".equals(nameType) || "double".equals(nameType)) {
+      targetObj = Double.valueOf(source);
+    } else if ("java.lang.Boolean".equals(nameType) || "boolean".equals(nameType)) {
+      targetObj = Boolean.valueOf(source);
+    } else if ("java.lang.Long".equals(nameType) || "long".equals(nameType)) {
+      targetObj = Long.valueOf(source);
+    } else if ("java.lang.Short".equals(nameType) || "short".equals(nameType)) {
+      targetObj = Short.valueOf(source);
+    } else if ("java.lang.Character".equals(nameType) || "char".equals(nameType)) {
+      targetObj = source.charAt(1);
+    }
+
+    return targetObj;
+  }
+
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/Test.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/Test.java
new file mode 100644
index 0000000..a82d78c
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/Test.java
@@ -0,0 +1,77 @@
+package com.supwisdom.leaveschool.common.util;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+public class Test extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3545969960574885501L;
+  @Column
+  private String code = null;
+  @Column
+  private String name = null;
+  @Column
+  private Date date = null;
+  @Column
+  private Boolean enabled = null;
+  @Column
+  private Integer status = null;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public Date getDate() {
+    return date;
+  }
+
+  public void setDate(Date date) {
+    this.date = date;
+  }
+
+  public Boolean getEnabled() {
+    return enabled;
+  }
+
+  public void setEnabled(Boolean enabled) {
+    this.enabled = enabled;
+  }
+
+  public Integer getStatus() {
+    return status;
+  }
+
+  public void setStatus(Integer status) {
+    this.status = status;
+  }
+
+  @Override
+  public String toString() {
+    String s = "";
+    s+="id="+this.getId();
+    s += "&code="+String.valueOf(this.code);
+    s += "&name="+String.valueOf(this.name);
+    s += "&date="+String.valueOf(this.date);
+    s += "&enabled="+String.valueOf(this.enabled);
+    s += "&status="+String.valueOf(this.status);
+    return s;
+  }
+}
diff --git a/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/Test2.java b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/Test2.java
new file mode 100644
index 0000000..c093d0f
--- /dev/null
+++ b/leaveschool/common/src/main/java/com/supwisdom/leaveschool/common/util/Test2.java
@@ -0,0 +1,78 @@
+package com.supwisdom.leaveschool.common.util;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+public class Test2 extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3545969960574885501L;
+  @Column
+  private String name = null;
+  @Column
+  private String memo = null;
+  @Column
+  private Date date = null;
+  @Column
+  private Boolean enabled = null;
+  @Column
+  private Integer status = null;
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public Date getDate() {
+    return date;
+  }
+
+  public void setDate(Date date) {
+    this.date = date;
+  }
+
+  public Boolean getEnabled() {
+    return enabled;
+  }
+
+  public void setEnabled(Boolean enabled) {
+    this.enabled = enabled;
+  }
+
+  public Integer getStatus() {
+    return status;
+  }
+
+  public void setStatus(Integer status) {
+    this.status = status;
+  }
+
+  @Override
+  public String toString() {
+    String s = "";
+    s+="id="+this.getId();
+    //s += "&code="+String.valueOf(this.code);
+    s += "&name="+String.valueOf(this.name);
+    s += "&memo="+String.valueOf(this.memo);
+    s += "&date="+String.valueOf(this.date);
+    s += "&enabled="+String.valueOf(this.enabled);
+    s += "&status="+String.valueOf(this.status);
+    return s;
+  }
+}
diff --git a/leaveschool/pom.xml b/leaveschool/pom.xml
new file mode 100644
index 0000000..be8a1e7
--- /dev/null
+++ b/leaveschool/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.buildcommons</groupId>
+    <artifactId>spring-cloud-parent</artifactId>
+    <version>Finchley.RELEASE-1.0-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.supwisdom.leaveschool</groupId>
+  <artifactId>leaveschool-parent</artifactId>
+  <version>0.0.1-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <properties>
+    <maven.deploy.skip>true</maven.deploy.skip>
+
+    <infras.version>0.0.1-SNAPSHOT</infras.version>
+  </properties>
+
+  <repositories>
+    <repository>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+      <id>supwisdom</id>
+      <url>http://app.supwisdom.com/nexus/content/groups/public/</url>
+    </repository>
+    <repository>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <id>central</id>
+      <url>http://repo.maven.apache.org/maven2</url>
+    </repository>
+  </repositories>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>com.supwisdom.infras</groupId>
+        <artifactId>infras-bom</artifactId>
+        <version>${infras.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.leaveschool</groupId>
+        <artifactId>leaveschool-common</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>commons-collections</groupId>
+        <artifactId>commons-collections</artifactId>
+        <version>3.2.2</version>
+      </dependency>
+
+      <dependency>
+        <groupId>org.json</groupId>
+        <artifactId>json</artifactId>
+        <version>20180130</version>
+      </dependency>
+
+    </dependencies>
+
+  </dependencyManagement>
+  
+  <modules>
+  <!--
+    <module>foo</module>
+    <module>bar</module>
+    <module>security</module>
+	<module>auth</module>
+	<module>gateway</module>
+-->
+    <module>common</module>
+
+    
+    <module>user</module>
+    
+    
+    <module>client</module>
+  </modules>
+
+</project>
diff --git a/leaveschool/user/pom.xml b/leaveschool/user/pom.xml
new file mode 100644
index 0000000..6dc3755
--- /dev/null
+++ b/leaveschool/user/pom.xml
@@ -0,0 +1,123 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.leaveschool</groupId>
+    <artifactId>leaveschool-parent</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.supwisdom.leaveschool</groupId>
+  <artifactId>leaveschool-user</artifactId>
+  <packaging>jar</packaging>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>com.supwisdom.leaveschool</groupId>
+      <artifactId>leaveschool-common</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <!-- 微服务 健康监控 -->
+    <!-- <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency> -->
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+    
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-mvc</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-object-mapper</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-i18n</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-lang</artifactId>
+    </dependency>
+
+    <!-- 
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-pinyin</artifactId>
+    </dependency>
+     -->
+
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>mysql</groupId>
+      <artifactId>mysql-connector-java</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+    <!-- Test things -->
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/UserApplication.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/UserApplication.java
new file mode 100644
index 0000000..85c853c
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/UserApplication.java
@@ -0,0 +1,16 @@
+package com.supwisdom.leaveschool.user;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import com.supwisdom.infras.data.jpa.EnableInfrasDataJpa;
+
+@SpringBootApplication
+@EnableInfrasDataJpa
+public class UserApplication {
+
+  public static void main(String[] args) {
+    SpringApplication.run(UserApplication.class, args);
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/config/FilterConfig.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/config/FilterConfig.java
new file mode 100644
index 0000000..d029ef7
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/config/FilterConfig.java
@@ -0,0 +1,14 @@
+package com.supwisdom.leaveschool.user.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+public class FilterConfig extends WebMvcConfigurerAdapter{
+
+	 @Override
+	    public void addCorsMappings(CorsRegistry registry) {
+	        registry.addMapping("/**");
+	    }
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/config/PasswordEncoderConfig.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/config/PasswordEncoderConfig.java
new file mode 100644
index 0000000..a92dea4
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/config/PasswordEncoderConfig.java
@@ -0,0 +1,30 @@
+package com.supwisdom.leaveschool.user.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Configuration
+public class PasswordEncoderConfig {
+  
+  private static final Logger logger = LoggerFactory.getLogger(PasswordEncoderConfig.class);
+  
+  @Bean
+  public PasswordEncoder passwordEncoder() {
+    
+    PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+
+    if (passwordEncoder instanceof DelegatingPasswordEncoder) {
+      ((DelegatingPasswordEncoder)passwordEncoder).setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance());
+    }
+
+    logger.debug("PasswordEncoderConfig passwordEncoder is {}", passwordEncoder);
+    return passwordEncoder;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminGroupController.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminGroupController.java
new file mode 100644
index 0000000..f8ab693
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminGroupController.java
@@ -0,0 +1,194 @@
+package com.supwisdom.leaveschool.user.controller.api.admin;
+
+import java.util.HashMap;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.leaveschool.common.controller.api.CrudApiController;
+import com.supwisdom.leaveschool.common.model.PagerRequestModel;
+import com.supwisdom.leaveschool.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.common.model.SuccessResponseModel;
+import com.supwisdom.leaveschool.user.domain.Group;
+import com.supwisdom.leaveschool.user.domain.GroupRole;
+import com.supwisdom.leaveschool.user.domain.UserGroup;
+import com.supwisdom.leaveschool.user.model.GroupRoles;
+import com.supwisdom.leaveschool.user.model.UserGroups;
+import com.supwisdom.leaveschool.user.repository.GroupRepository;
+import com.supwisdom.leaveschool.user.repository.GroupRoleRepository;
+import com.supwisdom.leaveschool.user.repository.UserGroupRepository;
+
+@RestController
+@RequestMapping("/api/v1/admin/groups")
+public class Api1AdminGroupController extends CrudApiController<Group, GroupRepository> {
+
+  @Autowired
+  private GroupRepository groupRepository;
+
+  @Autowired
+  private UserGroupRepository userGroupRepository;
+
+  @Autowired
+  private GroupRoleRepository groupRoleRepository;
+
+  @Override
+  protected GroupRepository getRepository() {
+    return groupRepository;
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/groups/1/users'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/groups/1/users?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/groups/1/users?pageIndex=0&pageSize=20&mapBean[userUsername]=userUsername&mapBean[userName]=userName'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(value = "/{id}/users", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public PagerResponseModel<UserGroup> groupUsers(@PathVariable("id") String id, PagerRequestModel pagerRequestModel) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupRepository.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (pagerRequestModel.getMapBean() == null) {
+      pagerRequestModel.setMapBean(new HashMap<String, Object>());
+    }
+    pagerRequestModel.getMapBean().put("groupId", group.getId());
+
+    Page<UserGroup> page = userGroupRepository.selectUserGroups(pagerRequestModel.getPageIndex(),
+        pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+
+    PagerResponseModel<UserGroup> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/groups/1/users' \
+   * -d '{"groupUsers":[{"userId":"1"},{"userId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param groupUsers
+   * @return
+   */
+  @RequestMapping(value = "/{id}/users", method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public SuccessResponseModel relateUsers(@PathVariable("id") String id, @RequestBody UserGroups groupUsers) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupRepository.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    userGroupRepository.relateGroupUsers(group, groupUsers.getUserGroups());
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.set.success");
+
+    return res;
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/groups/1/roles'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/groups/1/roles?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/groups/1/roles?pageIndex=0&pageSize=20&mapBean[rolecode]=code&mapBean[rolename]=name'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(value = "/{id}/roles", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public PagerResponseModel<GroupRole> groupRoles(@PathVariable("id") String id, PagerRequestModel pagerRequestModel) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupRepository.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (pagerRequestModel.getMapBean() == null) {
+      pagerRequestModel.setMapBean(new HashMap<String, Object>());
+    }
+    pagerRequestModel.getMapBean().put("groupId", group.getId());
+
+    Page<GroupRole> page = groupRoleRepository.selectGroupRoles(pagerRequestModel.getPageIndex(),
+        pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+
+    PagerResponseModel<GroupRole> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/groups/1/roles' \
+   * -d '{"groupRoles":[{"roleId":"1"},{"roleId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param groupUsers
+   * @return
+   */
+  @RequestMapping(value = "/{id}/roles", method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public SuccessResponseModel relateRoles(@PathVariable("id") String id, @RequestBody GroupRoles groupRoles) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Group group = groupRepository.selectById(id);
+
+    if (group == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    groupRoleRepository.relateGroupRoles(group, groupRoles.getGroupRoles());
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.set.success");
+
+    return res;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminPermissionController.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminPermissionController.java
new file mode 100644
index 0000000..69f64c8
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminPermissionController.java
@@ -0,0 +1,114 @@
+package com.supwisdom.leaveschool.user.controller.api.admin;
+
+import java.util.HashMap;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.leaveschool.common.controller.api.CrudApiController;
+import com.supwisdom.leaveschool.common.model.PagerRequestModel;
+import com.supwisdom.leaveschool.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.common.model.SuccessResponseModel;
+import com.supwisdom.leaveschool.user.domain.Permission;
+import com.supwisdom.leaveschool.user.domain.RolePermission;
+import com.supwisdom.leaveschool.user.model.RolePermissions;
+import com.supwisdom.leaveschool.user.repository.PermissionRepository;
+import com.supwisdom.leaveschool.user.repository.RolePermissionRepository;
+
+@RestController
+@RequestMapping("/api/v1/admin/permissions")
+public class Api1AdminPermissionController extends CrudApiController<Permission, PermissionRepository> {
+
+  @Autowired
+  private PermissionRepository permissionRepository;
+
+  @Autowired
+  private RolePermissionRepository permissionRoleRepository;
+
+  @Override
+  protected PermissionRepository getRepository() {
+    return permissionRepository;
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/permissions/1/roles'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/permissions/1/roles?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/permissions/1/roles?pageIndex=0&pageSize=20&mapBean[roleCode]=roleCode&mapBean[roleName]=roleName'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(value = "/{id}/roles", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public PagerResponseModel<RolePermission> permissionRoles(@PathVariable("id") String id, PagerRequestModel pagerRequestModel) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Permission permission = permissionRepository.selectById(id);
+
+    if (permission == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (pagerRequestModel.getMapBean() == null) {
+      pagerRequestModel.setMapBean(new HashMap<String, Object>());
+    }
+    pagerRequestModel.getMapBean().put("permissionId", permission.getId());
+
+    Page<RolePermission> page = permissionRoleRepository.selectRolePermissions(pagerRequestModel.getPageIndex(),
+        pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+
+    PagerResponseModel<RolePermission> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/permissions/1/roles' \
+   * -d '{"rolePermissions":[{"roleId":"1"},{"roleId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param permissionUsers
+   * @return
+   */
+  @RequestMapping(value = "/{id}/roles", method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public SuccessResponseModel relateRoles(@PathVariable("id") String id, @RequestBody RolePermissions rolePermissions) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Permission permission = permissionRepository.selectById(id);
+
+    if (permission == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    permissionRoleRepository.relatePermissionRoles(permission, rolePermissions.getRolePermissions());
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.set.success");
+
+    return res;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminRoleController.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminRoleController.java
new file mode 100644
index 0000000..cbd6845
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminRoleController.java
@@ -0,0 +1,279 @@
+package com.supwisdom.leaveschool.user.controller.api.admin;
+
+import java.util.HashMap;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.leaveschool.common.controller.api.CrudApiController;
+import com.supwisdom.leaveschool.common.model.PagerRequestModel;
+import com.supwisdom.leaveschool.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.common.model.SuccessResponseModel;
+import com.supwisdom.leaveschool.user.domain.GroupRole;
+import com.supwisdom.leaveschool.user.domain.Role;
+import com.supwisdom.leaveschool.user.domain.RolePermission;
+import com.supwisdom.leaveschool.user.domain.UserRole;
+import com.supwisdom.leaveschool.user.model.GroupRoles;
+import com.supwisdom.leaveschool.user.model.RolePermissions;
+import com.supwisdom.leaveschool.user.model.UserRoles;
+import com.supwisdom.leaveschool.user.repository.GroupRoleRepository;
+import com.supwisdom.leaveschool.user.repository.RolePermissionRepository;
+import com.supwisdom.leaveschool.user.repository.RoleRepository;
+import com.supwisdom.leaveschool.user.repository.UserRoleRepository;
+
+@RestController
+@RequestMapping("/api/v1/admin/roles")
+public class Api1AdminRoleController extends CrudApiController<Role, RoleRepository> {
+
+  @Autowired
+  private RoleRepository roleRepository;
+
+  @Override
+  protected RoleRepository getRepository() {
+    return roleRepository;
+  }
+
+  @Autowired
+  private UserRoleRepository userRoleRepository;
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/users'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/users?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/users?pageIndex=0&pageSize=20&mapBean[userName]=userName'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(value = "/{id}/users", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public PagerResponseModel<UserRole> roleUsers(@PathVariable("id") String id, PagerRequestModel pagerRequestModel) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleRepository.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (pagerRequestModel.getMapBean() == null) {
+      pagerRequestModel.setMapBean(new HashMap<String, Object>());
+    }
+    pagerRequestModel.getMapBean().put("roleId", role.getId());
+
+    Page<UserRole> page = userRoleRepository.selectUserRoles(pagerRequestModel.getPageIndex(),
+        pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+
+    PagerResponseModel<UserRole> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/users' \
+   * -d '{"userRoles":[{"userId":"test001"},{"userId":"test002"}]}'
+   * 
+   * 
+   * @param id
+   * @param userRoles
+   * @return
+   */
+  @RequestMapping(value = "/{id}/users", method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public SuccessResponseModel relateUsers(@PathVariable("id") String id, @RequestBody UserRoles userRoles) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleRepository.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    userRoleRepository.relateRoleUsers(role, userRoles.getUserRoles());
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.set.success");
+
+    return res;
+  }
+
+  
+  
+
+  @Autowired
+  private GroupRoleRepository groupRoleRepository;
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/groups'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/groups?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/groups?pageIndex=0&pageSize=20&mapBean[groupName]=groupName'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(value = "/{id}/groups", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public PagerResponseModel<GroupRole> roleGroups(@PathVariable("id") String id, PagerRequestModel pagerRequestModel) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleRepository.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (pagerRequestModel.getMapBean() == null) {
+      pagerRequestModel.setMapBean(new HashMap<String, Object>());
+    }
+    pagerRequestModel.getMapBean().put("roleId", role.getId());
+
+    Page<GroupRole> page = groupRoleRepository.selectGroupRoles(pagerRequestModel.getPageIndex(),
+        pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+
+    PagerResponseModel<GroupRole> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/groups' \
+   * -d '{"userRoles":[{"groupId":"1"},{"groupId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param groupRoles
+   * @return
+   */
+  @RequestMapping(value = "/{id}/groups", method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public SuccessResponseModel relateGroups(@PathVariable("id") String id, @RequestBody GroupRoles groupRoles) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleRepository.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    groupRoleRepository.relateRoleGroups(role, groupRoles.getGroupRoles());
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.set.success");
+
+    return res;
+  }
+
+
+
+  @Autowired
+  private RolePermissionRepository rolePermissionRepository;
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/permissions'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/permissions?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/permissions?pageIndex=0&pageSize=20&mapBean[permissionCode]=permissionCode'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(value = "/{id}/permissions", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public PagerResponseModel<RolePermission> rolePermissions(@PathVariable("id") String id, PagerRequestModel pagerRequestModel) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleRepository.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (pagerRequestModel.getMapBean() == null) {
+      pagerRequestModel.setMapBean(new HashMap<String, Object>());
+    }
+    pagerRequestModel.getMapBean().put("roleId", role.getId());
+
+    Page<RolePermission> page = rolePermissionRepository.selectRolePermissions(pagerRequestModel.getPageIndex(),
+        pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+
+    PagerResponseModel<RolePermission> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/roles/1/permissons' \
+   * -d '{"userRoles":[{"permissionId":"1"},{"permissionId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param groupRoles
+   * @return
+   */
+  @RequestMapping(value = "/{id}/permissons", method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public SuccessResponseModel relatePermissions(@PathVariable("id") String id, @RequestBody RolePermissions rolePermissions) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    Role role = roleRepository.selectById(id);
+
+    if (role == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    rolePermissionRepository.relateRolePermissions(role, rolePermissions.getRolePermissions());
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.set.success");
+
+    return res;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminUserController.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminUserController.java
new file mode 100644
index 0000000..7b53c29
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/admin/Api1AdminUserController.java
@@ -0,0 +1,568 @@
+package com.supwisdom.leaveschool.user.controller.api.admin;
+
+import java.util.HashMap;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.leaveschool.common.controller.api.CrudApiController;
+import com.supwisdom.leaveschool.common.model.PagerRequestModel;
+import com.supwisdom.leaveschool.common.model.PagerResponseModel;
+import com.supwisdom.leaveschool.common.model.SuccessResponseModel;
+import com.supwisdom.leaveschool.common.util.DomainUtils;
+import com.supwisdom.leaveschool.user.domain.UserGroup;
+import com.supwisdom.leaveschool.user.domain.User;
+import com.supwisdom.leaveschool.user.domain.UserRole;
+import com.supwisdom.leaveschool.user.model.UserGroups;
+import com.supwisdom.leaveschool.user.model.UserRoles;
+import com.supwisdom.leaveschool.user.repository.UserGroupRepository;
+import com.supwisdom.leaveschool.user.repository.UserRepository;
+import com.supwisdom.leaveschool.user.repository.UserRoleRepository;
+
+@RestController
+@RequestMapping("/api/v1/admin/users")
+public class Api1AdminUserController extends CrudApiController<User, UserRepository> {
+
+  @Autowired
+  private UserRepository userRepository;
+
+  @Autowired
+  private UserGroupRepository userGroupRepository;
+
+  @Autowired
+  private UserRoleRepository userRoleRepository;
+  
+  @Autowired
+  private PasswordEncoder passwordEncoder;
+
+  @Override
+  protected UserRepository getRepository() {
+
+    return userRepository;
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users?pageIndex=0&pageSize=20&mapBean[username]=username&mapBean[name]=name&mapBean[status]=1'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users?pageIndex=0&pageSize=20&mapBean[username]=username&mapBean[name]=name&mapBean[status]=0'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "pageIndex":0,
+   *   "pageSize":20,
+   *   "mapBean":null,
+   *   "pageCount":1,
+   *   "recordCount":1,
+   *   "items":[
+   *     {
+   *       "id":"ff80808164feb8990164feba0de50000",
+   *       "companyId":"1",
+   *       "deleted":false,
+   *       "addAccount":"user","addTime":"2018-08-03T07:39:23.000+0000",
+   *       "editAccount":null,"editTime":null,
+   *       "deleteAccount":null,"deleteTime":null,
+   *       "username":"test001",
+   *       "password":"test001",
+   *       "enabled":true,
+   *       "accountNonExpired":true,
+   *       "accountNonLocked":true,
+   *       "credentialsNonExpired":true,
+   *       "name":"测试001",
+   *       "status":"1",
+   *       "mobile":null,
+   *       "email":null
+   *     }
+   *   ]
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/users"
+   * }
+   * </pre>
+   * 
+   * @param pagerRequestModel
+   * @return
+   */
+  @Override
+  @GetMapping(produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public PagerResponseModel<User> list(PagerRequestModel pagerRequestModel) {
+
+    Page<User> page = userRepository.selectPageList(pagerRequestModel.getPageIndex(), pagerRequestModel.getPageSize(),
+        pagerRequestModel.getMapBean());
+
+    PagerResponseModel<User> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setCurrentItemCount(page.getNumberOfElements());
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "id":"ff80808164feb8990164feba0de50000",
+   *   "companyId":"1",
+   *   "deleted":false,
+   *   "addAccount":"user","addTime":"2018-08-03T07:39:23.000+0000",
+   *   "editAccount":null,"editTime":null,
+   *   "deleteAccount":null,"deleteTime":null,
+   *   "username":"test001",
+   *   "password":"test001",
+   *   "enabled":true,
+   *   "accountNonExpired":true,
+   *   "accountNonLocked":true,
+   *   "credentialsNonExpired":true,
+   *   "name":"测试001",
+   *   "status":"1",
+   *   "mobile":null,
+   *   "email":null
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:43:26.080+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/users/ff80808164fecf640164fed269480000"
+   * }
+   * </pre>
+   * 
+   * response error 500:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:44:07.963+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.get.domain.not.exist",
+   *   "path":"/api/v1/admin/users/1"
+   * }
+   * </pre>
+   * 
+   * @param id
+   * @return
+   */
+  @Override
+  @GetMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public User get(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    User user = userRepository.selectById(id);
+
+    if (user == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    return user;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users' \
+   * -d '{"username":"test001","password":"test001","enabled":true,"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"name":"测试001","status":"1"}'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "success":"info.create.success"
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/users"
+   * }
+   * </pre>
+   * 
+   * response error: // FIXME: save error
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:45:43.436+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"org.springframework.dao.DataIntegrityViolationException",
+   *   "message":"could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
+   *   "path":"/api/v1/admin/users"
+   * }
+   * </pre>
+   * 
+   * @param user
+   * @return
+   */
+  @Override
+  @PostMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public SuccessResponseModel create(@RequestBody User user) {
+    
+    // FIXME: 验证数据有效性
+    
+    if (user.getPassword() !=null && user.getPassword().length() > 0 && !user.getPassword().startsWith("{")) {
+      user.setPassword(passwordEncoder.encode(user.getPassword()));
+    }
+
+    @SuppressWarnings("unused")
+    User ret = userRepository.insert(user);
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.create.success");
+
+    return res;
+  }
+
+  /**
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users' \
+   * -d '{"id":"1","status":"0"}'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "success":"info.update.success"
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/users"
+   * }
+   * </pre>
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users' \
+   * -d '{"status":"0"}'
+   * 
+   * response error:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:50:52.327+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.update.id.must.not.empty",
+   *   "path":"/api/v1/admin/users"
+   * }
+   * </pre>
+   * 
+   * curl -i -s -X PUT -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users' \
+   * -d '{"id":"1","status":"0"}'
+   * 
+   * response error:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:48:24.774+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.update.domain.not.exist",
+   *   "path":"/api/v1/admin/users"
+   * }
+   * </pre>
+   * 
+   * @param user
+   * @return
+   */
+  @Override
+  @PutMapping(consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public SuccessResponseModel update(@RequestBody User user) {
+
+    if (user.getId() == null || user.getId().length() == 0) {
+      throw new RuntimeException("exception.update.id.must.not.empty"); // FIXME: RestException
+    }
+
+    User tmp = userRepository.selectById(user.getId());
+    if (tmp == null) {
+      throw new RuntimeException("exception.update.domain.not.exist"); // FIXME: RestException
+    }
+    
+    if (user.getPassword() !=null && user.getPassword().length() > 0 && !user.getPassword().startsWith("{")) {
+      user.setPassword(passwordEncoder.encode(user.getPassword()));
+    }
+
+    tmp = DomainUtils.merge(user, tmp);
+
+    @SuppressWarnings("unused")
+    User ret = userRepository.update(tmp);
+    userRepository.flush();
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.update.success");
+
+    return res;
+  }
+
+  /**
+   * 
+   * curl -i -s -X DELETE -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "success":"info.delete.success"
+   * }
+   * </pre>
+   * 
+   * response error 401:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:48:25.777+0000",
+   *   "status":401,
+   *   "error":"Http Status 401",
+   *   "message":"Unauthorized",
+   *   "path":"/api/v1/admin/users/1"
+   * }
+   * </pre>
+   * 
+   * response error 500:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T08:03:16.364+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.delete.domain.not.exist",
+   *   "path":"/api/v1/admin/users/1"
+   * }
+   * </pre>
+   * 
+   * @param id
+   * @return
+   */
+  @Override
+  @DeleteMapping(path = "/{id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public SuccessResponseModel delete(@PathVariable("id") String id) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.delete.id.must.not.empty"); // FIXME: RestException
+    }
+
+    User tmp = userRepository.selectById(id);
+    if (tmp == null) {
+      throw new RuntimeException("exception.delete.domain.not.exist"); // FIXME: RestException
+    }
+
+    userRepository.delete(tmp);
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.delete.success");
+
+    return res;
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1/groups'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1/groups?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1/groups?pageIndex=0&pageSize=20&mapBean[groupCode]=groupCode&mapBean[groupName]=groupName'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(value = "/{id}/groups", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public PagerResponseModel<UserGroup> userGroups(@PathVariable("id") String id, PagerRequestModel pagerRequestModel) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    User user = userRepository.selectById(id);
+
+    if (user == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (pagerRequestModel.getMapBean() == null) {
+      pagerRequestModel.setMapBean(new HashMap<String, Object>());
+    }
+    pagerRequestModel.getMapBean().put("userId", user.getId());
+
+    Page<UserGroup> page = userGroupRepository.selectUserGroups(pagerRequestModel.getPageIndex(),
+        pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+
+    PagerResponseModel<UserGroup> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1/groups' \
+   * -d '{"groupUsers":[{"groupId":"1"},{"groupId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param userUsers
+   * @return
+   */
+  @RequestMapping(value = "/{id}/groups", method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public SuccessResponseModel relateGroups(@PathVariable("id") String id, @RequestBody UserGroups groupUsers) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    User user = userRepository.selectById(id);
+
+    if (user == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    userGroupRepository.relateUserGroups(user, groupUsers.getUserGroups());
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.set.success");
+
+    return res;
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1/roles'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1/roles?pageIndex=2&pageSize=50'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1/roles?pageIndex=0&pageSize=20&mapBean[roleCode]=roleCode&mapBean[roleName]=roleName'
+   * 
+   * 
+   * 
+   * @param id
+   * @param pagerRequestModel
+   * @return
+   */
+  @RequestMapping(value = "/{id}/roles", method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public PagerResponseModel<UserRole> userRoles(@PathVariable("id") String id, PagerRequestModel pagerRequestModel) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    User user = userRepository.selectById(id);
+
+    if (user == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    if (pagerRequestModel.getMapBean() == null) {
+      pagerRequestModel.setMapBean(new HashMap<String, Object>());
+    }
+    pagerRequestModel.getMapBean().put("userId", user.getId());
+
+    Page<UserRole> page = userRoleRepository.selectUserRoles(pagerRequestModel.getPageIndex(),
+        pagerRequestModel.getPageSize(), pagerRequestModel.getMapBean());
+
+    PagerResponseModel<UserRole> pagerResponseModel = PagerResponseModel.of(pagerRequestModel);
+    pagerResponseModel.setPageCount(page.getTotalPages());
+    pagerResponseModel.setRecordCount(page.getTotalElements());
+    pagerResponseModel.setItems(page.getContent());
+
+    return pagerResponseModel;
+  }
+
+  /**
+   * 
+   * curl -i -s -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:10010/api/v1/admin/users/1/roles' \
+   * -d '{"userRoles":[{"roleId":"1"},{"roleId":"2"}]}'
+   * 
+   * 
+   * @param id
+   * @param userUsers
+   * @return
+   */
+  @RequestMapping(value = "/{id}/roles", method = RequestMethod.POST, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseBody
+  public SuccessResponseModel relateRoles(@PathVariable("id") String id, @RequestBody UserRoles userRoles) {
+
+    if (id == null || id.length() == 0) {
+      throw new RuntimeException("exception.get.id.must.not.empty"); // FIXME: RestException
+    }
+
+    User user = userRepository.selectById(id);
+
+    if (user == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+
+    userRoleRepository.relateUserRoles(user, userRoles.getUserRoles());
+
+    SuccessResponseModel res = new SuccessResponseModel();
+    res.setSuccess("info.set.success");
+
+    return res;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/demo/ApiDemoUserController.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/demo/ApiDemoUserController.java
new file mode 100644
index 0000000..8d776f4
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/demo/ApiDemoUserController.java
@@ -0,0 +1,34 @@
+package com.supwisdom.leaveschool.user.controller.api.demo;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/demo/users")
+public class ApiDemoUserController {
+  
+  private static final Logger logger = LoggerFactory.getLogger(ApiDemoUserController.class);
+
+  /**
+   * curl -i -s -X GET -H 'Remote_User:admin' -H 'Accept:application/json' 'http://localhost:10010/api/demo/users/greeting/abc'
+   *
+   * @param name
+   * @return
+   */
+  @GetMapping(path = "/greeting/{name}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  public Map<String, Object> greeting(@PathVariable("name") String name) {
+    logger.debug(name);
+    Map<String, Object> result = new HashMap<String, Object>();
+    result.put("message", "Good " + name);
+    return result;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/security/Api1SecurityUserController.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/security/Api1SecurityUserController.java
new file mode 100644
index 0000000..ba76b0b
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/controller/api/security/Api1SecurityUserController.java
@@ -0,0 +1,185 @@
+package com.supwisdom.leaveschool.user.controller.api.security;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.leaveschool.user.domain.Permission;
+import com.supwisdom.leaveschool.user.domain.Role;
+import com.supwisdom.leaveschool.user.domain.User;
+import com.supwisdom.leaveschool.user.model.SecurityUser;
+import com.supwisdom.leaveschool.user.repository.PermissionRepository;
+import com.supwisdom.leaveschool.user.repository.RoleRepository;
+import com.supwisdom.leaveschool.user.repository.UserRepository;
+
+@RestController
+@RequestMapping("/api/v1/security/users")
+public class Api1SecurityUserController {
+
+  @Autowired
+  private UserRepository userRepository;
+
+  @Autowired
+  private RoleRepository roleRepository;
+
+  @Autowired
+  private PermissionRepository permissionRepository;
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/security/users/test001'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "user":{
+   *     "id":"ff80808164feb8990164feba0de50000",
+   *     "companyId":"1",
+   *     "deleted":false,
+   *     "addAccount":"user","addTime":"2018-08-03T07:39:23.000+0000",
+   *     "editAccount":null,"editTime":null,
+   *     "deleteAccount":null,"deleteTime":null,
+   *     "username":"test001",
+   *     "password":"test001",
+   *     "enabled":true,
+   *     "accountNonExpired":true,
+   *     "accountNonLocked":true,
+   *     "credentialsNonExpired":true,
+   *     "name":"测试001",
+   *     "status":"1",
+   *     "mobile":null,
+   *     "email":null
+   *   },
+   *   "roles":[],
+   *   "permissions":null
+   * }
+   * </pre>
+   * 
+   * response error 500:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:44:07.963+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.get.domain.not.exist",
+   *   "path":"/api/v1/security/users/test000"
+   * }
+   * </pre>
+   * 
+   */
+  @GetMapping(path = "/{username}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public SecurityUser loadUserByUsername(@PathVariable("username") String username) {
+    
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty"); // FIXME: RestException
+    }
+
+    User user = userRepository.selectByUsername(username);
+
+    if (user == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    
+    List<Role> roles = roleRepository.selectByUsername(username);
+    
+    SecurityUser securityUser = new SecurityUser();
+    securityUser.setUser(user);
+    securityUser.setRoles(roles);
+
+    return securityUser;
+  }
+
+  /**
+   * 
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/security/users/test001/app001'
+   * curl -i -s -X GET -H 'Accept:application/json' 'http://localhost:10010/api/v1/security/users/test001/app001?type=2'
+   * 
+   * response success:
+   * 
+   * <pre>
+   * {
+   *   "user":{
+   *     "id":"ff80808164feb8990164feba0de50000",
+   *     "companyId":"1",
+   *     "deleted":false,
+   *     "addAccount":"user","addTime":"2018-08-03T07:39:23.000+0000",
+   *     "editAccount":null,"editTime":null,
+   *     "deleteAccount":null,"deleteTime":null,
+   *     "username":"test001",
+   *     "password":"test001",
+   *     "enabled":true,
+   *     "accountNonExpired":true,
+   *     "accountNonLocked":true,
+   *     "credentialsNonExpired":true,
+   *     "name":"测试001",
+   *     "status":"1",
+   *     "mobile":null,
+   *     "email":null
+   *   },
+   *   "roles":[],
+   *   "permissions":[]
+   * }
+   * </pre>
+   * 
+   * response error 500:
+   * 
+   * <pre>
+   * {
+   *   "timestamp":"2018-08-03T07:44:07.963+0000",
+   *   "status":500,
+   *   "error":"Internal Server Error",
+   *   "exception":"java.lang.RuntimeException",
+   *   "message":"exception.get.domain.not.exist",
+   *   "path":"/api/v1/security/users/test000/app001"
+   * }
+   * </pre>
+   * 
+   * @param username
+   * @param applicationCode
+   * @param type 权限类型,1 应用,2 页面,3 操作
+   * @return
+   */
+  @GetMapping(path = "/{username}/{applicationCode}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public SecurityUser loadPermissionsByUsernameAppcode(
+      @PathVariable("username") String username, 
+      @PathVariable("applicationCode") String applicationCode, 
+      @RequestParam(value = "type", required = false) String type) {
+    
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty"); // FIXME: RestException
+    }
+
+    User user = userRepository.selectByUsername(username);
+
+    if (user == null) {
+      throw new RuntimeException("exception.get.domain.not.exist"); // FIXME: RestException
+    }
+    
+    List<Role> roles = roleRepository.selectByUsername(username);
+    
+    List<Permission> permissions = permissionRepository.selectByUsername(username, applicationCode, type);
+    
+    SecurityUser securityUser = new SecurityUser();
+    securityUser.setUser(user);
+    securityUser.setRoles(roles);
+    securityUser.setPermissions(permissions);
+    
+    return securityUser;
+  }
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Group.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Group.java
new file mode 100644
index 0000000..cc3a73d
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Group.java
@@ -0,0 +1,74 @@
+package com.supwisdom.leaveschool.user.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Entity(name = "Group_")
+@Table(name = "TB_U_GROUP")
+public class Group extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 4260326816456622523L;
+
+  /**
+   * 代码
+   */
+  @Column(name = "CODE")
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO")
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  private String status;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/GroupRole.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/GroupRole.java
new file mode 100644
index 0000000..a2f4ac1
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/GroupRole.java
@@ -0,0 +1,46 @@
+package com.supwisdom.leaveschool.user.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Entity
+@Table(name = "TB_U_GROUP_ROLE")
+public class GroupRole extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3141266845902556712L;
+
+  /**
+   * 用户组ID
+   */
+  @Column(name = "GROUP_ID")
+  private String groupId;
+
+  /**
+   * 角色ID
+   */
+  @Column(name = "ROLE_ID")
+  private String roleId;
+
+  public String getGroupId() {
+    return groupId;
+  }
+
+  public void setGroupId(String groupId) {
+    this.groupId = groupId;
+  }
+
+  public String getRoleId() {
+    return roleId;
+  }
+
+  public void setRoleId(String roleId) {
+    this.roleId = roleId;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Permission.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Permission.java
new file mode 100644
index 0000000..30911e8
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Permission.java
@@ -0,0 +1,172 @@
+package com.supwisdom.leaveschool.user.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Entity
+@Table(name = "TB_U_PERMISSION")
+public class Permission extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8834200833972243635L;
+
+  /**
+   * 代码
+   */
+  @Column(name = "CODE")
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO")
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  private String status;
+
+  /**
+   * 类型(1 应用,2 页面,3 操作)
+   */
+  @Column(name = "TYPE_")
+  private String type;
+
+  /**
+   * URL地址
+   */
+  @Column(name = "URL")
+  private String url;
+
+  /**
+   * 父级ID
+   */
+  @Column(name = "PARENT_ID")
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  @Column(name = "ORDER_")
+  private String order;
+
+  /**
+   * 层次
+   */
+  @Column(name = "LEVEL_")
+  private String level;
+
+  /**
+   * 左序
+   */
+  @Column(name = "LFT")
+  private int lft;
+
+  /**
+   * 右序
+   */
+  @Column(name = "RGT")
+  private int rgt;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public void setUrl(String url) {
+    this.url = url;
+  }
+
+  public String getParentId() {
+    return parentId;
+  }
+
+  public void setParentId(String parentId) {
+    this.parentId = parentId;
+  }
+
+  public String getOrder() {
+    return order;
+  }
+
+  public void setOrder(String order) {
+    this.order = order;
+  }
+
+  public String getLevel() {
+    return level;
+  }
+
+  public void setLevel(String level) {
+    this.level = level;
+  }
+
+  public int getLft() {
+    return lft;
+  }
+
+  public void setLft(int lft) {
+    this.lft = lft;
+  }
+
+  public int getRgt() {
+    return rgt;
+  }
+
+  public void setRgt(int rgt) {
+    this.rgt = rgt;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Role.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Role.java
new file mode 100644
index 0000000..5d2d9fb
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/Role.java
@@ -0,0 +1,74 @@
+package com.supwisdom.leaveschool.user.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Entity
+@Table(name = "TB_U_ROLE")
+public class Role extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5470129732727732514L;
+
+  /**
+   * 代码
+   */
+  @Column(name = "CODE")
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Column(name = "NAME")
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Column(name = "MEMO")
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  private String status;
+
+  public String getCode() {
+    return code;
+  }
+
+  public void setCode(String code) {
+    this.code = code;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getMemo() {
+    return memo;
+  }
+
+  public void setMemo(String memo) {
+    this.memo = memo;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/RolePermission.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/RolePermission.java
new file mode 100644
index 0000000..ab1fd86
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/RolePermission.java
@@ -0,0 +1,46 @@
+package com.supwisdom.leaveschool.user.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Entity
+@Table(name = "TB_U_ROLE_PERMISSION")
+public class RolePermission extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5293251541687343949L;
+
+  /**
+   * 角色ID
+   */
+  @Column(name = "ROLE_ID")
+  private String roleId;
+
+  /**
+   * 权限ID
+   */
+  @Column(name = "PERMISSION_ID")
+  private String permissionId;
+
+  public String getRoleId() {
+    return roleId;
+  }
+
+  public void setRoleId(String roleId) {
+    this.roleId = roleId;
+  }
+
+  public String getPermissionId() {
+    return permissionId;
+  }
+
+  public void setPermissionId(String permissionId) {
+    this.permissionId = permissionId;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/User.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/User.java
new file mode 100644
index 0000000..db696e8
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/User.java
@@ -0,0 +1,154 @@
+package com.supwisdom.leaveschool.user.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Entity
+@Table(name = "TB_U_USER")
+public class User extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 7955624268022038897L;
+
+  /**
+   * 用户名
+   */
+  @Column(name = "USERNAME", unique = true)
+  private String username;
+  
+  /**
+   * 密码
+   */
+  @Column(name = "PASSWORD")
+  private String password;
+
+  /**
+   * 是否可用,1 可用,0 不可用,默认:1
+   */
+  @Column(name = "ENABLED")
+  private Boolean enabled;
+  /**
+   * 账号未过期,1 未过期,0 过期,默认:1
+   */
+  @Column(name = "ACCOUNT_NON_EXPIRED")
+  private Boolean accountNonExpired;
+  /**
+   * 账号未锁定,1 未锁定,0 锁定,默认:1
+   */
+  @Column(name = "ACCOUNT_NON_LOCKED")
+  private Boolean accountNonLocked;
+  /**
+   * 密码未过期,1 未过期,0 过期,默认:1
+   */
+  @Column(name = "CREDENTIALS_NON_EXPIRED")
+  private Boolean credentialsNonExpired;
+
+  /**
+   * 姓名
+   */
+  @Column(name = "NAME")
+  private String name;
+  
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Column(name = "STATUS")
+  private String status;
+
+  /**
+   * 登录手机
+   */
+  @Column(name = "MOBILE")
+  private String mobile;
+  /**
+   * 登录邮箱
+   */
+  @Column(name = "EMAIL")
+  private String email;
+
+  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 Boolean getEnabled() {
+    return enabled;
+  }
+
+  public void setEnabled(Boolean enabled) {
+    this.enabled = enabled;
+  }
+
+  public Boolean getAccountNonExpired() {
+    return accountNonExpired;
+  }
+
+  public void setAccountNonExpired(Boolean accountNonExpired) {
+    this.accountNonExpired = accountNonExpired;
+  }
+
+  public Boolean getAccountNonLocked() {
+    return accountNonLocked;
+  }
+
+  public void setAccountNonLocked(Boolean accountNonLocked) {
+    this.accountNonLocked = accountNonLocked;
+  }
+
+  public Boolean getCredentialsNonExpired() {
+    return credentialsNonExpired;
+  }
+
+  public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
+    this.credentialsNonExpired = credentialsNonExpired;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getStatus() {
+    return status;
+  }
+
+  public void setStatus(String status) {
+    this.status = status;
+  }
+
+  public String getMobile() {
+    return mobile;
+  }
+
+  public void setMobile(String mobile) {
+    this.mobile = mobile;
+  }
+
+  public String getEmail() {
+    return email;
+  }
+
+  public void setEmail(String email) {
+    this.email = email;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/UserGroup.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/UserGroup.java
new file mode 100644
index 0000000..4911bb7
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/UserGroup.java
@@ -0,0 +1,46 @@
+package com.supwisdom.leaveschool.user.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Entity
+@Table(name = "TB_U_USER_GROUP")
+public class UserGroup extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4239845385965871983L;
+
+  /**
+   * 用户ID
+   */
+  @Column(name = "USER_ID")
+  private String userId;
+
+  /**
+   * 用户组ID
+   */
+  @Column(name = "GROUP_ID")
+  private String groupId;
+
+  public String getUserId() {
+    return userId;
+  }
+
+  public void setUserId(String userId) {
+    this.userId = userId;
+  }
+
+  public String getGroupId() {
+    return groupId;
+  }
+
+  public void setGroupId(String groupId) {
+    this.groupId = groupId;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/UserRole.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/UserRole.java
new file mode 100644
index 0000000..71ae23a
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/domain/UserRole.java
@@ -0,0 +1,46 @@
+package com.supwisdom.leaveschool.user.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+import com.supwisdom.leaveschool.common.domain.ABaseDomain;
+
+@Entity
+@Table(name = "TB_U_USER_ROLE")
+public class UserRole extends ABaseDomain {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6158470486381850942L;
+
+  /**
+   * 用户ID
+   */
+  @Column(name = "USER_ID")
+  private String userId;
+
+  /**
+   * 角色ID
+   */
+  @Column(name = "ROLE_ID")
+  private String roleId;
+
+  public String getUserId() {
+    return userId;
+  }
+
+  public void setUserId(String userId) {
+    this.userId = userId;
+  }
+
+  public String getRoleId() {
+    return roleId;
+  }
+
+  public void setRoleId(String roleId) {
+    this.roleId = roleId;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/GroupRoles.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/GroupRoles.java
new file mode 100644
index 0000000..14b8565
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/GroupRoles.java
@@ -0,0 +1,25 @@
+package com.supwisdom.leaveschool.user.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.supwisdom.leaveschool.user.domain.GroupRole;
+
+public class GroupRoles implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -5839141864223755990L;
+
+  private List<GroupRole> groupRoles;
+
+  public List<GroupRole> getGroupRoles() {
+    return groupRoles;
+  }
+
+  public void setGroupRoles(List<GroupRole> groupRoles) {
+    this.groupRoles = groupRoles;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/RolePermissions.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/RolePermissions.java
new file mode 100644
index 0000000..72cfb4d
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/RolePermissions.java
@@ -0,0 +1,25 @@
+package com.supwisdom.leaveschool.user.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.supwisdom.leaveschool.user.domain.RolePermission;
+
+public class RolePermissions implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -2452925034310554167L;
+  
+  private List<RolePermission> rolePermissions;
+
+  public List<RolePermission> getRolePermissions() {
+    return rolePermissions;
+  }
+
+  public void setRolePermissions(List<RolePermission> rolePermissions) {
+    this.rolePermissions = rolePermissions;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/SecurityUser.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/SecurityUser.java
new file mode 100644
index 0000000..01611b2
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/SecurityUser.java
@@ -0,0 +1,47 @@
+package com.supwisdom.leaveschool.user.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.supwisdom.leaveschool.user.domain.Permission;
+import com.supwisdom.leaveschool.user.domain.Role;
+import com.supwisdom.leaveschool.user.domain.User;
+
+public class SecurityUser implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -1068580365294859071L;
+
+  private User user;
+
+  private List<Role> roles;
+
+  private List<Permission> permissions;
+
+  public User getUser() {
+    return user;
+  }
+
+  public void setUser(User user) {
+    this.user = user;
+  }
+
+  public List<Role> getRoles() {
+    return roles;
+  }
+
+  public void setRoles(List<Role> roles) {
+    this.roles = roles;
+  }
+
+  public List<Permission> getPermissions() {
+    return permissions;
+  }
+
+  public void setPermissions(List<Permission> permissions) {
+    this.permissions = permissions;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/UserGroups.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/UserGroups.java
new file mode 100644
index 0000000..8736c15
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/UserGroups.java
@@ -0,0 +1,25 @@
+package com.supwisdom.leaveschool.user.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.supwisdom.leaveschool.user.domain.UserGroup;
+
+public class UserGroups implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 8195230337021165172L;
+
+  private List<UserGroup> userGroups;
+
+  public List<UserGroup> getUserGroups() {
+    return userGroups;
+  }
+
+  public void setUserGroups(List<UserGroup> userGroups) {
+    this.userGroups = userGroups;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/UserRoles.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/UserRoles.java
new file mode 100644
index 0000000..e17bd2f
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/model/UserRoles.java
@@ -0,0 +1,25 @@
+package com.supwisdom.leaveschool.user.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.supwisdom.leaveschool.user.domain.UserRole;
+
+public class UserRoles implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 5444997122746118950L;
+
+  private List<UserRole> userRoles;
+
+  public List<UserRole> getUserRoles() {
+    return userRoles;
+  }
+
+  public void setUserRoles(List<UserRole> userRoles) {
+    this.userRoles = userRoles;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/GroupRepository.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/GroupRepository.java
new file mode 100644
index 0000000..14f3ea5
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/GroupRepository.java
@@ -0,0 +1,41 @@
+package com.supwisdom.leaveschool.user.repository;
+
+import java.util.Map;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.MapBeanUtils;
+import com.supwisdom.leaveschool.user.domain.Group;
+
+@Repository
+public interface GroupRepository extends BaseJpaRepository<Group> {
+
+  public default Page<Group> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    Group probe = new Group();
+    if (mapBean != null) {
+      probe.setCode(MapBeanUtils.getString(mapBean, "code"));
+      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+      probe.setMemo(MapBeanUtils.getString(mapBean, "memo"));
+      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+    }
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("memo", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<Group> example = Example.of(probe, matcher);
+    
+    Page<Group> page = this.findAll(example, pageRequest);
+    
+    return page;
+  }
+  
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/GroupRoleRepository.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/GroupRoleRepository.java
new file mode 100644
index 0000000..253f4fe
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/GroupRoleRepository.java
@@ -0,0 +1,149 @@
+package com.supwisdom.leaveschool.user.repository;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.MapBeanUtils;
+import com.supwisdom.leaveschool.user.domain.Group;
+import com.supwisdom.leaveschool.user.domain.GroupRole;
+import com.supwisdom.leaveschool.user.domain.Role;
+
+@Repository
+public interface GroupRoleRepository extends BaseJpaRepository<GroupRole> {
+
+  public default Page<GroupRole> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    GroupRole probe = new GroupRole();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    Page<GroupRole> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<GroupRole> selectGroupRoles(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    GroupRole probe = new GroupRole();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<GroupRole> page = this.findAll(example, pageRequest);  // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public default void relateGroupRoles(Group group, List<GroupRole> groupRoles) {
+
+    List<GroupRole> existGroupRoles = this.selectListByGroupId(group.getId());
+
+    Map<String, GroupRole> existMapGroupRoles = new LinkedHashMap<String, GroupRole>();
+    for (GroupRole groupRole : existGroupRoles) {
+      String k = String.format("%s", groupRole.getRoleId());
+      existMapGroupRoles.put(k, groupRole);
+    }
+
+    for (GroupRole groupRole : groupRoles) {
+      String k = String.format("%s", groupRole.getRoleId());
+
+      if (existMapGroupRoles.containsKey(k)) {
+        existMapGroupRoles.remove(k);
+      } else {
+        groupRole.setCompanyId(group.getCompanyId());
+        groupRole.setGroupId(group.getId());
+
+        this.insert(groupRole);
+      }
+    }
+
+    for (GroupRole groupRole : existMapGroupRoles.values()) {
+      this.deleteById(groupRole.getId());
+    }
+  }
+
+  public default List<GroupRole> selectListByGroupId(String groupId) {
+
+    GroupRole probe = new GroupRole();
+    probe.setGroupId(groupId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("groupId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    List<GroupRole> groupRoles = this.findAll(example);
+
+    return groupRoles;
+  }
+
+
+  public default void relateRoleGroups(Role role, List<GroupRole> groupRoles) {
+
+    List<GroupRole> existRoleGroups = this.selectListByRoleId(role.getCode());
+
+    Map<String, GroupRole> existMapRoleGroups = new LinkedHashMap<String, GroupRole>();
+    for (GroupRole groupRole : existRoleGroups) {
+      String k = String.format("%s", groupRole.getGroupId());
+      existMapRoleGroups.put(k, groupRole);
+    }
+
+    for (GroupRole groupRole : groupRoles) {
+      String k = String.format("%s", groupRole.getGroupId());
+
+      if (existMapRoleGroups.containsKey(k)) {
+        existMapRoleGroups.remove(k);
+      } else {
+        groupRole.setCompanyId(role.getCompanyId());
+        groupRole.setRoleId(role.getId());
+
+        this.insert(groupRole);
+      }
+    }
+
+    for (GroupRole groupRole : existMapRoleGroups.values()) {
+      this.deleteById(groupRole.getId());
+    }
+  }
+
+  public default List<GroupRole> selectListByRoleId(String roleId) {
+
+    GroupRole probe = new GroupRole();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<GroupRole> example = Example.of(probe, matcher);
+
+    List<GroupRole> groupRoles = this.findAll(example);
+
+    return groupRoles;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/PermissionRepository.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/PermissionRepository.java
new file mode 100644
index 0000000..49825f6
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/PermissionRepository.java
@@ -0,0 +1,114 @@
+package com.supwisdom.leaveschool.user.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.MapBeanUtils;
+import com.supwisdom.leaveschool.user.domain.Permission;
+
+@Repository
+public interface PermissionRepository extends BaseJpaRepository<Permission> {
+
+  public default Page<Permission> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    Permission probe = new Permission();
+    if (mapBean != null) {
+      probe.setCode(MapBeanUtils.getString(mapBean, "code"));
+      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+      probe.setMemo(MapBeanUtils.getString(mapBean, "memo"));
+      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+    }
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("memo", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<Permission> example = Example.of(probe, matcher);
+    
+    Page<Permission> page = this.findAll(example, pageRequest);
+    
+    return page;
+  }
+  
+
+
+  public default Permission selectApplicationPermissionByCode(String code) {
+    Permission probe = new Permission();
+    probe.setCode(code);
+    probe.setType("1");
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("type", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    Example<Permission> example = Example.of(probe, matcher);
+    
+    Optional<Permission> o = this.findOne(example);
+    
+    if (o.isPresent()) {
+      return o.get();
+    }
+    
+    return null;
+  }
+  
+ 
+
+  @Query(value = "select p from Permission p "
+      + "inner join RolePermission rp on p.id=rp.permissionId "
+      + "inner join Role r on rp.roleId=r.id "
+      + "inner join UserRole ur on r.id=ur.roleId "
+      + "inner join User u on ur.userId=u.id "
+      + "where u.username=:username "
+      + "and p.lft >= :lft and p.rgt <= :rgt "
+      + "and (:type is null or p.type=:type) "
+      + "and p.status='1' and r.status='1' and u.status='1' and u.enabled=1 ")
+  public List<Permission> selectUserRolePermissionByUsername(@Param("username") String username, @Param("lft") int lft, @Param("rgt") int rgt, @Param("type") String type);
+  
+  @Query(value = "select p from Permission p "
+      + "inner join RolePermission rp on p.id=rp.permissionId "
+      + "inner join Role r on rp.roleId=r.id "
+      + "inner join GroupRole gr on r.id=gr.roleId "
+      + "inner join Group_ g on gr.groupId=g.id "
+      + "inner join UserGroup ug on g.id=ug.groupId "
+      + "inner join User u on ug.userId=u.id "
+      + "where u.username=:username "
+      + "and p.lft >= :lft and p.rgt <= :rgt "
+      + "and (:type is null or p.type=:type) "
+      + "and p.status='1' and r.status='1' and g.status='1' and u.status='1' and u.enabled=1 ")
+  public List<Permission> selectUserGroupRolePermissionByUsername(@Param("username") String username, @Param("lft") int lft, @Param("rgt") int rgt, @Param("type") String type);
+
+  public default List<Permission> selectByUsername(String username, String applicationCode, String type) {
+    List<Permission> permissions = new ArrayList<Permission>();
+    
+    Permission applicationPermission = selectApplicationPermissionByCode(applicationCode);
+    if (applicationPermission == null) {
+      return permissions;
+    }
+    
+    int lft = applicationPermission.getLft();
+    int rgt = applicationPermission.getRgt();
+    
+    List<Permission> userRolePermissions = selectUserRolePermissionByUsername(username, lft, rgt, type);
+    permissions.addAll(userRolePermissions);
+    
+    List<Permission> userGroupRolePermissions = selectUserGroupRolePermissionByUsername(username, lft, rgt, type);
+    permissions.addAll(userGroupRolePermissions);
+    
+    return permissions;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/RolePermissionRepository.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/RolePermissionRepository.java
new file mode 100644
index 0000000..6dc9f2d
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/RolePermissionRepository.java
@@ -0,0 +1,151 @@
+package com.supwisdom.leaveschool.user.repository;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.MapBeanUtils;
+import com.supwisdom.leaveschool.user.domain.Permission;
+import com.supwisdom.leaveschool.user.domain.RolePermission;
+import com.supwisdom.leaveschool.user.domain.Role;
+
+@Repository
+public interface RolePermissionRepository extends BaseJpaRepository<RolePermission> {
+
+  public default Page<RolePermission> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    RolePermission probe = new RolePermission();
+    if (mapBean != null) {
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+      probe.setPermissionId(MapBeanUtils.getString(mapBean, "permissionId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    Page<RolePermission> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<RolePermission> selectRolePermissions(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    RolePermission probe = new RolePermission();
+    if (mapBean != null) {
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+      probe.setPermissionId(MapBeanUtils.getString(mapBean, "permissionId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("permissionId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<RolePermission> page = this.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public default void relateRolePermissions(Role role, List<RolePermission> rolePermissions) {
+
+    List<RolePermission> existRolePermissions = this.selectListByRoleId(role.getId());
+
+    Map<String, RolePermission> existMapRolePermissions = new LinkedHashMap<String, RolePermission>();
+    for (RolePermission rolePermission : existRolePermissions) {
+      String k = String.format("%s", rolePermission.getPermissionId());
+      existMapRolePermissions.put(k, rolePermission);
+    }
+
+    for (RolePermission rolePermission : rolePermissions) {
+      String k = String.format("%s", rolePermission.getPermissionId());
+
+      if (existMapRolePermissions.containsKey(k)) {
+        existMapRolePermissions.remove(k);
+      } else {
+        rolePermission.setCompanyId(role.getCompanyId());
+        rolePermission.setRoleId(role.getId());
+
+        this.insert(rolePermission);
+      }
+    }
+
+    for (RolePermission rolePermission : existMapRolePermissions.values()) {
+      this.deleteById(rolePermission.getId());
+    }
+  }
+
+  public default List<RolePermission> selectListByRoleId(String roleId) {
+
+    RolePermission probe = new RolePermission();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("roleId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    List<RolePermission> rolePermissions = this.findAll(example);
+
+    return rolePermissions;
+  }
+
+  public default void relatePermissionRoles(Permission permission, List<RolePermission> rolePermissions) {
+
+    // 获取权限已关联的角色
+    List<RolePermission> existPermissionRoles = this.selectListByPermissionId(permission.getId());
+
+    Map<String, RolePermission> existMapPermissionRoles = new LinkedHashMap<String, RolePermission>();
+    for (RolePermission rolePermission : existPermissionRoles) {
+      String k = String.format("%s", rolePermission.getRoleId());
+      existMapPermissionRoles.put(k, rolePermission);
+    }
+
+    // 保存未关联的角色
+    for (RolePermission rolePermission : rolePermissions) {
+      String k = String.format("%s", rolePermission.getRoleId());
+
+      if (existMapPermissionRoles.containsKey(k)) {
+        existMapPermissionRoles.remove(k);
+      } else {
+        rolePermission.setCompanyId(permission.getCompanyId());
+        rolePermission.setPermissionId(permission.getId());
+
+        this.insert(rolePermission);
+      }
+    }
+
+    // 删除移除关联的角色
+    for (RolePermission rolePermission : existMapPermissionRoles.values()) {
+      this.deleteById(rolePermission.getId());
+    }
+  }
+
+  public default List<RolePermission> selectListByPermissionId(String permissionId) {
+
+    RolePermission probe = new RolePermission();
+    probe.setPermissionId(permissionId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("permissionId",
+        ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<RolePermission> example = Example.of(probe, matcher);
+
+    List<RolePermission> rolePermissions = this.findAll(example);
+
+    return rolePermissions;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/RoleRepository.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/RoleRepository.java
new file mode 100644
index 0000000..f82d771
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/RoleRepository.java
@@ -0,0 +1,93 @@
+package com.supwisdom.leaveschool.user.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.MapBeanUtils;
+import com.supwisdom.leaveschool.user.domain.Role;
+
+@Repository
+public interface RoleRepository extends BaseJpaRepository<Role> {
+
+  public default Page<Role> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    Role probe = new Role();
+    if (mapBean != null) {
+      probe.setCode(MapBeanUtils.getString(mapBean, "code"));
+      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+      probe.setMemo(MapBeanUtils.getString(mapBean, "memo"));
+      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+    }
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("memo", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<Role> example = Example.of(probe, matcher);
+    
+    Page<Role> page = this.findAll(example, pageRequest);
+    
+    return page;
+  }
+  
+  
+  public default Role selectByCode(String code) {
+    Role probe = new Role();
+    probe.setCode(code);
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    Example<Role> example = Example.of(probe, matcher);
+    
+    Optional<Role> o = this.findOne(example);
+    
+    if (o.isPresent()) {
+      return o.get();
+    }
+    
+    return null;
+  }
+  
+  @Query(value = "select r from Role r "
+      + "inner join UserRole ur on r.id=ur.roleId "
+      + "inner join User u on ur.userId=u.id "
+      + "where u.username=:username "
+      + "and r.status='1' and u.status='1' and u.enabled=1 ")
+  public List<Role> selectUserRoleByUsername(@Param("username") String username);
+  
+  @Query(value = "select r from Role r "
+      + "inner join GroupRole gr on r.id=gr.roleId "
+      + "inner join Group_ g on gr.groupId=g.id "
+      + "inner join UserGroup ug on g.id=ug.groupId "
+      + "inner join User u on ug.userId=u.id "
+      + "where u.username=:username "
+      + "and r.status='1' and g.status='1' and u.status='1' and u.enabled=1 ")
+  public List<Role> selectUserGroupRoleByUsername(@Param("username") String username);
+
+  public default List<Role> selectByUsername(String username) {
+    List<Role> roles = new ArrayList<Role>();
+    
+    List<Role> userRoles = selectUserRoleByUsername(username);
+    roles.addAll(userRoles);
+    
+    List<Role> userGroupRoles = selectUserGroupRoleByUsername(username);
+    roles.addAll(userGroupRoles);
+    
+    return roles;
+  }
+  
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserGroupRepository.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserGroupRepository.java
new file mode 100644
index 0000000..04f26d0
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserGroupRepository.java
@@ -0,0 +1,151 @@
+package com.supwisdom.leaveschool.user.repository;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.MapBeanUtils;
+import com.supwisdom.leaveschool.user.domain.Group;
+import com.supwisdom.leaveschool.user.domain.UserGroup;
+import com.supwisdom.leaveschool.user.domain.User;
+
+@Repository
+public interface UserGroupRepository extends BaseJpaRepository<UserGroup> {
+
+  public default Page<UserGroup> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    UserGroup probe = new UserGroup();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setUserId(MapBeanUtils.getString(mapBean, "userId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("userId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<UserGroup> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<UserGroup> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<UserGroup> selectUserGroups(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    UserGroup probe = new UserGroup();
+    if (mapBean != null) {
+      probe.setGroupId(MapBeanUtils.getString(mapBean, "groupId"));
+      probe.setUserId(MapBeanUtils.getString(mapBean, "userId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("userId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<UserGroup> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<UserGroup> page = this.findAll(example, pageRequest);  // FIXME: 多表关联查询
+
+    return page;
+  }
+
+
+  public default void relateUserGroups(User user, List<UserGroup> userGroups) {
+
+    List<UserGroup> existUserGroups = this.selectListByUserId(user.getId());
+
+    Map<String, UserGroup> existMapUserGroups = new LinkedHashMap<String, UserGroup>();
+    for (UserGroup userGroup : existUserGroups) {
+      String k = String.format("%s", userGroup.getGroupId());
+      existMapUserGroups.put(k, userGroup);
+    }
+
+    for (UserGroup userGroup : userGroups) {
+      String k = String.format("%s", userGroup.getGroupId());
+
+      if (existMapUserGroups.containsKey(k)) {
+        existMapUserGroups.remove(k);
+      } else {
+        userGroup.setCompanyId(user.getCompanyId());
+        userGroup.setUserId(user.getId());
+
+        this.insert(userGroup);
+      }
+    }
+
+    for (UserGroup userGroup : existMapUserGroups.values()) {
+      this.deleteById(userGroup.getId());
+    }
+  }
+
+  public default List<UserGroup> selectListByUserId(String userId) {
+
+    UserGroup probe = new UserGroup();
+    probe.setUserId(userId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("userId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<UserGroup> example = Example.of(probe, matcher);
+
+    List<UserGroup> userGroups = this.findAll(example);
+
+    return userGroups;
+  }
+
+  
+  public default void relateGroupUsers(Group group, List<UserGroup> userGroups) {
+
+    List<UserGroup> existGroupUsers = this.selectListByGroupId(group.getId());
+
+    Map<String, UserGroup> existMapGroupUsers = new LinkedHashMap<String, UserGroup>();
+    for (UserGroup userGroup : existGroupUsers) {
+      String k = String.format("%s", userGroup.getUserId());
+      existMapGroupUsers.put(k, userGroup);
+    }
+
+    for (UserGroup userGroup : userGroups) {
+      String k = String.format("%s", userGroup.getUserId());
+
+      if (existMapGroupUsers.containsKey(k)) {
+        existMapGroupUsers.remove(k);
+      } else {
+        userGroup.setCompanyId(group.getCompanyId());
+        userGroup.setGroupId(group.getId());
+
+        this.insert(userGroup);
+      }
+    }
+
+    for (UserGroup userGroup : existMapGroupUsers.values()) {
+      this.deleteById(userGroup.getId());
+    }
+  }
+
+  public default List<UserGroup> selectListByGroupId(String groupId) {
+
+    UserGroup probe = new UserGroup();
+    probe.setGroupId(groupId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("groupId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<UserGroup> example = Example.of(probe, matcher);
+
+    List<UserGroup> userGroups = this.findAll(example);
+
+    return userGroups;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserRepository.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserRepository.java
new file mode 100644
index 0000000..dbeb279
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserRepository.java
@@ -0,0 +1,100 @@
+package com.supwisdom.leaveschool.user.repository;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.MapBeanUtils;
+import com.supwisdom.leaveschool.user.domain.User;
+
+@Repository
+public interface UserRepository extends BaseJpaRepository<User> {
+  
+  public default Page<User> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+    User probe = new User();
+    if (mapBean != null) {
+      probe.setUsername(MapBeanUtils.getString(mapBean, "username"));
+      probe.setName(MapBeanUtils.getString(mapBean, "name"));
+      probe.setStatus(MapBeanUtils.getString(mapBean, "status"));
+    }
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains())
+        .withMatcher("status", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<User> example = Example.of(probe, matcher);
+    
+    Page<User> page = this.findAll(example, pageRequest);
+    
+    return page;
+  }
+  
+  /*
+  public default User selectById(String id) {
+    
+    try {
+      Optional<User> entity = this.findById(id);
+      
+      return entity.get();
+    } catch(RuntimeException e) {
+      System.out.println("RuntimeException:"+e.getMessage());
+    } catch(Exception e) {
+      System.out.println("Exception:"+e.getMessage());
+    }
+    
+    return null;
+  }
+  
+  public default User insert(User entity) {
+    
+    if (entity.getCompanyId() == null || entity.getCompanyId().isEmpty()) {
+      entity.setCompanyId("1");
+    }
+    
+    entity.setDeleted(false);
+    //entity.setAddAccount(AuthUtil.getRemoteUser());
+    entity.setAddTime(Calendar.getInstance().getTime());
+    
+    User e = this.save(entity);
+    
+    return e;
+  }
+  
+  public default User update(User entity) {
+    
+    //entity.setEditAccount(AuthUtil.getRemoteUser());
+    entity.setEditTime(Calendar.getInstance().getTime());
+    
+    User e = this.save(entity);
+    
+    return e;
+  }
+  */
+  
+  public default User selectByUsername(String username) {
+    User probe = new User();
+    probe.setUsername(username);
+    
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.exact());
+    
+    Example<User> example = Example.of(probe, matcher);
+    
+    Optional<User> u = this.findOne(example);
+    
+    if (u.isPresent()) {
+      return u.get();
+    }
+    
+    return null;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserRoleRepository.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserRoleRepository.java
new file mode 100644
index 0000000..995da11
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/repository/UserRoleRepository.java
@@ -0,0 +1,150 @@
+package com.supwisdom.leaveschool.user.repository;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.ExampleMatcher;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Repository;
+
+import com.supwisdom.leaveschool.common.repository.BaseJpaRepository;
+import com.supwisdom.leaveschool.common.util.MapBeanUtils;
+import com.supwisdom.leaveschool.user.domain.Role;
+import com.supwisdom.leaveschool.user.domain.User;
+import com.supwisdom.leaveschool.user.domain.UserRole;
+
+@Repository
+public interface UserRoleRepository extends BaseJpaRepository<UserRole> {
+
+  public default Page<UserRole> selectPageList(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    UserRole probe = new UserRole();
+    if (mapBean != null) {
+      probe.setUserId(MapBeanUtils.getString(mapBean, "userId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("userId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+    Example<UserRole> example = Example.of(probe, matcher);
+
+    Page<UserRole> page = this.findAll(example, pageRequest);
+
+    return page;
+  }
+
+  public default Page<UserRole> selectUserRoles(int pageIndex, int pageSize, Map<String, Object> mapBean) {
+
+    UserRole probe = new UserRole();
+    if (mapBean != null) {
+      probe.setUserId(MapBeanUtils.getString(mapBean, "userId"));
+      probe.setRoleId(MapBeanUtils.getString(mapBean, "roleId"));
+    }
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("userId", ExampleMatcher.GenericPropertyMatchers.exact())
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<UserRole> example = Example.of(probe, matcher);
+
+    PageRequest pageRequest = PageRequest.of(pageIndex, pageSize);
+
+    Page<UserRole> page = this.findAll(example, pageRequest); // FIXME: 多表关联查询
+
+    return page;
+  }
+
+  public default void relateUserRoles(User user, List<UserRole> userRoles) {
+
+    List<UserRole> existUserRoles = this.selectListByUserId(user.getId());
+
+    Map<String, UserRole> existMapUserRoles = new LinkedHashMap<String, UserRole>();
+    for (UserRole userRole : existUserRoles) {
+      String k = String.format("%s", userRole.getRoleId());
+      existMapUserRoles.put(k, userRole);
+    }
+
+    for (UserRole userRole : userRoles) {
+      String k = String.format("%s", userRole.getRoleId());
+
+      if (existMapUserRoles.containsKey(k)) {
+        existMapUserRoles.remove(k);
+      } else {
+        userRole.setCompanyId(user.getCompanyId());
+        userRole.setUserId(user.getId());
+
+        this.insert(userRole);
+      }
+    }
+
+    for (UserRole userRole : existMapUserRoles.values()) {
+      this.deleteById(userRole.getId());
+    }
+  }
+
+  public default List<UserRole> selectListByUserId(String userId) {
+
+    UserRole probe = new UserRole();
+    probe.setUserId(userId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("userId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<UserRole> example = Example.of(probe, matcher);
+
+    List<UserRole> userRoles = this.findAll(example);
+
+    return userRoles;
+  }
+
+  
+  public default void relateRoleUsers(Role role, List<UserRole> userRoles) {
+
+    List<UserRole> existRoleUsers = this.selectListByRoleId(role.getId());
+
+    Map<String, UserRole> existMapRoleUsers = new LinkedHashMap<String, UserRole>();
+    for (UserRole userRole : existRoleUsers) {
+      String k = String.format("%s", userRole.getUserId());
+      existMapRoleUsers.put(k, userRole);
+    }
+
+    for (UserRole userRole : userRoles) {
+      String k = String.format("%s", userRole.getUserId());
+
+      if (existMapRoleUsers.containsKey(k)) {
+        existMapRoleUsers.remove(k);
+      } else {
+        userRole.setCompanyId(role.getCompanyId());
+        userRole.setRoleId(role.getId());
+
+        this.insert(userRole);
+      }
+    }
+
+    for (UserRole userRole : existMapRoleUsers.values()) {
+      this.deleteById(userRole.getId());
+    }
+  }
+
+  public default List<UserRole> selectListByRoleId(String roleId) {
+
+    UserRole probe = new UserRole();
+    probe.setRoleId(roleId);
+
+    ExampleMatcher matcher = ExampleMatcher.matching()
+        .withMatcher("roleId", ExampleMatcher.GenericPropertyMatchers.exact());
+
+    Example<UserRole> example = Example.of(probe, matcher);
+
+    List<UserRole> userRoles = this.findAll(example);
+
+    return userRoles;
+  }
+
+}
diff --git a/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/util/AuthUtil.java b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/util/AuthUtil.java
new file mode 100644
index 0000000..29366aa
--- /dev/null
+++ b/leaveschool/user/src/main/java/com/supwisdom/leaveschool/user/util/AuthUtil.java
@@ -0,0 +1,87 @@
+package com.supwisdom.leaveschool.user.util;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+public class AuthUtil {
+
+  private static final Logger logger = LoggerFactory.getLogger(AuthUtil.class);
+
+  public static String getRemoteUser() {
+    
+    if (RequestContextHolder.getRequestAttributes() != null) {
+      ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
+      if (servletRequestAttributes != null) {
+        HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
+        if (httpServletRequest != null) {
+          String remoteUser = httpServletRequest.getRemoteUser();
+          logger.debug("httpServletRequest.getRemoteUser(): {}", remoteUser);
+          
+          if (remoteUser == null || remoteUser.isEmpty()) {
+            remoteUser = httpServletRequest.getHeader("remote_user");
+            logger.debug("remote_user: {}", remoteUser);
+          }
+          
+          if (remoteUser != null) {
+            return remoteUser;
+          }
+
+          if (logger.isDebugEnabled()) {
+            logger.debug("request headers: ");
+            Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
+            while(headerNames.hasMoreElements()) {
+              String headerName = headerNames.nextElement();
+              logger.debug("{}: {}", headerName, httpServletRequest.getHeader(headerName));
+            }
+            logger.debug("request headers: ");
+          }
+          
+        }
+      }
+    }
+    
+    //logger.warn("FIXME: currentUsername. return 'user' by default.");
+    //return "user";  // FIXME: currentUsername
+    throw new RuntimeException("exception.authentication.remote.user.must.not.empty");
+
+    /*
+    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+    if (authentication == null) {
+      logger.error("authentication is null");
+      return null;
+    }
+
+    logger.debug("authentication is {}", authentication.getPrincipal());
+
+    if (!authentication.isAuthenticated()) {
+      logger.error("authentication is not authenticated");
+      return null;
+    }
+
+    if (authentication.getPrincipal() == null) {
+      logger.error("authentication's principal is null");
+      return null;
+    }
+
+    logger.debug("authentication's principal is {}", authentication.getPrincipal());
+
+    //if (authentication.getPrincipal() instanceof MyUser) {
+    //  return ((MyUser) authentication.getPrincipal()).getUsername();
+    //}
+    if (authentication.getPrincipal() instanceof String) {
+      return String.valueOf(authentication.getPrincipal());
+    }
+    
+
+    return null;
+    */
+  }
+
+}
diff --git a/leaveschool/user/src/main/resources/application.yml b/leaveschool/user/src/main/resources/application.yml
new file mode 100644
index 0000000..5493de8
--- /dev/null
+++ b/leaveschool/user/src/main/resources/application.yml
@@ -0,0 +1,44 @@
+server:
+  port: 10010
+
+## logging
+logging:
+  level:
+    root: INFO
+    org.springframework.web: TRACE
+    org.springframework.data.jpa: TRACE
+    com.supwisdom.infras.security: DEBUG
+    com.supwisdom.leaveschool: DEBUG
+
+spring:
+  application:
+    name: sample-user
+  datasource:
+    driver-class-name: com.mysql.jdbc.Driver
+    url: jdbc:mysql://172.50.10.15:3306/lixiao?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull
+    username: lixiao
+    password: lixiao@1234
+  jpa:
+    hibernate:
+      ddl-auto: none
+      naming:
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+    show-sql: true
+
+infras:
+  mvc:
+# 自定义error输出的例子
+    custom-error:
+      enabled: true
+      error-map:
+        org.springframework.validation.BindException: Customized Bind Error Reason
+      include-message: true
+      include-errors: true
+      include-error: true
+      include-exception: true
+      include-path: true
+      include-timestamp: true
+      include-status: true
+  data:
+    jpa:
+      basePackages: com.supwisdom.leaveschool.user.repository