**

最近准备开发一个家庭财务管理系统,该系统后台采用目前比较流行的Spring4、Spring MVC、Mybatis三大MVC框架技术,并且加入了shiro权限框架登录认证,maven项目管理工具,在前台用目前比较流行的bootstrap+ajax+json技术渲染分页数据显示。下面给出整个项目的详细构建过程和代码配置。

**

首先创建数据库quick4j,一共有5张表,分别是user,role,permission,user_role和role_permission。结构如下:

这里写图片描述

该数据表对应的创建sql语句如下:

/*
Navicat MySQL Data Transfer

Source Server         : 本地数据库
Source Server Version : 50703
Source Host           : localhost:3306
Source Database       : quick4j

Target Server Type    : MYSQL
Target Server Version : 50703
File Encoding         : 65001

Date: 2016-08-21 09:43:02

先在本地创建quick4j数据库:
mysql>create database quick4j;
mysql>use quick4j;
mysql>set names utf8;
mysql>source D:/sql/quick4j.sql;   
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `permission`
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '权限id',
  `permission_name` varchar(32) DEFAULT NULL COMMENT '权限名',
  `permission_sign` varchar(128) DEFAULT NULL COMMENT '权限标识,程序中判断使用,如"user:create"',
  `description` varchar(256) DEFAULT NULL COMMENT '权限描述,UI界面显示使用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='权限表';

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES ('1', 'user:create', 'user:create', null);

-- ----------------------------
-- Table structure for `role`
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '角色id',
  `role_name` varchar(32) DEFAULT NULL COMMENT '角色名',
  `role_sign` varchar(128) DEFAULT NULL COMMENT '角色标识,程序中判断使用,如"admin"',
  `description` varchar(256) DEFAULT NULL COMMENT '角色描述,UI界面显示使用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='角色表';

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'admin', 'admin', '管理员');
INSERT INTO `role` VALUES ('2', 'manager', 'manager', '管理者');

-- ----------------------------
-- Table structure for `role_permission`
-- ----------------------------
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '表id',
  `role_id` bigint(20) unsigned DEFAULT NULL COMMENT '角色id',
  `permission_id` bigint(20) unsigned DEFAULT NULL COMMENT '权限id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='角色与权限关联表';

-- ----------------------------
-- Records of role_permission
-- ----------------------------
INSERT INTO `role_permission` VALUES ('1', '2', '1');

-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `username` varchar(50) DEFAULT NULL COMMENT '用户名',
  `password` char(64) DEFAULT NULL COMMENT '密码',
  `state` varchar(32) DEFAULT NULL COMMENT '状态',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `test` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='用户表';

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('13', 'test3', 'test', '启用', '2016-08-18 21:59:17', 'test');
INSERT INTO `user` VALUES ('14', 'test4', 'test', '启用', '2016-08-18 21:59:17', 'test');
INSERT INTO `user` VALUES ('15', 'test44', 'test', '启用', '2016-08-18 21:59:17', 'test');
INSERT INTO `user` VALUES ('16', 'test333', 'test', '启用', '2016-08-18 21:59:17', 'test');
INSERT INTO `user` VALUES ('17', 'test33', 'test', '启用', '2016-08-18 21:59:17', 'test');
INSERT INTO `user` VALUES ('18', 'test55', 'test', '启用', '2016-08-18 21:59:17', 'test');

-- ----------------------------
-- Table structure for `user_role`
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '表id',
  `user_id` bigint(20) unsigned DEFAULT NULL COMMENT '用户id',
  `role_id` bigint(20) unsigned DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC COMMENT='用户与角色关联表';

-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1', '1');
INSERT INTO `user_role` VALUES ('2', '13', '1');
INSERT INTO `user_role` VALUES ('3', '14', '2');

数据库创建完成后,接下来构建maven项目–家庭财务管理系统,名称为HelloHome,该项目代码结构图如下:
这里写图片描述
这里写图片描述

接下来给出maven配置文件pom.xml的详细配置(该文件主要用来管理jar包依赖关系和项目部署情况):

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.kj.test</groupId>
    <artifactId>HelloHome</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>HelloHome Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <properties>
        <!-- spring版本号 -->
        <spring.version>4.0.6.RELEASE</spring.version>
        <!-- mybatis版本号 -->
        <mybatis.version>3.3.0</mybatis.version>
        <!-- log4j日志文件管理包版本 -->
        <slf4j.version>1.7.7</slf4j.version>
        <log4j.version>1.2.17</log4j.version>
        <shiro.version>1.2.3</shiro.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <!-- 表示开发的时候引入,发布的时候不会加载此包 -->
            <scope>test</scope>
        </dependency>
        <!-- spring核心包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-mock</artifactId>
            <version>2.0.8</version>
            <scope>test</scope>
        </dependency>
        <!-- mybatis核心包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <!-- mybatis/spring包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.2.2</version>
        </dependency>
        <!-- 添加分布插件的包pagehelper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>4.0.0</version>
        </dependency>

        <!-- 导入java ee jar 包 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <!-- 导入Mysql数据库链接jar包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>

        <!-- DruidDataSource -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.5</version>
        </dependency>

        <!-- 导入dbcp的jar包,用来在applicationContext.xml中配置数据库 -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>commons-pool</groupId>
            <artifactId>commons-pool</artifactId>
            <version>1.6</version>
        </dependency>
        <!-- JSP标签类 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <!-- JSTL标签类 -->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- 日志文件管理包 -->
        <!-- log start -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <!-- 格式化对象,方便输出日志 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.1.41</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <!-- logback start -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.logback-extensions</groupId>
            <artifactId>logback-ext-spring</artifactId>
            <version>0.1.1</version>
        </dependency>

        <!-- log end -->

        <!-- 映入JSON -->
        <!-- 添加json的依赖包 -->
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.3</version>
            <classifier>jdk15</classifier>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
            <version>1.6.1.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.4.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
        <!-- 上传组件包 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.9</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.0</version>
        </dependency>

        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.0.1.Final</version>
        </dependency>

        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-quartz</artifactId>
            <version>${shiro.version}</version>
        </dependency>

    </dependencies>

    <build>
        <!-- 配置将properties时的变量用pom.xml里的变量赋值替换 -->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <targetPath>${basedir}/target/classes</targetPath>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <targetPath>${basedir}/target/resources</targetPath>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>

        <!-- </plugins> -->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.1.1</version>
                <configuration>
                    <warSourceExcludes>${basedir}/target/HelloHome.war</warSourceExcludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.4.3</version>
                <configuration>
                    <testFailureIgnore>true</testFailureIgnore>
                </configuration>
            </plugin>
            <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <!-- 开发环境 -->
        <profile>
            <id>dev</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <!-- 以下配置properties中用到一些变量,如数据库连接参数、日志打印等 -->
            <properties>
                <!-- 数据库源设置 -->
                <jdbc_driverClassName>com.mysql.jdbc.Driver</jdbc_driverClassName>
                <jdbc_url>jdbc:mysql://localhost:3306/quick4j</jdbc_url>
                <jdbc_username>root</jdbc_username>
                <jdbc_password>root</jdbc_password>
                <!-- 日志打印的设置 -->
                <log.moduleName>HelloHome</log.moduleName>
                <log.base>logs</log.base>
                <log.other.level>DEBUG</log.other.level>
                <log.root.level>DEBUG</log.root.level>
                <log.stdout.ref><![CDATA[<appender-ref ref="stdout" />]]></log.stdout.ref>
            </properties>
        </profile>
    </profiles>
</project>

下载好相关jar包后,使用mybatis工具代码自动生成实体类domain,接口类Mapper(Dao)和配置文件mapper.xml。generatorConfig.xml工具代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>

    <!-- 配置数据库驱动 location:数据库驱动路径 -->
    <classPathEntry
        location="D:\development\MavenRepository\maven_jar\mysql\mysql-connector-java\5.1.30\mysql-connector-java-5.1.30.jar" />

    <context id="mbgtest">

        <!-- 是否去除自动生成的注释 true:是 : false:否 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
            <property name="suppressDate" value="true" />
        </commentGenerator>

        <!-- 配置数据库链接URL、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/quick4j" userId="root"
            password="root" />
        <!-- 配置生成模型的包名和位置 -->
        <javaModelGenerator targetPackage="com.kj.domain"
            targetProject="HelloHome/src/main/java" />
        <!-- 配置生成映射文件的包名和位置 -->
        <sqlMapGenerator targetPackage="com.kj.mapper"
            targetProject="HelloHome/src/main/java" />
        <!-- 配置生成DAO的包名和位置 -->
        <javaClientGenerator targetPackage="com.kj.dao"
            targetProject="HelloHome/src/main/java" type="XMLMAPPER" />

        <!-- 配置需要生成的表 -->
        <table schema="quick4j" tableName="user" domainObjectName="User" 
               enableCountByExample="false" 
               enableUpdateByExample="false" 
               enableDeleteByExample="false"
            enableSelectByExample="false" 
            selectByExampleQueryId="false" >
            <!-- 使用从数据库元数据获取的列名作为生成的实体对象的属性 -->
            <property name="useActualColumnNames" value="true" />
            <!-- 指定自动生成主键 -->
            <generatedKey column="id" sqlStatement="MySql" identity="true" />
        </table>
        <table schema="quick4j" tableName="role" domainObjectName="Role"
               enableCountByExample="false" 
               enableUpdateByExample="false" 
               enableDeleteByExample="false"
            enableSelectByExample="false" 
            selectByExampleQueryId="false" >
            <property name="useActualColumnNames" value="true" />
            <generatedKey column="id" sqlStatement="MySql" identity="true" />
        </table>
        <table schema="quick4j" tableName="permission" domainObjectName="Permission"
               enableCountByExample="false" 
               enableUpdateByExample="false" 
               enableDeleteByExample="false"
            enableSelectByExample="false" 
            selectByExampleQueryId="false" >
            <property name="useActualColumnNames" value="true" />
            <generatedKey column="id" sqlStatement="MySql" identity="true" />
        </table>
        <table schema="quick4j" tableName="role_permission" domainObjectName="RolePermission"
               enableCountByExample="false" 
               enableUpdateByExample="false" 
               enableDeleteByExample="false"
            enableSelectByExample="false" 
            selectByExampleQueryId="false" >
            <property name="useActualColumnNames" value="true" />
            <generatedKey column="id" sqlStatement="MySql" identity="true" />
        </table>
        <table schema="quick4j" tableName="user_role" domainObjectName="UserRole"
               enableCountByExample="false" 
               enableUpdateByExample="false" 
               enableDeleteByExample="false"
            enableSelectByExample="false" 
            selectByExampleQueryId="false" >
            <property name="useActualColumnNames" value="true" />
            <generatedKey column="id" sqlStatement="MySql" identity="true" />
        </table>
    </context>

</generatorConfiguration>

创建好工具代码后,安装eclipse插件,该插件安装方法请看mybatis逆向工程自动生成工具
安装插件启动eclipse后,右击generatorConfig.xml文件(该文件放在HelloHome目录下),点击generate MyBatis/iBATIS Artifacts即可自动生成数据库表对应的实体类,接口类以及对应的sql配置文件。

在com.kj.domain包下生成的类代码如下:

package com.kj.domain;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 用户
 * @author KJ
 *
 */
public class User {

    /** 主键 */
    private Long id;

    /** 账号:唯一 */
    private String username;
    /** 密码 */
    private String password;
    /** 启用状态 */
    private String state;
    /** 创建时间 */
    private Date create_time;
    /** 测试字段 */
    private String test;
    /** 所拥有的角色 */
    private List<Role> roles;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public Date getCreate_time() {
        return create_time;
    }

    public void setCreate_time(Date create_time) {
        this.create_time = create_time;
    }

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        this.test = test;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public Set<String> getRolesName() {
        List<Role> roles = getRoles();
        Set<String> set = new HashSet<String>();
        if (roles != null) {
            for (Role role : roles) {
                set.add(role.getRole_name());
            }
        }
        return set;
    }

}
package com.kj.domain;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 角色
 * @author KJ
 *
 */
public class Role {

    /** 主键 */
    private Long id;
    /** 角色标识 */
    private String role_name;
    /** 角色名称 */
    private String role_sign;
    /** 角色描述 */
    private String description;
    /** 该角色的用户集 */
    private List<User> users;
    /** 该角色对应的操作权限 */
    private List<Permission> permissions;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getRole_name() {
        return role_name;
    }

    public void setRole_name(String role_name) {
        this.role_name = role_name;
    }

    public String getRole_sign() {
        return role_sign;
    }

    public void setRole_sign(String role_sign) {
        this.role_sign = role_sign;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public List<Permission> getPermissions() {
        return permissions;
    }

    public void setPermissions(List<Permission> permissions) {
        this.permissions = permissions;
    }

    public Set<String> getPermissionsName() {
        List<Permission> permissions = getPermissions();
        Set<String> set = new HashSet<String>();
        if (permissions != null) {
            for (Permission permission : permissions) {
                set.add(permission.getPermission_name());
            }
        }
        return set;
    }
}
package com.kj.domain;

import java.util.List;

/**
 * 操作权限
 * @author KJ
 *
 */
public class Permission {

    /** 主键 */
    private Long id;
    /** 操作权限标识 */
    private String permission_name;
    /** 操作权限名称 */
    private String permission_sign;
    /** 操作权限描述 */
    private String description;
    /** 操作权限对应的角色 */
    private List<Role> roles;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getPermission_name() {
        return permission_name;
    }

    public void setPermission_name(String permission_name) {
        this.permission_name = permission_name;
    }

    public String getPermission_sign() {
        return permission_sign;
    }

    public void setPermission_sign(String permission_sign) {
        this.permission_sign = permission_sign;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

}
package com.kj.domain;

/**
 * 用户-角色关联类
 * @author KJ
 *
 */
public class UserRole {

    private Long id;

    private User user;

    private Role role;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }

}
package com.kj.domain;

/**
 * 角色-操作权限关联类
 * @author KJ
 *
 */
public class RolePermission {

    private Long id;

    private Role role;

    private Permission permission;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }

    public Permission getPermission() {
        return permission;
    }

    public void setPermission(Permission permission) {
        this.permission = permission;
    }

}

com.kj.dao包下的类如下:

package com.kj.dao;

import java.util.List;

import org.apache.ibatis.annotations.Param;

import com.kj.domain.User;

/**
 * 用户Dao接口类
 * @author KJ
 *
 */
public interface UserMapper {

     /**
     * 根据主键删除用户记录
     * @param username
     * @return
     */
    int deleteByPrimaryKey(Long id);

    /**
     * 插入用户记录
     * @param username
     * @return
     */
    int insert(User record);

    /**
     * 插入非空字段的用户记录
     * @param username
     * @return
     */
    int insertSelective(User record);

    /**
     * 根据主键查询用户记录
     * @param username
     * @return
     */
    public User selectByPrimaryKey(Long id);

    /**
     * 更新用户非空字段记录
     * @param username
     * @return
     */
    int updateByPrimaryKeySelective(User record);

    /**
     * 更新用户记录
     * @param username
     * @return
     */
    int updateByPrimaryKey(User record);

    /**
     * 根据用户登录账号查询用户信息(确保数据库中的账号唯一性)
     * @param username
     * @return
     */
    public User selectUserRoleByUserName(String username);

    /**
      * 根据用户名查询用户集合
      * @param userName
      * @return
      */
    public List<User> selectUserByUserName(@Param("userName") String userName);
}
package com.kj.dao;

import com.kj.domain.Role;

/**
 * Role对应的Dao接口类
 * @author KJ
 *
 */
public interface RoleMapper {

    int deleteByPrimaryKey(Long id);

    int insert(Role record);

    int insertSelective(Role record);

    public Role selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(Role record);

    int updateByPrimaryKey(Role record);

    public Role selectRolePermission(Long id);
}
package com.kj.dao;

import com.kj.domain.Permission;

public interface PermissionMapper {

    int deleteByPrimaryKey(Long id);

    int insert(Permission record);

    int insertSelective(Permission record);

    Permission selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(Permission record);

    int updateByPrimaryKey(Permission record);
}
package com.kj.dao;

import com.kj.domain.UserRole;

public interface UserRoleMapper {

    int deleteByPrimaryKey(Long id);

    int insert(UserRole record);

    int insertSelective(UserRole record);

    UserRole selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(UserRole record);

    int updateByPrimaryKey(UserRole record);
}
package com.kj.dao;

import com.kj.domain.RolePermission;

public interface RolePermissionMapper {

    int deleteByPrimaryKey(Long id);

    int insert(RolePermission record);

    int insertSelective(RolePermission record);

    RolePermission selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(RolePermission record);

    int updateByPrimaryKey(RolePermission record);
}

com.kj.mapper包下的代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.kj.dao.UserMapper" >
  <resultMap id="BaseResultMap" type="com.kj.domain.User" >
    <id column="id" property="id" jdbcType="BIGINT" />
    <result column="username" property="username" jdbcType="VARCHAR" />
    <result column="password" property="password" jdbcType="CHAR" />
    <result column="state" property="state" jdbcType="VARCHAR" />
    <result column="create_time" property="create_time" jdbcType="TIMESTAMP" />
    <result column="test" property="test" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, username, password, state, create_time, test
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from user
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from user
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.kj.domain.User" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into user (username, password, state, 
      create_time, test)
    values (#{username,jdbcType=VARCHAR}, #{password,jdbcType=CHAR}, #{state,jdbcType=VARCHAR}, 
      #{create_time,jdbcType=TIMESTAMP}, #{test,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="com.kj.domain.User" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into user
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="username != null" >
        username,
      </if>
      <if test="password != null" >
        password,
      </if>
      <if test="state != null" >
        state,
      </if>
      <if test="create_time != null" >
        create_time,
      </if>
      <if test="test != null" >
        test,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="username != null" >
        #{username,jdbcType=VARCHAR},
      </if>
      <if test="password != null" >
        #{password,jdbcType=CHAR},
      </if>
      <if test="state != null" >
        #{state,jdbcType=VARCHAR},
      </if>
      <if test="create_time != null" >
        #{create_time,jdbcType=TIMESTAMP},
      </if>
      <if test="test != null" >
        #{test,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.kj.domain.User" >
    update user
    <set >
      <if test="username != null" >
        username = #{username,jdbcType=VARCHAR},
      </if>
      <if test="password != null" >
        password = #{password,jdbcType=CHAR},
      </if>
      <if test="state != null" >
        state = #{state,jdbcType=VARCHAR},
      </if>
      <if test="create_time != null" >
        create_time = #{create_time,jdbcType=TIMESTAMP},
      </if>
      <if test="test != null" >
        test = #{test,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.kj.domain.User" >
    update user
    set username = #{username,jdbcType=VARCHAR},
      password = #{password,jdbcType=CHAR},
      state = #{state,jdbcType=VARCHAR},
      create_time = #{create_time,jdbcType=TIMESTAMP},
      test = #{test,jdbcType=VARCHAR}
    where id = #{id,jdbcType=BIGINT}
  </update>

    <resultMap type="com.kj.domain.User" id="userRoleMap" extends="BaseResultMap">  
        <collection property="roles" ofType="com.kj.domain.Role">  
            <id property="id" column="roleId" />  
            <result property="role_name" column="role_name" />  
            <result property="role_sign" column="role_sign" />  
            <result property="description" column="description" />  
            <collection property="permissions" ofType="com.kj.domain.Permission">  
                <id property="id" column="permissionId" />  
                <result property="permission_name" column="permission_name" />  
                <result property="permission_sign" column="permission_sign" />  
                <result property="description" column="permission_description" />   
            </collection>  
        </collection>  
    </resultMap>  

    <!-- 根据user表中的username查询用户和角色信息 -->  
    <select id="selectUserRoleByUserName" parameterType="java.lang.String" resultMap="userRoleMap">  
        select u.id,u.username,u.password,u.state,u.create_time, 
        r.id as roleId,r.role_name, r.role_sign, r.description,
        p.id as permissionId,p.permission_name,p.permission_sign,p.description as permission_description
        from user u left join user_role ur on u.id=ur.user_id  
        left join role r on ur.role_id=r.id 
        left join role_permission rp on rp.role_id=r.id 
        left join permission p on rp.permission_id=p.id 
        where u.username = #{username}  
    </select>  

  <select id="selectUserByUserName" parameterType="java.lang.String" resultMap="BaseResultMap">
        SELECT *
        FROM user
        WHERE 1 = 1
        <if test="userName != null and userName != ''">
            AND username like "%"#{userName,jdbcType=VARCHAR}"%"
        </if>
        ORDER BY id
    </select>

</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.kj.dao.RoleMapper" >
  <resultMap id="BaseResultMap" type="com.kj.domain.Role" >
    <id column="id" property="id" jdbcType="BIGINT" />
    <result column="role_name" property="role_name" jdbcType="VARCHAR" />
    <result column="role_sign" property="role_sign" jdbcType="VARCHAR" />
    <result column="description" property="description" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, role_name, role_sign, description
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from role
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from role
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.kj.domain.Role" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into role (role_name, role_sign, description
      )
    values (#{role_name,jdbcType=VARCHAR}, #{role_sign,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}
      )
  </insert>
  <insert id="insertSelective" parameterType="com.kj.domain.Role" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into role
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="role_name != null" >
        role_name,
      </if>
      <if test="role_sign != null" >
        role_sign,
      </if>
      <if test="description != null" >
        description,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="role_name != null" >
        #{role_name,jdbcType=VARCHAR},
      </if>
      <if test="role_sign != null" >
        #{role_sign,jdbcType=VARCHAR},
      </if>
      <if test="description != null" >
        #{description,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.kj.domain.Role" >
    update role
    <set >
      <if test="role_name != null" >
        role_name = #{role_name,jdbcType=VARCHAR},
      </if>
      <if test="role_sign != null" >
        role_sign = #{role_sign,jdbcType=VARCHAR},
      </if>
      <if test="description != null" >
        description = #{description,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.kj.domain.Role" >
    update role
    set role_name = #{role_name,jdbcType=VARCHAR},
      role_sign = #{role_sign,jdbcType=VARCHAR},
      description = #{description,jdbcType=VARCHAR}
    where id = #{id,jdbcType=BIGINT}
  </update>

    <resultMap type="com.kj.domain.Role" id="groupRolePermission" extends="BaseResultMap">  
        <collection property="permissions" ofType="com.kj.domain.Permission">  
            <id property="id" column="permissionId" />  
            <result property="permission_name" column="permission_name" />  
            <result property="permission_sign" column="permission_sign" />  
            <result property="description" column="permission_description" />  
        </collection>  
    </resultMap>  

    <!-- 根据Group表中的id或name查询组信息和组内用户信息 -->  
    <select id="selectRolePermission" parameterType="com.kj.domain.Role" resultMap="groupRolePermission">  
        select r.id,r.role_name,r.role_sign,r.description,
        p.id as permissionId,p.permission_name,p.permission_sign,p.description as permission_description 
        from role r left join 
        role_permission rp on r.id=rp.role_id left join 
        permission p on p.id=rp.permission_id 
        <where>  
            <!--当id为初始值0,不再使用id作为查询条件 -->  
            <if test="id != 0">r.id=#{id}</if>  
            <!-- 当name为空或为空串时,不再使用name作为查询条件 -->  
            <if test="name != null and name != ''">  
                or r.role_name = #{name}  
            </if>  
        </where>  
    </select>  


</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.kj.dao.PermissionMapper" >
  <resultMap id="BaseResultMap" type="com.kj.domain.Permission" >
    <id column="id" property="id" jdbcType="BIGINT" />
    <result column="permission_name" property="permission_name" jdbcType="VARCHAR" />
    <result column="permission_sign" property="permission_sign" jdbcType="VARCHAR" />
    <result column="description" property="description" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, permission_name, permission_sign, description
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from permission
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from permission
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.kj.domain.Permission" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into permission (permission_name, permission_sign, 
      description)
    values (#{permission_name,jdbcType=VARCHAR}, #{permission_sign,jdbcType=VARCHAR}, 
      #{description,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="com.kj.domain.Permission" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into permission
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="permission_name != null" >
        permission_name,
      </if>
      <if test="permission_sign != null" >
        permission_sign,
      </if>
      <if test="description != null" >
        description,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="permission_name != null" >
        #{permission_name,jdbcType=VARCHAR},
      </if>
      <if test="permission_sign != null" >
        #{permission_sign,jdbcType=VARCHAR},
      </if>
      <if test="description != null" >
        #{description,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.kj.domain.Permission" >
    update permission
    <set >
      <if test="permission_name != null" >
        permission_name = #{permission_name,jdbcType=VARCHAR},
      </if>
      <if test="permission_sign != null" >
        permission_sign = #{permission_sign,jdbcType=VARCHAR},
      </if>
      <if test="description != null" >
        description = #{description,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.kj.domain.Permission" >
    update permission
    set permission_name = #{permission_name,jdbcType=VARCHAR},
      permission_sign = #{permission_sign,jdbcType=VARCHAR},
      description = #{description,jdbcType=VARCHAR}
    where id = #{id,jdbcType=BIGINT}
  </update>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.kj.dao.UserRoleMapper" >
  <resultMap id="BaseResultMap" type="com.kj.domain.UserRole" >
    <id column="id" property="id" jdbcType="BIGINT" />
    <result column="user_id" property="user_id" jdbcType="BIGINT" />
    <result column="role_id" property="role_id" jdbcType="BIGINT" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, user_id, role_id
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from user_role
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from user_role
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.kj.domain.UserRole" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into user_role (user_id, role_id)
    values (#{user_id,jdbcType=BIGINT}, #{role_id,jdbcType=BIGINT})
  </insert>
  <insert id="insertSelective" parameterType="com.kj.domain.UserRole" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into user_role
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="user_id != null" >
        user_id,
      </if>
      <if test="role_id != null" >
        role_id,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="user_id != null" >
        #{user_id,jdbcType=BIGINT},
      </if>
      <if test="role_id != null" >
        #{role_id,jdbcType=BIGINT},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.kj.domain.UserRole" >
    update user_role
    <set >
      <if test="user_id != null" >
        user_id = #{user_id,jdbcType=BIGINT},
      </if>
      <if test="role_id != null" >
        role_id = #{role_id,jdbcType=BIGINT},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.kj.domain.UserRole" >
    update user_role
    set user_id = #{user_id,jdbcType=BIGINT},
      role_id = #{role_id,jdbcType=BIGINT}
    where id = #{id,jdbcType=BIGINT}
  </update>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.kj.dao.RoleMapper" >
  <resultMap id="BaseResultMap" type="com.kj.domain.Role" >
    <id column="id" property="id" jdbcType="BIGINT" />
    <result column="role_name" property="role_name" jdbcType="VARCHAR" />
    <result column="role_sign" property="role_sign" jdbcType="VARCHAR" />
    <result column="description" property="description" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, role_name, role_sign, description
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
    select 
    <include refid="Base_Column_List" />
    from role
    where id = #{id,jdbcType=BIGINT}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
    delete from role
    where id = #{id,jdbcType=BIGINT}
  </delete>
  <insert id="insert" parameterType="com.kj.domain.Role" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into role (role_name, role_sign, description
      )
    values (#{role_name,jdbcType=VARCHAR}, #{role_sign,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}
      )
  </insert>
  <insert id="insertSelective" parameterType="com.kj.domain.Role" >
    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
      SELECT LAST_INSERT_ID()
    </selectKey>
    insert into role
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="role_name != null" >
        role_name,
      </if>
      <if test="role_sign != null" >
        role_sign,
      </if>
      <if test="description != null" >
        description,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="role_name != null" >
        #{role_name,jdbcType=VARCHAR},
      </if>
      <if test="role_sign != null" >
        #{role_sign,jdbcType=VARCHAR},
      </if>
      <if test="description != null" >
        #{description,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.kj.domain.Role" >
    update role
    <set >
      <if test="role_name != null" >
        role_name = #{role_name,jdbcType=VARCHAR},
      </if>
      <if test="role_sign != null" >
        role_sign = #{role_sign,jdbcType=VARCHAR},
      </if>
      <if test="description != null" >
        description = #{description,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.kj.domain.Role" >
    update role
    set role_name = #{role_name,jdbcType=VARCHAR},
      role_sign = #{role_sign,jdbcType=VARCHAR},
      description = #{description,jdbcType=VARCHAR}
    where id = #{id,jdbcType=BIGINT}
  </update>

    <resultMap type="com.kj.domain.Role" id="groupRolePermission" extends="BaseResultMap">  
        <collection property="permissions" ofType="com.kj.domain.Permission">  
            <id property="id" column="permissionId" />  
            <result property="permission_name" column="permission_name" />  
            <result property="permission_sign" column="permission_sign" />  
            <result property="description" column="permission_description" />  
        </collection>  
    </resultMap>  

    <!-- 根据Group表中的id或name查询组信息和组内用户信息 -->  
    <select id="selectRolePermission" parameterType="com.kj.domain.Role" resultMap="groupRolePermission">  
        select r.id,r.role_name,r.role_sign,r.description,
        p.id as permissionId,p.permission_name,p.permission_sign,p.description as permission_description 
        from role r left join 
        role_permission rp on r.id=rp.role_id left join 
        permission p on p.id=rp.permission_id 
        <where>  
            <!--当id为初始值0,不再使用id作为查询条件 -->  
            <if test="id != 0">r.id=#{id}</if>  
            <!-- 当name为空或为空串时,不再使用name作为查询条件 -->  
            <if test="name != null and name != ''">  
                or r.role_name = #{name}  
            </if>  
        </where>  
    </select>  


</mapper>

IUserService类代码如下:

package com.kj.service;

import com.kj.domain.User;
import com.kj.util.PagedResult;

/**
 * 功能概要:UserService接口类
 * 
 * @author KJ
 * @since 2016-08-15
 */
public interface IUserService {

    /**
     * 根据用户主键查询用户信息
     * @param userId
     * @return
     */
    public User selectUserById(Long userId);

    /**
     * 根据用户登录账号查询用户信息(确保数据库中的账号唯一性)
     * @param username
     * @return
     */
    public User selectUserRoleByUserName(String username);
    /**
     * 
     * @param userName 查询条件,可为空
     * @param pageNo 查询条件,可为空,默认取1
     * @param pageSize 查询条件,可为空,默认取10
     * @return
     */
    public PagedResult<User> queryByPage(String userName,Integer pageNo,Integer pageSize);

}

UserServiceImpl类代码如下:

package com.kj.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.github.pagehelper.PageHelper;
import com.kj.dao.UserMapper;
import com.kj.domain.User;
import com.kj.service.IUserService;
import com.kj.util.BeanUtil;
import com.kj.util.PagedResult;

/**
 * 功能概要:UserService实现类
 * 
 * @author KJ
 * @since  2016-08-15
 */
@Service
public class UserServiceImpl implements IUserService{

    @Autowired
    private UserMapper userMapper;

    /**
     * 根据用户主键查询用户信息
     * @param userId
     * @return
     */
    public User selectUserById(Long userId) {
        return userMapper.selectByPrimaryKey(userId);
    }

    /**
     * 根据用户登录账号查询用户信息(确保数据库中的账号唯一性)
     * @param username
     * @return
     */
    public User selectUserRoleByUserName(String username) {
        return userMapper.selectUserRoleByUserName(username);
    }

    /**
     * 分页查询用户记录
     * @param userName 查询条件,可为空
     * @param pageNo 查询条件,可为空,默认取1
     * @param pageSize 查询条件,可为空,默认取10
     * @return
     */
    public PagedResult<User> queryByPage(String userName,Integer pageNo,Integer pageSize ) {
        pageNo = pageNo == null?1:pageNo;
        pageSize = pageSize == null?10:pageSize;
        PageHelper.startPage(pageNo,pageSize);  //startPage是告诉拦截器说我要开始分页了。分页参数是这两个。
        return BeanUtil.toPagedResult(userMapper.selectUserByUserName(userName));
    }

}

PagedResult分页类代码

package com.kj.util;

import java.util.List;

import com.kj.dto.BaseEntity;

/**
 * 功能概要:
 * @author KJ
 * @since  2016-08-15
 */
public class PagedResult<T> extends BaseEntity {

    /*serialVersionUID*/
    private static final long serialVersionUID = 1L;

    private List<T> dataList;//数据

    private long pageNo;//当前页

    private long pageSize;//条数

    private long total;//总条数

    private long pages;//总页面数目

    public List<T> getDataList() {
        return dataList;
    }

    public void setDataList(List<T> dataList) {
        this.dataList = dataList;
    }

    public long getPageNo() {
        return pageNo;
    }

    public void setPageNo(long pageNo) {
        this.pageNo = pageNo;
    }

    public long getPageSize() {
        return pageSize;
    }

    public void setPageSize(long pageSize) {
        this.pageSize = pageSize;
    }

    public long getTotal() {
        return total;
    }

    public void setTotal(long total) {
        this.total = total;
    }

    public long getPages() {
        return pages;
    }

    public void setPages(long pages) {
        this.pages = pages;
    }

}

BeanUtil分页工具类

package com.kj.util;

import java.util.List;

import com.github.pagehelper.Page;
import com.kj.util.PagedResult;

/**
 * 功能概要:
 * 
 * @author KJ
 * @since  2016-08-15
 */
public class BeanUtil {

    public static <T> PagedResult<T> toPagedResult(List<T> datas) {
        PagedResult<T> result = new PagedResult<T>();
        if (datas instanceof Page) {
            Page page = (Page) datas;
            result.setPageNo(page.getPageNum());
            result.setPageSize(page.getPageSize());
            result.setDataList(page.getResult());
            result.setTotal(page.getTotal());
            result.setPages(page.getPages());
        } else {
            result.setPageNo(1);
            result.setPageSize(datas.size());
            result.setDataList(datas);
            result.setTotal(datas.size());
        }
        return result;
    }

}

BaseEntity基础类

package com.kj.dto;


import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 
 * <b>类说明:</b>bean基类
 * 
 * <p>
 * <b>详细描述:</b>
 * 
 * @author KJ 
 * @since 2016-08-15
 */
public abstract class BaseEntity implements Serializable{
    private static final long serialVersionUID = 1L;

    private static Map<Class<?>,PropertyInfo[]> class2Props = new HashMap<Class<?>,PropertyInfo[]>(128);

    @Override
    public String toString() {
        PropertyInfo[] props = class2Props.get(this.getClass());
        if( props == null ){
            props = getProps(this.getClass());
        }

        StringBuilder   builder = new StringBuilder(1024);
        boolean isFirst = true;
        for (int i = 0, n = props.length; i < n; i++) {
            try {
                PropertyInfo propInfo = props[i];               

                Object value = propInfo.getMethod.invoke(this, new Object[0]);
                if (isFirst)
                    isFirst = false;
                else
                    builder.append(",");
                builder.append(propInfo.propName);
                builder.append(":");
                if (value instanceof String)
                    builder.append("\"");
                builder.append(value);
                if (value instanceof String)
                    builder.append("\"");               
            } catch (Exception e) {
                // ignore
            }
        }
        return "{" + builder.toString() + "}";
    }

    private static PropertyInfo[] getProps(Class<? extends BaseEntity> clazz) {
        PropertyInfo[] props;
        Method[] allMethods = clazz.getMethods(); 
        List<PropertyInfo> propList = new ArrayList<PropertyInfo>();

        for (int i = 0, n = allMethods.length; i < n; i++) {
            try {
                Method method = allMethods[i];
                if ((method.getModifiers() & Modifier.PUBLIC) == 1
                        && method.getDeclaringClass() != Object.class
                        && (method.getParameterTypes() == null || method
                                .getParameterTypes().length == 0)) {
                    String methodName = method.getName();
                    if (methodName.startsWith("get") || methodName.startsWith("is") ) {
                        PropertyInfo propInfo = new PropertyInfo();                                     
                        propInfo.getMethod = method;
                        if (methodName.startsWith("get")) {
                            propInfo.propName = methodName.substring(3, 4).toLowerCase()
                                    + methodName.substring(4);
                        } else if (methodName.startsWith("is")) {
                            propInfo.propName = methodName.substring(2, 3).toLowerCase()
                                    + methodName.substring(3);
                        }               
                        propList.add(propInfo);
                    }
                }                   
            }catch(Exception e){                    
            }
        }

        props =  new PropertyInfo[propList.size()];
        propList.toArray(props);
        class2Props.put(clazz, props);
        return props;
    }

    static class PropertyInfo{
        Method getMethod;
        String propName;        
    }

}

HttpConstants公共类

package com.kj.common;

public class HttpConstants {

    public static final String SYSTEM_ERROR_MSG = "系统错误";

    public static final String REQUEST_PARAMS_NULL = "请求参数为空";

    public static final String SERVICE_RESPONSE_NULL = "服务端返回结果为空";

    // 服务端返回成功的标志
    public static final String SERVICE_RESPONSE_SUCCESS_CODE = "AMS00000";

    // 服务端返回结果的标志
    public static final String SERVICE_RESPONSE_RESULT_FLAG = "returnCode";

    // 服务端返回结果失败的标志
    public static final String SERVICE_RESPONSE_RESULT_MSG = "errorMsg";

    // 返回给前段页面成功或失败的标志
    public static final String RESPONSE_RESULT_FLAG_ISERROR = "isError";

    // 执行删除操作
    public static final String OPERATION_TYPE_DELETE = "D";

    public static final String ENUM_PATH = "com.mucfc.msm.enumeration.";

}

BaseController基础控制类

package com.kj.controller;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.kj.common.HttpConstants;
import com.kj.json.JsonDateValueProcessor;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;

/**
 * Controller基类
 */
public class BaseController {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    protected final static String DATE_FORMATE = "yyyy-MM-dd";

    /**
     * 返回服务端处理结果
     * @param obj 服务端输出对象
     * @return 输出处理结果给前段JSON格式数据
     * @author KJ
     * @since 2016-08-15
     */
    public String responseResult(Object obj){
        JSONObject jsonObj = null;
        if(obj != null){
            //调试用,发布时清除
            logger.info("后端返回对象:{}", obj);
            JsonConfig jsonConfig = new JsonConfig(); 
            jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
            jsonObj = JSONObject.fromObject(obj, jsonConfig);
            logger.info("后端返回数据:" + jsonObj);
            if(HttpConstants.SERVICE_RESPONSE_SUCCESS_CODE.equals(jsonObj.getString(HttpConstants.SERVICE_RESPONSE_RESULT_FLAG))){
                jsonObj.element(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, false);
                jsonObj.element(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, "");
            }else{
                jsonObj.element(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, true);
                String errMsg = jsonObj.getString(HttpConstants.SERVICE_RESPONSE_RESULT_MSG);
                jsonObj.element(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, errMsg==null?HttpConstants.SERVICE_RESPONSE_NULL:errMsg);
            }
        }
        logger.info("输出结果:{}", jsonObj.toString());
        return jsonObj.toString();
    }

    /**
     * 返回成功
     * @param obj 输出对象
     * @return 输出成功的JSON格式数据
     */
    public String responseSuccess(Object obj){
        JSONObject jsonObj = null;
        if(obj != null){
            logger.info("后端返回对象:{}", obj);
            JsonConfig jsonConfig = new JsonConfig(); 
            jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
            jsonObj = JSONObject.fromObject(obj, jsonConfig);
            logger.info("后端返回数据:" + jsonObj);
            jsonObj.element(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, false);
            jsonObj.element(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, "");
        }
        logger.info("输出结果:{}", jsonObj.toString());
        return jsonObj.toString();
    }

    /**
     * 返回成功
     * @param obj 输出对象
     * @return 输出成功的JSON格式数据
     */
    public String responseArraySuccess(Object obj){
        JSONArray jsonObj = null;
        if(obj != null){
            logger.info("后端返回对象:{}", obj);
            JsonConfig jsonConfig = new JsonConfig();
            jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
            jsonObj = JSONArray.fromObject(obj, jsonConfig);
            logger.info("后端返回数据:" + jsonObj);
        }
        logger.info("输出结果:{}", jsonObj.toString());
        return jsonObj.toString();
    }

    /**
     * 返回成功
     * @param obj 输出对象
     * @return 输出成功的JSON格式数据
     */
    public String responseSuccess(Object obj, String msg){
        JSONObject jsonObj = null;
        if(obj != null){
            logger.info("后端返回对象:{}", obj);
            JsonConfig jsonConfig = new JsonConfig(); 
            jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor());
            jsonObj = JSONObject.fromObject(obj, jsonConfig);
            logger.info("后端返回数据:" + jsonObj);
            jsonObj.element(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, false);
            jsonObj.element(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, msg);
        }
        logger.info("输出结果:{}", jsonObj.toString());
        return jsonObj.toString();
    }

    /**
     * 返回失败
     * @param errorMsg 错误信息
     * @return 输出失败的JSON格式数据
     */
    public String responseFail(String errorMsg){
        JSONObject jsonObj = new JSONObject();
        jsonObj.put(HttpConstants.RESPONSE_RESULT_FLAG_ISERROR, true);
        jsonObj.put(HttpConstants.SERVICE_RESPONSE_RESULT_MSG, errorMsg);
        logger.info("输出结果:{}", jsonObj.toString());
        return jsonObj.toString();
    }

}

登录管理类HomeController

package com.kj.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

/**
 * 功能概要:HomeController
 * 
 * @author KJ
 * @since  2016-08-15
 */
@Controller  
public class HomeController {  

    @RequestMapping(value="/login",method=RequestMethod.GET)  
    public String loginForm(Model model){  
        return "login";  
    }  

    @RequestMapping(value="/login",method=RequestMethod.POST)  
    public String login(HttpServletRequest req, HttpServletResponse response, RedirectAttributes redirectAttributes){  
        try {  
            String username = req.getParameter("username");
            if (username == null || username.trim().length() == 0) {
                redirectAttributes.addFlashAttribute("message", "用户名不能为空");
                return "redirect:login";
            }
            String password = req.getParameter("password");
            if (password == null || password.trim().length() == 0) {
                redirectAttributes.addFlashAttribute("message", "密码不能为空");
                return "redirect:login";
            }
            Subject subject= SecurityUtils.getSubject();
            subject.login(new UsernamePasswordToken(username, password));  
            return "redirect:/user/";  
        } catch (AuthenticationException e) {  
            redirectAttributes.addFlashAttribute("message","用户名或密码错误");
            e.printStackTrace();
            return "redirect:login";  
        }  
    }  

    @RequestMapping(value="/logout",method=RequestMethod.GET)    
    public String logout(HttpServletRequest req,RedirectAttributes redirectAttributes ){   
        //使用权限配置管理工具进行用户的退出,跳出登录,给出提示信息  
        redirectAttributes.addFlashAttribute("message", "您已安全退出");    
        return "redirect:login";  
    }   

    @RequestMapping("/unauthorizedUrl")  
    public String unauthorizedRole(){  
        //未授权角色用户跳转到unauthorizedTip.jsp页面
        return "unauthorizedTip";  
    }  
}  

用户控制类UserController

package com.kj.controller;

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 org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.kj.service.IUserService;
import com.kj.util.PagedResult;
import com.kj.domain.User;

/**
 * 功能概要:UserController
 * 
 * @author KJ
 * @since  2016-08-15
 */
@Controller
@RequestMapping(value="/user")
public class UserController extends BaseController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private IUserService userService;

    /**
     * 显示首页
     * @return
     */
    @RequestMapping("/")  
    public String userManage(){
        return "user/userList";
    }

    /**
     * 测试是否具有访问增加路径权限
     * @return
     */
    @RequestMapping("/add")  
    public String userAdd(){
        return "user/add/userList";
    }

    /**
     * 测试是否具有访问删除路径权限
     * @return
     */
    @RequestMapping("/del")  
    public String userDel(){
        return "user/del/userList";
    }

    /**
     * 测试是否具有访问编辑路径权限
     * @return
     */
    @RequestMapping("/edit")  
    public String userEdit(){
        return "user/edit/userList";
    }

    /**
     * 分页查询用户信息
     * @param page
     * @return
     */
    @RequestMapping(value="/list", method= RequestMethod.POST)
    @ResponseBody
    public String list(Integer pageNumber,Integer pageSize ,String userName) {
        logger.info("分页查询用户信息列表请求入参:pageNumber{},pageSize{}", pageNumber,pageSize);
        try {
            PagedResult<User> pageResult = userService.queryByPage(userName, pageNumber,pageSize);
            return responseSuccess(pageResult);
        } catch (Exception e) {
            e.printStackTrace();
            return responseFail(e.getMessage());
        }
    }
}

返回json数据封装类JsonDateValueProcessor

package com.kj.json;

import net.sf.json.JsonConfig;
import net.sf.json.processors.JsonValueProcessor;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * @author KJ
 * @since 2016-08-15
 */
public class JsonDateValueProcessor implements JsonValueProcessor {

    /**
     * datePattern
     */
    private String datePattern = "yyyy-MM-dd HH:mm:ss";

    /**
     * JsonDateValueProcessor
     */
    public JsonDateValueProcessor() {
        super();
    }

    /**
     * @param format
     */
    public JsonDateValueProcessor(String format) {
        super();
        this.datePattern = format;
    }

    /**
     * @param value
     * @param jsonConfig
     * @return Object
     */
    public Object processArrayValue(Object value, JsonConfig jsonConfig) {
        return process(value);
    }

    /**
     * @param key
     * @param value
     * @param jsonConfig
     * @return Object
     */
    public Object processObjectValue(String key, Object value, JsonConfig jsonConfig) {
        return process(value);
    }

    /**
     * process
     *
     * @param value
     * @return
     */
    private Object process(Object value) {
        try {
            if (value instanceof Date) {
                SimpleDateFormat sdf = new SimpleDateFormat(datePattern, Locale.UK);
                return sdf.format((Date) value);
            }
            return value == null ? "" : value.toString();
        } catch (Exception e) {
            return "";
        }

    }

    /**
     * @return the datePattern
     */
    public String getDatePattern() {
        return datePattern;
    }

    /**
     * @param pDatePattern the datePattern to set
     */
    public void setDatePattern(String pDatePattern) {
        datePattern = pDatePattern;
    }

}

为了登录时候避免登录URL出现JSESSIONID后缀,对shiro框架filter类进行了重写,对应的两个类如下:
MyShiroHttpServletResponse类

package com.kj.filter;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.ShiroHttpServletResponse;

public class MyShiroHttpServletResponse extends ShiroHttpServletResponse {
    public MyShiroHttpServletResponse(HttpServletResponse wrapped, ServletContext context, ShiroHttpServletRequest request) {
        super(wrapped, context, request);
    }

    @Override
    protected String toEncoded(String url, String sessionId) {
        if ((url == null) || (sessionId == null))
            return (url);
        String path = url;
        String query = "";
        String anchor = "";
        int question = url.indexOf('?');
        if (question >= 0) {
            path = url.substring(0, question);
            query = url.substring(question);
        }
        int pound = path.indexOf('#');
        if (pound >= 0) {
            anchor = path.substring(pound);
            path = path.substring(0, pound);
        }
        StringBuilder sb = new StringBuilder(path);
        // 重写toEncoded方法,注释掉这几行代码就不会再生成JESSIONID了。
        // if (sb.length() > 0) { // session id param can't be first.
        // sb.append(";");
        // sb.append(DEFAULT_SESSION_ID_PARAMETER_NAME);
        // sb.append("=");
        // sb.append(sessionId);
        // }
        sb.append(anchor);
        sb.append(query);
        return (sb.toString());
    }
}

MyShiroFilterFactoryBean类

package com.kj.filter;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.springframework.beans.factory.BeanInitializationException;

public class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean { 

    @Override  
      public Class getObjectType() {  
            return MySpringShiroFilter.class;  
      } 

    @Override
    protected AbstractShiroFilter createInstance() throws Exception {

        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            String msg = "SecurityManager property must be set.";
            throw new BeanInitializationException(msg);
        }

        if (!(securityManager instanceof WebSecurityManager)) {
            String msg = "The security manager does not implement the WebSecurityManager interface.";
            throw new BeanInitializationException(msg);
        }
        FilterChainManager manager = createFilterChainManager();

        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        chainResolver.setFilterChainManager(manager);

        return new MySpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }

    private static final class MySpringShiroFilter extends AbstractShiroFilter {  

        protected MySpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {  
          super();  
          if (webSecurityManager == null) {  
            throw new IllegalArgumentException("WebSecurityManager property cannot be null.");  
          }  
          setSecurityManager(webSecurityManager);  
          if (resolver != null) {  
            setFilterChainResolver(resolver);  
          }  
        }  

        @Override  
        protected ServletResponse wrapServletResponse(HttpServletResponse orig, ShiroHttpServletRequest request) {  
          return new MyShiroHttpServletResponse(orig, getServletContext(), request);  
        }  
    }
}

然后自定义一个权限控制类MyShiro,用来管理用户角色权限的。

package com.kj.filter;

import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.kj.domain.Role;
import com.kj.domain.User;
import com.kj.service.IUserService;

/**
 * 
 * @author KJ
 * @since 2016-8-15
 */
@Service
@Transactional
public class MyShiro extends AuthorizingRealm {

    @Autowired
    private IUserService userService;

    /**
     * 权限认证
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获取登录时输入的用户名
        String loginName = (String) principalCollection.fromRealm(getName()).iterator().next();
        // 到数据库查是否有此对象
        User user = userService.selectUserRoleByUserName(loginName);
        if (user != null) {
            // 权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            // 用户的角色集合名字
            info.setRoles(user.getRolesName());
            // 用户的角色对应的所有权限,如果只使用角色定义访问权限,下面的四行可以不要
            List<Role> roleList = user.getRoles();
            for (Role role : roleList) {
                info.addStringPermissions(role.getPermissionsName());
            }
            return info;
        }
        return null;
    }

    /**
     * 登录认证;
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // UsernamePasswordToken对象用来存放提交的登录信息
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 查出是否有此用户
        User user = userService.selectUserRoleByUserName(token.getUsername());
        if (user != null) {
            // 若存在,将此用户存放到登录认证info中
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
        }
        return null;
    }

}

至此,所有的java代码都已经给出。下面来给出相应的配置文件

首先是web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>Archetype Created Web Application</display-name>

    <!-- 读取spring配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:application.xml
            classpath:applicationContext-shiro.xml
        </param-value>
    </context-param>
    <!-- 设计路径变量值 -->
    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>springmvc.root</param-value>
    </context-param>

    <!-- Spring字符集过滤器 -->
    <filter>
        <filter-name>SpringEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SpringEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 添加日志监听器 -->
    <context-param>
        <param-name>logbackConfigLocation</param-name>
        <param-value>classpath:logback.xml</param-value>
    </context-param>
    <listener>
        <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
    </listener>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- springMVC核心配置 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--spingMVC的配置路径 -->
            <param-value>classpath:springmvc/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 拦截设置 -->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- Shiro配置 -->    
    <filter>    
        <filter-name>shiroFilter</filter-name>    
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>    
    </filter>    
    <filter-mapping>    
        <filter-name>shiroFilter</filter-name>    
        <url-pattern>/*</url-pattern>    
    </filter-mapping>  

    <!-- 错误跳转页面 -->
    <error-page>
        <!-- 路径不正确 -->
        <error-code>404</error-code>
        <location>/WEB-INF/errorpage/404.jsp</location>
    </error-page>
    <error-page>
        <!-- 没有访问权限,访问被禁止 -->
        <error-code>405</error-code>
        <location>/WEB-INF/errorpage/405.jsp</location>
    </error-page>
    <error-page>
        <!-- 内部错误 -->
        <error-code>500</error-code>
        <location>/WEB-INF/errorpage/500.jsp</location>
    </error-page>
</web-app>

然后是application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xsi:schemaLocation="  
           http://www.springframework.org/schema/beans  
           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
           http://www.springframework.org/schema/aop  
           http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
           http://www.springframework.org/schema/context  
           http://www.springframework.org/schema/context/spring-context-4.0.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
           http://www.springframework.org/schema/cache  
           http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">

    <!-- 以下 validator  ConversionService 在使用 mvc:annotation-driven 会 自动注册-->
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    </bean>

     <!-- 引入jdbc配置文件 -->  
     <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
               <value>classpath:properties/*.properties</value>
                <!--要是有多个配置文件,只需在这里继续添加即可 -->
            </list>
        </property>
    </bean>

    <!-- 扫描注解Bean -->
    <context:component-scan base-package="com.kj.service">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 激活annotation功能 -->
    <context:annotation-config />
    <!-- 激活annotation功能 -->
    <context:spring-configured />
    <!-- 注解事务配置 -->

    <!-- 类型转换及数据格式化 -->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"/>    

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">  
        <!-- 基本属性 url、user、password -->  
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>  
        <property name="username" value="${jdbc.username}"/>  
        <property name="password" value="${jdbc.password}"/>  

        <!-- 配置初始化大小、最小、最大 -->  
        <property name="initialSize" value="${ds.initialSize}"/>  
        <property name="minIdle" value="${ds.minIdle}"/>  
        <property name="maxActive" value="${ds.maxActive}"/>  

        <!-- 配置获取连接等待超时的时间 -->  
        <property name="maxWait" value="${ds.maxWait}"/>  

        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
        <property name="timeBetweenEvictionRunsMillis" value="${ds.timeBetweenEvictionRunsMillis}"/>  

        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
        <property name="minEvictableIdleTimeMillis" value="${ds.minEvictableIdleTimeMillis}"/>  

        <property name="validationQuery" value="SELECT 'x'"/>  
        <property name="testWhileIdle" value="true"/>  
        <property name="testOnBorrow" value="false"/>  
        <property name="testOnReturn" value="false"/>  

        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->  
        <property name="poolPreparedStatements" value="false"/>  
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20"/>  

        <!-- 配置监控统计拦截的filters -->  
        <property name="filters" value="stat"/>  
    </bean>  

    <!-- 自动扫描了所有的XxxxMapper.xml对应的mapper接口文件,这样就不用一个一个手动配置Mpper的映射了,只要Mapper接口类和Mapper映射文件对应起来就可以了。 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage"
            value="com.kj.dao" />
    </bean>

    <!-- 配置Mybatis的文件 ,mapperLocations配置**Mapper.xml文件位置,configLocation配置mybatis-config文件位置-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath*:com/kj/mapper/*.xml"/>  
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />
    </bean>

<!-- 对dataSource 数据源进行事务管理 -->  
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 事务管理 通知 -->  
    <tx:advice id="txAdvice" transaction-manager="transactionManager">  
        <tx:attributes>  
            <!-- 对insert,update,delete 开头的方法进行事务管理,只要有异常就回滚 -->  
            <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
            <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
            <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
            <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>  
            <!-- select,count开头的方法,开启只读,提高数据库访问性能 -->  
            <tx:method name="select*" read-only="true"/>  
            <tx:method name="count*" read-only="true"/>  
            <tx:method name="get*" read-only="true"/>  
            <!-- 对其他方法 使用默认的事务管理 -->  
            <tx:method name="*"/>  
        </tx:attributes>  
    </tx:advice>  

    <!-- 事务 aop 配置 -->  
    <aop:config>  
        <aop:pointcut id="serviceMethods" expression="execution(* com.kj.service..*(..))"/>  
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>  
    </aop:config>  

    <!-- 配置使Spring采用CGLIB代理 -->  
    <aop:aspectj-autoproxy proxy-target-class="true"/>  

    <!-- 启用对事务注解的支持 -->  
    <tx:annotation-driven transaction-manager="transactionManager"/>  

    <!-- Cache配置 -->  
    <cache:annotation-driven cache-manager="cacheManagers"/> 
    <bean id="ehCacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
        <property name="configLocation" value="classpath:ehcache.xml" />  
    </bean>  

    <bean id="cacheManagers" class="org.springframework.cache.ehcache.EhCacheCacheManager">      
        <property name="cacheManager"  ref="ehCacheManagerFactory"/>      
    </bean>  
</beans>

jdbc.properties数据库连接配置

##JDBC Global Setting  
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/quick4j?useUnicode=true&amp;characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root

##DataSource Global Setting  

#配置初始化大小、最小、最大  
ds.initialSize=1
ds.minIdle=1
ds.maxActive=20

#配置获取连接等待超时的时间   
ds.maxWait=60000

#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒  
ds.timeBetweenEvictionRunsMillis=60000

#配置一个连接在池中最小生存的时间,单位是毫秒  
ds.minEvictableIdleTimeMillis=300000

mybatis-config.xml 分页插件配置

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
"http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>    
<!-- 
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
    properties?, settings?, 
    typeAliases?, typeHandlers?, 
    objectFactory?,objectWrapperFactory?, 
    plugins?, 
    environments?, databaseIdProvider?, mappers?
-->
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
        <!-- 该参数默认为false -->
        <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
        <!-- 和startPage中的pageNum效果一样-->
        <property name="offsetAsPageNum" value="true"/>
        <!-- 该参数默认为false -->
        <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
        <property name="rowBoundsWithCount" value="true"/>
        <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
        <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
        <property name="pageSizeZero" value="true"/>
        <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
        <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
        <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
        <property name="reasonable" value="true"/>
        <!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
        <!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
        <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 -->
        <!-- 不理解该含义的前提下,不要随便复制该配置 -->
        <property name="params" value="pageNum=start;pageSize=limit;"/>
    </plugin>
</plugins>
</configuration>

spring缓存配置ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

<diskStore path="java.io.tmpdir"/>

<!--
Mandatory Default Cache configuration. These settings will be applied to caches
created programmtically using CacheManager.add(String cacheName)
-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>

applicationContext-shiro.xml shiro权限框架配置文件

<?xml version="1.0" encoding="UTF-8" ?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xmlns:aop="http://www.springframework.org/schema/aop"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="  
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd  
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  

    <bean id="myShiro" class="com.kj.filter.MyShiro" />

    <!-- 配置权限管理器 -->  
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">    
        <!-- 使用下面配置的缓存管理器 -->  
        <property name="cacheManager" ref="cacheManager"/>   
        <property name="sessionMode" value="native"/>
        <!-- ref对应我们写的realm  MyShiro -->  
        <property name="realm" ref="myShiro"/>    
        <property name="sessionManager" ref="sessionManager"/>
    </bean>  
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
             will be creaed with a default config:                -->
       <!-- <property name="cacheManager" ref="cacheManagers"/>-->
        <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml" />
        <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
             a specific Ehcache configuration to be used, specify that here.  If you don't, a default
             will be used.:
        <property name="cacheManagerConfigFile" value="classpath:some/path/to/ehcache.xml"/> -->
    </bean>
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="sessionDAO" ref="sessionDAO"/>
    </bean>
    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
    </bean>


    <!-- Shiro生命周期处理器 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />  

    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->  
    <bean id="shiroFilter" class="com.kj.filter.MyShiroFilterFactoryBean">   
        <!-- 调用我们配置的权限管理器 -->   
        <property name="securityManager" ref="securityManager"/>   
        <!-- 配置我们的登录请求地址 -->   
        <property name="loginUrl" value="/login"/>    
        <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 -->  
        <property name="successUrl" value="/user/"/>    
        <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->  
        <property name="unauthorizedUrl" value="/unauthorizedUrl"/>    
        <!-- 权限配置 -->  
        <property name="filterChainDefinitions">    
            <value>    
            <!--
                anon:例子/admins/**=anon 没有参数,表示可以匿名使用。 
                authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数 
                roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。 
                perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 
                rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。 
                port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。 
                authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证 
                ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https 
                user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

                <shiro:authenticated> 登录之后
                <shiro:notAuthenticated> 不在登录状态时
                <shiro:guest> 用户在没有RememberMe时
                <shiro:user> 用户在RememberMe时
                <shiro:hasAnyRoles name="abc,123" > 在有abc或者123角色时
                <shiro:hasRole name="abc"> 拥有角色abc
                <shiro:lacksRole name="abc"> 没有角色abc
                <shiro:hasPermission name="abc"> 拥有权限abc
                <shiro:lacksPermission name="abc"> 没有权限abc
                <shiro:principal> 显示用户登录名
                -->  

                <!-- anon表示此地址不需要任何权限即可访问 -->  
                /static/**=anon  
                /logout=logout
                <!-- perms["user:create,user:query"]当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 -->  
               <!-- /user/create/query/=perms["user:create,user:query"] -->
                <!-- roles[admin,manager]表示访问此连接需要用户同时拥有admin和manager两个角色 -->  
                /user/add/**=roles[admin,manager]  
                /user/del/**=perms[user:create]  
                /user/edit/**=roles[admin]  
                <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login-->    
                /** = authc  
            </value>    
        </property>    
    </bean>  

</beans>  

shiro缓存配置文件ehcache-shiro.xml

<ehcache>
    <diskStore path="java.io.tmpdir/tuan-oauth"/>


    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />
    <!-- We want eternal="true" (with no timeToIdle or timeToLive settings) because Shiro manages session
expirations explicitly.  If we set it to false and then set corresponding timeToIdle and timeToLive properties,
ehcache would evict sessions without Shiro's knowledge, which would cause many problems
(e.g. "My Shiro session timeout is 30 minutes - why isn't a session available after 2 minutes?"
Answer - ehcache expired it due to the timeToIdle property set to 120 seconds.)
diskPersistent=true since we want an enterprise session management feature - ability to use sessions after
even after a JVM restart.  -->
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           eternal="true"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>
    <cache name="shiro.authorizationCache"
           maxElementsInMemory="100"
           eternal="false"
           timeToLiveSeconds="600"
           overflowToDisk="false"/>
</ehcache>

spring mvc配置文件spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!-- 扫描controller(controller层注入) -->
    <context:component-scan base-package="com.kj.controller" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <!-- 会自动注册了validator  ConversionService  -->
    <mvc:annotation-driven validator="validator" conversion-service="conversionService" content-negotiation-manager="contentNegotiationManager">
        <mvc:message-converters register-defaults="true">
            <!-- StringHttpMessageConverter编码为UTF-8,防止乱码 -->
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8"/>
                <property name = "supportedMediaTypes">
                    <list>
                        <bean class="org.springframework.http.MediaType">
                            <constructor-arg index="0" value="text"/>
                            <constructor-arg index="1" value="plain"/>
                            <constructor-arg index="2" value="UTF-8"/>
                        </bean>
                        <bean class="org.springframework.http.MediaType">
                            <constructor-arg index="0" value="*"/>
                            <constructor-arg index="1" value="*"/>
                            <constructor-arg index="2" value="UTF-8"/>
                        </bean>
                    </list>
                </property>
            </bean>
            <!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
            <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
                <!--<property name="serializerFeature">-->
                <!--这个地方加上这个功能吧,能自己配置一些东西,比如时间的格式化,null输出""等等-->
                <!--</property>-->
            </bean>
        </mvc:message-converters>

        <mvc:argument-resolvers>
            <bean class="org.springframework.data.web.PageableHandlerMethodArgumentResolver" />
        </mvc:argument-resolvers>
    </mvc:annotation-driven>


    <!-- 内容协商管理器  -->
    <!--1、首先检查路径扩展名(如my.pdf);2、其次检查Parameter(如my?format=pdf);3、检查Accept Header-->
    <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <!-- 扩展名至mimeType的映射,即 /user.json => application/json -->
        <property name="favorPathExtension" value="true"/>
        <!-- 用于开启 /userinfo/123?format=json 的支持 -->
        <property name="favorParameter" value="true"/>
        <property name="parameterName" value="format"/>
        <!-- 是否忽略Accept Header -->
        <property name="ignoreAcceptHeader" value="false"/>

        <property name="mediaTypes"> <!--扩展名到MIME的映射;favorPathExtension, favorParameter是true时起作用  -->
            <value>
                json=application/json
                xml=application/xml
                html=text/html
            </value>
        </property>
        <!-- 默认的content type -->
        <property name="defaultContentType" value="text/html"/>
    </bean>

    <!-- 当在web.xml 中   DispatcherServlet使用 <url-pattern>/</url-pattern> 映射时,能映射静态资源 -->
    <mvc:default-servlet-handler />  
    <!-- 静态资源映射 -->
    <mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>


    <!-- 对模型视图添加前后缀 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/view/" p:suffix=".jsp"/>


    <!-- 配置springMVC处理上传文件的信息 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="defaultEncoding" value="utf-8"/>
        <property name="maxUploadSize" value="10485760000"/>
        <property name="maxInMemorySize" value="40960"/>
    </bean>

    <!-- 这里设置静态的资源 -->
    <!--    <mvc:resources location="/static/" mapping="/static/**" /> -->


</beans>

日志输出 配置logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 尽量别用绝对路径,如果带参数不同容器路径解释可能不同,以下配置参数在pom.xml里 -->
    <property name="log.root.level" value="${log.root.level}" /> <!-- 日志级别 -->
    <property name="log.other.level" value="${log.other.level}" /> <!-- 其他日志级别 -->
    <property name="log.base" value="${log.base}" /> <!-- 日志路径,这里是相对路径,web项目eclipse下会输出到eclipse的安装目录下,如果部署到linux上的tomcat下,会输出到tomcat/bin目录 下 -->
    <property name="log.moduleName" value="${log.moduleName}" />  <!-- 模块名称, 影响日志配置名,日志文件名 -->
    <property name="log.max.size" value="100MB" /> <!-- 日志文件大小,超过这个大小将被压缩 -->

    <!--控制台输出 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level[%thread]%logger{56}.%method\(\):%L -%msg%n</Pattern>
        </encoder>
    </appender>

    <!-- 用来保存输出所有级别的日志 -->
    <appender name="file.all" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${log.base}/${log.moduleName}.log</File><!-- 设置日志不超过${log.max.size}时的保存路径,注意如果 
            是web项目会保存到Tomcat的bin目录 下 -->
        <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.base}/archive/${log.moduleName}_all_%d{yyyy-MM-dd}.%i.log.zip
            </FileNamePattern>
            <!-- 文件输出日志 (文件大小策略进行文件输出,超过指定大小对文件备份) -->
            <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${log.max.size}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 日志输出的文件的格式 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level[%thread]%logger{56}.%method\(\):%L -%msg%n</pattern>
        </layout>
    </appender>

    <!-- 这也是用来保存输出所有级别的日志 -->
    <appender name="file.all.other" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${log.base}/${log.moduleName}_other.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.base}/archive/${log.moduleName}_other_%d{yyyy-MM-dd}.%i.log.zip
            </FileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${log.max.size}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{56}.%method\(\):%L -%msg%n</pattern>
        </layout>
    </appender>

    <!-- 只用保存输出error级别的日志 -->
    <appender name="file.error"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${log.base}/${log.moduleName}_err.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${log.base}/archive/${log.moduleName}_err_%d{yyyy-MM-dd}.%i.log.zip
            </FileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>${log.max.size}</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{56}.%method\(\):%L - %msg%n</pattern>
        </layout>
        <!-- 下面为配置只输出error级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

   <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
    <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
    <!-- 添加附加的appender,最多只能添加一个 -->
    <appender name="file.async" class="ch.qos.logback.classic.AsyncAppender">
        <discardingThreshold>0</discardingThreshold>
        <queueSize>256</queueSize>
        <includeCallerData>true</includeCallerData>
        <appender-ref ref="file.all" />
    </appender>

    <appender name="file.async.other" class="ch.qos.logback.classic.AsyncAppender">
        <discardingThreshold>0</discardingThreshold>
        <queueSize>256</queueSize>
        <includeCallerData>true</includeCallerData>
        <appender-ref ref="file.all.other" />
    </appender>

    <!-- 为某个包下的所有类的指定Appender 这里也可以指定类名称例如:com.aa.bb.ClassName -->
    <logger name="com.kj" additivity="false">
        <level value="${log.root.level}" />
        <appender-ref ref="stdout" /> 
        <appender-ref ref="file.async" /><!-- 即com.kj包下级别为 ${log.root.level}的才会使用file.async来打印 -->
        <appender-ref ref="file.error" />
    </logger>

    <!-- root将级别为${log.root.level}及大于${log.root.level}的日志信息交给已经配置好的名为“Console”的appender处理,“Console”appender将信息打印到Console,其它同理 -->
    <root level="${log.root.level}">
        <appender-ref ref="stdout" /> <!--  标识这个appender将会添加到这个logger -->
        <appender-ref ref="file.async.other" />
        <appender-ref ref="file.error" />
    </root>
</configuration>

至此,所有的配置文件已给出,下面给出相应的jsp页面

首先是登录页面login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
<html>  
  <head>  
    <title>登录页面</title>  
  </head>  

  <body>  
    <h1>登录页面----${message }</h1>  
    <img alt="" src="<%=request.getContextPath()%>/static/image/1.jpg"> 
    <br/><br/>
    <form action="<%=request.getContextPath()%>/login" method="post">  
        用户名:<input name="username" value="${username }"/><br/><br/> 
        密 &nbsp;&nbsp;码:<input type="password" name="password" value="${password }"/><br/><br/>
      <input type="submit" value="提交"/>
    </form>  
  </body>  
</html>  

如果登录成功,跳转到view目录下的userList.jsp文件

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> 
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bootstrap分页实例用户列表</title>
<link href="${pageContext.request.contextPath}/static/js/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<script src="${pageContext.request.contextPath}/static/js/jQuery/jquery-2.1.4.min.js"></script>
<script src="${pageContext.request.contextPath}/static/js/bootstrap/js/bootstrap.min.js"></script>
<script src="${pageContext.request.contextPath}/static/js/bootstrap/js/bootstrap-paginator.min.js"></script>
<style type="text/css">
#queryDiv {
 margin-right: auto;
 margin-left: auto;
 width:600px;
}
#textInput {
 margin-top: 10px;
}
#tableResult {
 margin-right: auto;
 margin-left: auto;
 width:600px;
}
td {
 width:150px
}
</style>
</head>
<body>

    <h1>用户列表--<a href="${pageContext.request.contextPath}/user/add">添加用户</a>---<a href="${pageContext.request.contextPath}/logout">退出登录</a>    </h1> 
    <h2>权限列表</h2>  
    <shiro:authenticated>用户已经登录显示此内容<br/></shiro:authenticated>  
    <shiro:hasRole name="manager">manager角色登录显示此内容<br/></shiro:hasRole>  
    <shiro:hasRole name="admin">admin角色登录显示此内容<br/></shiro:hasRole>  
    <shiro:hasRole name="normal">normal角色登录显示此内容<br/></shiro:hasRole>  

    <shiro:hasAnyRoles name="manager,admin">**manager or admin 角色用户登录显示此内容**<br/></shiro:hasAnyRoles>  
    <shiro:principal/>-显示当前登录用户名  <br/>
    <shiro:hasPermission name="add">add权限用户显示此内容<br/></shiro:hasPermission>  
    <shiro:hasPermission name="user:create">user:create权限用户显示此内容<br/><shiro:principal/></shiro:hasPermission>  
    <shiro:lacksPermission name="user:del"> 不具有user:del权限的用户显示此内容<br/> </shiro:lacksPermission>  

    <div id = "queryDiv">
        <input id = "textInput" type="text" placeholder="请输入用户名" >
        <button id = "queryButton" class="btn btn-primary" type="button">查询</button>
    </div>
    <form id="form1">
        <table class="table table-bordered" id = 'tableResult'>
            <caption>查询用户结果</caption>
            <thead>
                <tr>
                    <th>序号</th>
                    <th>用户名</th>
                    <th>密码</th>
                    <th>状态</th>
                    <th width="200px">创建时间</th>
                </tr>
            </thead>
            <tbody id="tableBody">
            </tbody>
        </table>
        <!-- 底部分页按钮 -->
        <div id="bottomTab"></div>
    </form>
    <script type='text/javascript'>    
        var PAGESIZE = 10;
        var options = {  
            currentPage: 1,  //当前页数
            totalPages: 10,  //总页数,这里只是暂时的,后头会根据查出来的条件进行更改
            size:"normal",  
            alignment:"center",  
            itemTexts: function (type, page, current) {  
                switch (type) {  
                    case "first":  
                        return "第一页";  
                    case "prev":  
                        return "前一页";  
                    case "next":  
                        return "后一页";  
                    case "last":  
                        return "最后页";  
                    case "page":  
                        return  page;  
                }                 
            },  
            onPageClicked: function (e, originalEvent, type, page) {  
                var userName = $("#textInput").val(); //取内容
                buildTable(userName,page,PAGESIZE);//默认每页最多10条
            }  
        }  

        //获取当前项目的路径
        var urlRootContext = (function () {
            var strPath = window.document.location.pathname;
            var postPath = strPath.substring(0, strPath.substr(1).indexOf('/') + 1);
            return postPath;
        })();


        //生成表格
        function buildTable(userName,pageNumber,pageSize) {
             var url =  urlRootContext + "/user/list"; //请求的网址
             var reqParams = {'userName':userName, 'pageNumber':pageNumber,'pageSize':pageSize};//请求数据
             $(function () {   
                  $.ajax({
                        type:"POST",
                        url:url,
                        data:reqParams,
                        async:false,
                        dataType:"json",
                        success: function(data){
                            if(data.isError == false) {
                           // options.totalPages = data.pages;
                        var newoptions = {  
                        currentPage: data.pageNum < 1 ? 1 : data.pageNum,  //当前页数
                        totalPages: data.pages==0?1:data.pages,  //总页数
                        size:"normal",  
                        alignment:"center",  
                        itemTexts: function (type, page, current) {  
                        switch (type) {  
                            case "first":  
                            return "第一页";  
                            case "prev":  
                            return "前一页";  
                            case "next":  
                            return "后一页";  
                            case "last":  
                            return "最后页";  
                        case "page":  
                        return  page;  
                }                 
            },  
            onPageClicked: function (e, originalEvent, type, page) {  
                var userName = $("#textInput").val(); //取内容
                buildTable(userName,page,PAGESIZE);//默认每页最多10条
            }  
         }                         
         $('#bottomTab').bootstrapPaginator("setOptions",newoptions); //重新设置总页面数目
         var dataList = data.dataList;
         $("#tableBody").empty();//清空表格内容
         if (dataList.length > 0 ) {
             $(dataList).each(function(){//重新生成
                    $("#tableBody").append('<tr>');
                    $("#tableBody").append('<td>' + this.id + '</td>');
                    $("#tableBody").append('<td>' + this.username + '</td>');
                    $("#tableBody").append('<td>' + this.password + '</td>');
                    $("#tableBody").append('<td>' + this.state + '</td>');
                    $("#tableBody").append('<td>' + this.create_time + '</td>');
                    $("#tableBody").append('</tr>');
                    });  
                    } else {                                
                          $("#tableBody").append('<tr><th colspan ="4"><center>查询无数据</center></th></tr>');
                    }
                    }else{
                          alert(data.errorMsg);
                            }
                      },
                        error: function(e){
                           alert("查询失败:" + e);
                        }
                    });
               });
        }

        //渲染完就执行
        $(function() {

            //生成底部分页栏
            $('#bottomTab').bootstrapPaginator(options);     

            buildTable("",1,10);//默认空白查全部

            //创建结算规则
            $("#queryButton").bind("click",function(){
                var userName = $("#textInput").val();  
                buildTable(userName,1,PAGESIZE);
            });
        });
    </script>
</body>
</html>

当访问没有权限的路径,系统会自动跳转到没有权限的提示页面unauthorizedTip.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<script>

</script>
<body>
<h2>对不起,您没有权限访问该资源路径,请联系管理授权。</h2>
<a href="login">重新登录</a>
</body>
</html>

至此,所有文件代码均已提供,运行tomcat,访问地址:http://localhost:8080/HelloHome/login即可看到登录页:
这里写图片描述

登录成功后跳转到用户分页列表如下:
这里写图片描述

Logo

快速构建 Web 应用程序

更多推荐