SSM权限管理系统

时间:2024-01-30 18:21:28
    1    SSM权限管理系统-项目环境搭建
在创建Maven项目时,添加archetypeCatalog=internal
配置pom.xml的mvean依赖,添加逆向生成工具.
创建需要的目录domain,mapper,service,web,resources
替换web.xml,修改为3.0的约束
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0"
         metadata-complete="false">
    <absolute-ordering/>
    <display-name>web</display-name>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!--配置前端控制器-->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--加载的主配置文件-->
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <!-- 项目启动就加载框架 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!-- 编码过滤器 -->
    <filter>
        <filter-name>CharacterEncoding</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>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>


    2.    SSM权限管理-EasyUI主页框架
EasyUI主页
1.在目录当中引入EasyUI相关JS与css
    
2.在首页当中引入所需要的js与css
    
 <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/static/plugins/easyui/uimaker/easyui.css">
 <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/static/plugins/easyui/uimaker/icon.css">
 <script type="text/javascript" src="${pageContext.request.contextPath}/static/plugins/easyui/jquery.min.js"></script>
 <script type="text/javascript" src="${pageContext.request.contextPath}/static/plugins/easyui/jquery.easyui.min.js"></script>
 <script type="text/javascript" src="${pageContext.request.contextPath}/static/plugins/easyui/easyui-lang-zh_CN.js"></script>
 <script type="text/javascript" src="${pageContext.request.contextPath}/static/js/index.js"></script>

3.编写body所首页框架格式
<body class="easyui-layout">
<%--顶部--%>
<div data-options="region:\'north\'" style="height:100px; background: #ec4e00; padding: 20px 20px">
    <img src="static/images/main_logo.png" alt="">
</div>
<%--底部--%>
<div data-options="region:\'south\'" style="height:50px; border-bottom: 3px solid #ec4e00">
    <p align="center" style="font-size: 14px">撩课学院</p>
</div>
<%--左侧菜单--%>
<div data-options="region:\'west\',split:true" style="width:300px;">
    <div id="aa" class="easyui-accordion" data-options="fit:true">
        <div title="菜单" data-options="iconCls:\'icon-save\',selected:true" style="overflow:auto;padding:10px;">
            <!--tree-->
            <ul id="tree"></ul>
        </div>
        <div title="公告" data-options="iconCls:\'icon-reload\'" style="padding:10px;">
        </div>
    </div>
</div>
<%--右侧主区域--%>
<div data-options="region:\'center\'" style="background:#eee;">
    <!--标签-->
    <div id="tabs" style="overflow: hidden" ></div>
</div>
</body>

4.创建首页index.js引入
    $(function () {
        $("#tabs").tabs({
            fit:true
        })
    $(\'#tree\').tree({
            url:\'/getTreeData\',
            lines:true,
            onSelect: function(node){
                /*在添加之前, 做判断  判断这个标签是否存在 */
                var exists =   $("#tabs").tabs("exists",node.text);
                if(exists){
                    /*存在,就让它选中*/
                    $("#tabs").tabs("select",node.text);
                }else {
                    if (node.url !=\'\'&& node.url !=null){
                        /*如果不存在 ,添加新标签*/
                        $("#tabs").tabs("add",{
                            title:node.text,
                            /*href:node.attributes.url,*/  /*href  引入的是body当中*/
                            content:"<iframe src="+node.url+" frameborder=\'0\' width=\'100%\' height=\'100%\'></iframe>",
                            closable:true
                        })
                    }
                }
            },
            onLoadSuccess: function (node, data) {
                console.log(data[0].children[0].id);
                if (data.length > 0) {
                    //找到第一个元素
                    var n = $(\'#tree\').tree(\'find\', data[0].children[0].id);
                    //调用选中事件
                    $(\'#tree\').tree(\'select\', n.target);
                }
            }
    
        });
    });


    3.    菜单标签跳转
创建3个controller
EmployeeController    MenuController    RoleController
控制拦截url为employee,role,menu

跳转到相应的页面,比如EmployeeController    
@Controller
public class EmployeeController {
    @RequestMapping("/employee")
    public String employee(){
        return "employee";
    }
}

index.js上面的
        onLoadSuccess: function (node, data) {
            console.log(data[0].children[0].id);
            if (data.length > 0) {
                //找到第一个元素
                var n = $(\'#tree\').tree(\'find\', data[0].children[0].id);
                //调用选中事件
                $(\'#tree\').tree(\'select\', n.target);
            }
        }
    });
表示在页面初始时调用一个元素.让页面初始时右侧不为空.
注意需要清空缓存.

    4.    主页数据表格添加
将共用的scrpit和link抽成一个包.
common.jsp
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/static/plugins/easyui/uimaker/easyui.css">
<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/static/plugins/easyui/uimaker/icon.css">
<script type="text/javascript" src="${pageContext.request.contextPath}/static/plugins/easyui/jquery.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/plugins/easyui/jquery.easyui.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/plugins/easyui/easyui-lang-zh_CN.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/js/base.js"></script>

在index.jsp中引用

<head>
    <title>权限管理系统</title>
    <%@include file="/static/common/common.jsp"%>
    <script type="text/javascript" src="${pageContext.request.contextPath}/static/js/index.js"></script>
</head>
index.js属于index.jsp独有的.

在employee.jsp中引用
<head>
    <title>Title</title>
    <%@include file="/static/common/common.jsp"%>
</head>

给employee页面添加employee.js
$(function(){
    $("#dg").datagrid({
        url:"/employeeList",
        columns:[[
            {field:\'username\',title:\'姓名\',width:100,align:\'center\'},
            {field:\'inputtime\',title:\'入职时间\',width:100,align:\'center\'},
            {field:\'tel\',title:\'电话\',width:100,align:\'center\'},
            {field:\'email\',title:\'邮箱\',width:100,align:\'center\'},
            {field:\'department\',title:\'部门\',width:100,align:\'center\'},
            {field:\'state\',title:\'状态\',width:100,align:\'center\'},
            {field:\'admin\',title:\'管理员\',width:100,align:\'center\'},
        ]],
        fit:true,   /*是否需要填满*/
        fitColumns:true,    /*是否自动伸缩*/
        rownumbers:true,    /*是否需要显示行号*/
        pagination:true     /*是否需要分页*/
    })

});


    5.    员工表建立
设计数据库的表.
建立新的数据库promission
修改逆向工程的generatorConfig.xml,修改配置中的数据库名和表名.
逆向生成Employee,EmployeeMapper,EmployeeMapper.xml
注意把EmployeeMapper.xml需要放在resources下面新建的com.itlike.mapper.

    6.    员工列表查询
创建一个业务接口service,employeeService.实现类employeeServiceImpl.
将其标记为@Service
public class employeeServiceImpl implements employeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    @Override
    public void getEmployee() {
        System.out.println("来到业务层");
        /*调用mapper查询员工*/
        employeeMapper.selectAll();
    }
}

修改controller层,注入service,添加@RequestMapping("/employeeList")
EmployeeController
@Controller
public class EmployeeController {
    /*注入业务层*/
    @Autowired
    private employeeService employeeService;

    @RequestMapping("/employee")
    public String employee(){
        return "employee";
    }

    @RequestMapping("/employeeList")
    public void employeeList(){
        /*调用业务层查询员工*/
        employeeService.getEmployee();
    }
}

注意修改db.properties下面的数据库名jdbc.url=jdbc:mysql://localhost:3306/promission?characterEncoding=utf-8


新建一个PageListRes的domain类.
由于easyUI的datagrid需要有total分页和rows数组类型的json
PageListRes类.
@Getter@Setter
public class PageListRes {
    private Long total;
    private List<?> rows=new ArrayList<>();
}


修改employeeServiceImpl,使用pageHelper来进行分页
@Service
public class employeeServiceImpl implements employeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    @Override
    public PageListRes getEmployee() {
        /*调用mapper查询员工*/
        /*设置分页total*/
        Page<Object> page = PageHelper.startPage(1, 5);
        List<Employee> employees = employeeMapper.selectAll();
        /*封装成pageList*/
        PageListRes pageListRes = new PageListRes();
        pageListRes.setTotal(page.getTotal());
        pageListRes.setRows(employees);
        return pageListRes;
    }
}
修改接口employeeService,将返回值类型改为PageListRes 

前端控制器修改EmployeeController
@Controller
public class EmployeeController {
    /*注入业务层*/
    @Autowired
    private employeeService employeeService;
    @RequestMapping("/employeeList")
    @ResponseBody
    public PageListRes employeeList(){
        /*调用业务层查询员工*/
        PageListRes pageListRes = employeeService.getEmployee();
        return pageListRes;
    }
使用@ResponseBody获取json数据


    7.    日期格式化
由于上一节已经可以取到数据,并且显示在前端页面,但是日期格式不对,需要格式化日期数据
在employee.js做数据格式化
$(function(){
    $("#dg").datagrid({
        url:"/employeeList",
        columns:[[
            {field:\'username\',title:\'姓名\',width:100,align:\'center\'},
            {field:\'inputtime\',title:\'入职时间\',width:100,align:\'center\'},
            {field:\'tel\',title:\'电话\',width:100,align:\'center\'},
            {field:\'email\',title:\'邮箱\',width:100,align:\'center\'},
            {field:\'department\',title:\'部门\',width:100,align:\'center\'},
            {field:\'state\',title:\'状态\',width:100,align:\'center\',formatter:function (value,row,index) {
                    if (row.state){
                        return "在职";
                    }else {
                        return "<font style=\'color:red\'>离职</font>"
                    }
                }},
            {field:\'admin\',title:\'管理员\',width:100,align:\'center\',formatter:function (value,row,index) {
                    if (row.admin){
                        return "是";
                    }else {
                        return "否";
                    }
                }},
        ]],
        fit:true,   /*是否需要填满*/
        fitColumns:true,    /*是否自动伸缩*/
        rownumbers:true,    /*是否需要显示行号*/
        pagination:true     /*是否需要分页*/
    })
});
添加管理员和状态的判断,true为在职和是,false为离职和否.

在domain的实体类中添加注解
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date inputtime;
设置时间格式为GMT+8小时.
或者使用    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

    8.    部门表创建
包含id和name,在employee表建立外键dep_id
参考表department参考字段id.
逆向生成domain和mapper,mapper.xml.
将xml放在resources下面.


    9.    员工部门关联查询.
修改Employee的部门字段,需要修改为和employee.js的字段相同.
    private Department department;
修改数据库查询语句,使用左连接当两个表id相等时.
  <select id="selectAll" resultMap="BaseResultMap" >
select e.id, e.username, e.inputtime, e.tel, e.email, e.state, e.admin ,
d.id as d_id,
d.`name` as d_name
from employee as e
LEFT JOIN department as d
ON e.dep_id=d.id;
  </select>

修改 <result column="dep_id" property="dep_id" jdbcType="bigint" />为下面的
注意columnPrefix="d_",因为上面使用了as修改了别名,所有要加上d_,不然无法映射
      <association property="department" javaType="com.itlike.domain.Department" columnPrefix="d_">
          <result property="id" column="id"/>
          <result property="name" column="name"/>
      </association>

实现了查询部门名称,接下来再在前端去使用数据.
注意需要删除上面查询语句中的dep_id
注意SQL语句结尾不能添加;  否则sql语句不会执行.

修改前端的js,如果有部门则显示名字,注意使用value.
            {field:\'department\',title:\'部门\',width:100,align:\'center\',formatter:function (value,row,index) {
                    if(value.name){
                        return value.name;
                    }else {
                        return "无部门";
                    }
                }},

在多表查询有字段名相同时,起一个别名,在封装时注意使用columnPrefix.


    10.    添加对话框搭建
列表添加工具栏目
employee.jsp
<body>
<div id="tb">
    <a href="#" class="easyui-linkbutton" data-options="iconCls:\'icon-add\',plain:true" id="add">添加</a>
    <a href="#" class="easyui-linkbutton" data-options="iconCls:\'icon-edit\',plain:true" id="edit">编辑</a>
    <a href="#" class="easyui-linkbutton" data-options="iconCls:\'icon-remove\',plain:true" id="delete">删除</a>
    <a href="#" class="easyui-linkbutton" data-options="iconCls:\'icon-reload\',plain:true" id="reload">刷新</a>
</div>
    <table id="dg"></table>
</body>

修改前端样式,添加对话框
<%--对话框--%>
<div id="dialog">
    <table align="center" style="border-spacing: 0px 10px">
        <tr>
            <td>用户名:</td>
            <td><input type="text" class="easyui-validatebox" data-options="required:true"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" class="easyui-validatebox" data-options="required:true"></td>
        </tr>
        <tr>
            <td>手机:</td>
            <td><input type="text" class="easyui-validatebox" ></td>
        </tr>
        <tr>
            <td>邮箱:</td>
            <td><input type="text" class="easyui-validatebox" ></td>
        </tr>
        <tr>
            <td>入职日期:</td>
            <td><input type="text" class="easyui-datebox"></td>
        </tr>
        <tr>
            <td>部门:</td>
            <td><select id="department"></select></td>
        </tr>
        <tr>
            <td>是否管理员:</td>
            <td><select id="state"></select></td>
        </tr>
    </table>
</div>

添加增删编辑刷新按钮.
employee.js 监听添加按钮点击
    /*对话框*/
    $("#dialog").dialog({
        width:350,
        height:350,
        closed:true        //初始默认为隐藏
    });
    /*监听添加按钮点击*/
    $("#add").click(function () {
        $("#dialog").dialog("open");        //监听点击时open
    })


    11.    下拉列表placeholder处理
修改jsp中的页面,将select改为input,添加placeholder属性
        <tr>
            <td>部门:</td>
            <td><input id="department" placeholder="请选择部门"/></td>
        </tr>
        <tr>
            <td>是否管理员:</td>
            <td><input id="state" placeholder="是否为管理员"/></td>
        </tr>

在js中添加下拉列表,选中id为state和department的行

    /*部门选择 下拉列表*/
    $("#department").combobox({
        width:150,
        panelHeight:\'auto\'
    });

    /*是否为管理员 下拉列表*/
    $("#state").combobox({
       width:150,
       panelHeight: \'auto\',
       textField:\'label\',
       valueField:\'value\',
       editable:false,        //editable 是否可以输入
       data:[{
           label:\'是\',
            value:\'true\'
        },{
           label:\'否\',
            value:\'false\'
        }],
        onLoadSuccess:function () {/*数据加载完毕后回调显示提示*/
           $("#state").each(function(i){
               var span =$(this).siblings("span")[i];
               var targetInput = $(span).find("input:first");
               if(targetInput) {
                   $(targetInput).attr("placeholder", $(this).attr("placeholder"));
               }
           })
        }
    });



    12.    下拉列表部门数据加载
先建立一个controller
DepartmentController
@Controller
public class DepartmentController {
    /*注入DepartmentService业务层*/
    @Autowired
    private DepartmentService departmentService;
    //部门下拉查询部门
    @RequestMapping("/departList")
    @ResponseBody
    public List<Department> departList(){
        List<Department> departmentList = departmentService.getDepartmentList();
        return departmentList;
    }
}
创建它依赖的业务层接口DepartmentService

创建业务层DepartmentServiceImpl
@Service
public class DepartmentServiceImpl implements DepartmentService {
    /*注入mapper*/
    @Autowired
    private DepartmentMapper departmentMapper;
    @Override
    public List<Department> getDepartmentList() {
        List<Department> departments = departmentMapper.selectAll();
        return departments;
    }
}

修改employee.jsp
   /*部门选择 下拉列表*/
    $("#department").combobox({
        width:150,
        panelHeight:\'auto\',
        editable:false,
        url:\'departList\',
        textField:\'name\',
        valueField:\'id\',
        onLoadSuccess:function () {/*数据加载完毕后回调显示提示*/
            $("#department").each(function(i){
                var span =$(this).siblings("span")[i];
                var targetInput = $(span).find("input:first");
                if(targetInput) {
                    $(targetInput).attr("placeholder", $(this).attr("placeholder"));
                }
            })
        }
    });

    13.    添加表单提交
修改对话框,添加两个按钮
    /*对话框*/
    $("#dialog").dialog({
        width:350,
        height:350,
        closed:true,
        buttons:[{
                /*提交表单*/
                $("#employeeForm").form("submit",{
                    url:"saveEmployee",
                    success:function(data){
                        console.log(data);
                    }
                });
            }
        },{
            text:\'关闭\',
            handler:function() {
            }
        }]
    });
给.jsp的对话框添加form表单属性
<%--对话框--%>
<div id="dialog">
    <form id="employeeForm">
    </form>
</div>

给对话框内的所有参数添加name属性
<td><input type="text" name="username" class="easyui-validatebox" data-options="required:true"></td>
<td><input type="text" name="password" class="easyui-validatebox" data-options="required:true"></td>

在EmployeeController层添加接收表单
    /*接收员工添加表单*/
    @RequestMapping("/saveEmployee")
    public void saveEmployee(Employee employee){
        System.out.println("提交表单成功");
        System.out.println(employee);
    }
让后台可以接收到属性

    14.    添加表单提交
由于前面设计表时没有添加密码,需要在数据库添加password字段
在controller层
    /*接收员工添加表单*/
    @RequestMapping("/saveEmployee")
    public void saveEmployee(Employee employee){
        /*调用业务层,保存用户*/
        employeeService.saveEmployee(employee);
    }

service层
@Transactional
    /*保存员工*/
    @Override
    public void saveEmployee(Employee employee) {
        employeeMapper.insert(employee);
    }

注意是添加,所以需要添加事务的注释.
修改mapper
    <!--添加员工-->
  <insert id="insert" parameterType="com.itlike.domain.Employee" >
    insert into employee (id, username, password, inputtime,
      tel, email, state, admin, 
      dep_id)
    values (#{id}, #{username},#{password}, #{inputtime},
      #{tel}, #{email}, #{state}, #{admin},
      #{department.id})
  </insert>
由于dep_id是从department.id获取的值,所以修改values.并且添加password字段.

state还未设置,需要添加设置在service层
    /*接收员工添加表单*/
    @RequestMapping("/saveEmployee")
    public void saveEmployee(Employee employee){
        /*调用业务层,保存用户*/
        employee.setState(true);
        employeeService.saveEmployee(employee);
    }

添加一个domain类AjaxRes,用来接收参数.
用来表明接收失败还是成功,还有message.
.parseJSON()函数
用于将格式完好的JSON字符串转为与之对应的JavaScript对象。
AjaxRes
@Component
@Getter@Setter@ToString
public class AjaxRes {
    private boolean success;
    private String msg;
}

EmployeeController
    @Autowired
    private AjaxRes ajaxRes;
    /*接收员工添加表单*/
    @RequestMapping("/saveEmployee")
    @ResponseBody
    public AjaxRes saveEmployee(Employee employee){
        try {
            /*调用业务层,保存用户*/
            employee.setState(true);
            employeeService.saveEmployee(employee);
            ajaxRes.setMsg("保存成功");
            ajaxRes.setSuccess(true);
        }catch (Exception e){
            ajaxRes.setSuccess(false);
            ajaxRes.setMsg("保存失败");
        }
        return ajaxRes;
    }
修改employee.js
                    success:function(data){
                        data = $.parseJSON(data);
                        if(data.success){
                            $.messager.alert("温馨提示",data.msg);
                            /*关闭对话框*/
                            $("#dialog").dialog("close");
                            /*重新加载数据表格*/
                            $("#dg").datagrid("reload");
                        }else {
                            $.messager.alert("温馨提示",data.msg);
                        }
                    }


    15.    编辑数据回显.
先添加关闭按钮的响应
            text:\'关闭\',
            handler:function() {
                $("#dialog").dialog("close");
            }
然后添加按钮的打开时清空.
    /*监听添加按钮点击*/
    $("#add").click(function () {
        $("#employeeForm").form("clear");    使用.form(clear)
        $("#dialog").dialog("open");
    });
-----------------------------------------------------------------------

    /*监听添加按钮点击*/
    $("#add").click(function () {
        $("#dialog").dialog("setTitle","编辑员工");
        /*清空对话框中的数据*/
        $("#employeeForm").form("clear");
        /*打开对话框*/
        $("#dialog").dialog("open");
    });

   /*员工数据列表*/
    $("#dg").datagrid({
        singleSelect:true,  /*只能选择一行*/
        striped:true,       /*斑马线显示,隔行变色*/}

如果需求是不能编辑密码,那么选择编辑时隐藏密码选项
在jsp页面给tr添加id为password
            <tr id="password">
                <td>密码:</td>
                <td><input type="text" name="password" class="easyui-validatebox" data-options="required:true"></td>
            </tr>

    /*监听编辑按钮点击*/
    $("#edit").click(function () {
        /*获取当前选中的行*/
        var rowData = $("#dg").datagrid("getSelected");
        console.log(rowData);
        if(!rowData){
            $.messager.alert("提示","请选择一行数据进行编辑");
            return;
        }
        /*隐藏密码选项*/
        $("#password").hide();
        /*弹出对话框*/
        $("#dialog").dialog("setTitle","编辑员工");
        $("#dialog").dialog("open");
        /*设置部门,由于部门是department.id,所以需要设置,不能直接回调*/
        rowData["department.id"] = rowData["department"].id;
        /*设置管理员回显*/
        rowData["admin"]=rowData["admin"]+"";
        /*选中数据的回显*/
        $("#employeeForm").form("load",rowData);
    })

    16.    编辑提交
由于是使用的同一个对话框dialog,所以在提交表单之前,需要判断是新增还是添加.
在jsp页面的form标签下添加一个隐藏域
jsp页面
    <form id="employeeForm">
        <%--添加一个隐藏域用于区分添加和编辑--%>
        <input  type="hidden" name="id">
employee.js
            handler:function(){
                /*判断当前是新增还是编辑*/
                var id = $("[name=\'id\']").val();
                /*由于要区分新增和编辑,下面url不能写死.*/
                var url;
                if(id){
                    /*如果有id则为编辑操作*/
                    url = "updateEmployee"
                }else{
                    /*如果没有则为添加*/
                    url = "saveEmployee"
                }
                /*提交表单*/
                $("#employeeForm").form("submit",{
                    url:url,
由于采用的对话框编辑和添加时同一个,而编辑没有密码的选项,但是密码属于data-options="required:true"
是必填项,所以需要取消密码校验.

在employee.js  /*监听编辑按钮点击*/下添加
        /*取消密码的验证*/
        $("[name= \'password\']").validatebox({required:false});    //validatebox验证盒设置必需为否
        /*隐藏密码选项*/
        $("#password").hide();

然后在添加时需要去增加密码验证
    /*监听添加按钮点击*/
    $("#add").click(function () {
        /*添加密码验证*/
        $("[name=\'password\']").validatebox({required: true});

在EmployeeController添加    @RequestMapping("/updateEmployee")
    /*编辑员工数据*/
    @RequestMapping("/updateEmployee")
    public void updateEmployee(){
        System.out.println("编辑员工数据");
    }


    17.    更新员工
EmployeeController
    /*编辑员工数据*/
    @RequestMapping("/updateEmployee")
    @ResponseBody
    public AjaxRes updateEmployee(Employee employee){
        try {
            employeeService.updateEmployee(employee);
            ajaxRes.setMsg("编辑成功");
            ajaxRes.setSuccess(true);
        }catch (Exception e){
            ajaxRes.setSuccess(false);
            ajaxRes.setMsg("编辑失败");
        }
        return ajaxRes;
    }

employeeService
    /*编辑员工*/
    public void updateEmployee(Employee employee);

employeeServiceImpl
    @Override
    public void updateEmployee(Employee employee) {
        /*调用业务层更新员工*/
        employeeMapper.updateByPrimaryKey(employee);
    }

    18.    设置离职状态
先监听按钮的点击
    /*设置离职按钮的点击*/
    $("#delete").click(function () {
        /*获取当前选中的行*/
        var rowData =$("#dg").datagrid("getSelected");
        console.log(rowData);
        if(!rowData){
         $.messager.alert("请选择一行数据进行删除");
         return;
        }
        /*提醒用户是否做离职操作*/
        $.messager.confirm("确认","是否做离职操作",function (res) {
            if(res){
                /*点击确认,返回值为true,点击取消返回值为false,做离职的操作*/
                $.get("/updateState?id="+rowData.id,function (data) {
        /* data = $.parseJSON(data);由于前面时使用get方式提交,所有不再需要转换json格式*/
                    if(data.success){
                        $.messager.alert("温馨提示",data.msg);
                        /*重新加载数据表格*/
                        $("#dg").datagrid("reload");
                    }else {
                        $.messager.alert("温馨提示",data.msg);
                    }
                })
            }
        })
    });

EmployeeController
    /*离职操作请求*/
    @RequestMapping("/updateState")
    @ResponseBody
    public AjaxRes updateState(Long id){
        try {
            /*设置员工离职状态*/
            employeeService.updateState(id);
            ajaxRes.setMsg("删除成功");
            ajaxRes.setSuccess(true);
        }catch (Exception e){
            ajaxRes.setSuccess(false);
            ajaxRes.setMsg("删除失败");
        }
        return ajaxRes;
    }

employeeService
    /*离职员工*/
    public void updateState(Long id);

employeeServiceImpl
    @Override
    public void updateState(Long id) {
        /*员工离职状态*/
        employeeMapper.updateState(id);
    }
EmployeeMapper
    void updateState(Long id);

EmployeeMapper.xml
    <!--设置员工离职状态-->
    <update id="updateState">
        update employee set state=false where id=#{id}
    </update>


    19.    离职按钮状态设置
需求,状态设置为离职后,选择该条数据不能再点击离职.
employee.js/*员工数据列表*/下面最后添加

        onClickRow:function (rowIndex,rowData) {
            /*判断当前行是否是离职状态*/
            if(!rowData.state){
                /*是离职,把离职按钮禁用*/
                $("#delete").linkbutton("disable");
            }else{
                /*未离职,启用*/
                $("#delete").linkbutton("enable");
            }
        }

但是disable还是可以点击,esayUI的bug.
添加一个base.js覆盖重写linkbutton方法扩展
把base.js放在js目录下.

    20.    分页添加
添加一个domain QueryVo接收页面的两个参数
由于前端页面设定的form data 有两个参数page:1 rows:10.
QueryVo
@Getter@Setter@ToString
public class QueryVo {
    private int page;
    private int rows;
}
在EmployeeController层
    @RequestMapping("/employeeList")
    @ResponseBody
    public PageListRes employeeList(QueryVo vo){
        /*调用业务层查询员工*/
        PageListRes pageListRes = employeeService.getEmployee(vo);
        return pageListRes;
    }
传入一个vo参数.

employeeService
    /*查询员工*/
    public PageListRes getEmployee(QueryVo vo);

employeeServiceImpl
    @Override
    public PageListRes getEmployee(QueryVo vo) {
        /*调用mapper查询员工*/
        /*设置分页total*/
        Page<Object> page = PageHelper.startPage(vo.getPage(), vo.getRows());

就可以通过页面设置每页的数据条数,自动分页.
        pageList:[10,20,30,50], /*设置每页显示行数*/

    21.    搜索操作
在工具栏添加一个查询框
    <input type="text" name="keyword" style="width: 200px; height: 30px;padding-left: 5px;">
    <a class="easyui-linkbutton" iconCls="icon-search" id="searchbtn">查询</a>

监听按钮点击employee.js
    /*监听搜索按钮点击*/
    $("#searchbtn").click(function () {
        /*获取搜索框内容*/
         var keyword = $("[name=\'keyword\']").val();
        /*重新加载列表 把参数传过去*/
        $("#dg").datagrid("load",{keyword:keyword});
    })

QueryVo中添加
    private String keyword;

employeeServiceImpl在查询中传入参数vo
        List<Employee> employees = employeeMapper.selectAll(vo);

EmployeeMapper添加参数vo
    List<Employee> selectAll(QueryVo vo);

EmployeeMapper.xml改写sql语句,引用sql片段
添加sql片段
  <sql id="where_sql" >
      <where>
          <if test="keyword !=null and keyword!=\'\'">
              and e.username like concat(\'%\',#{keyword},\'%\')
              or e.tel like concat(\'%\',#{keyword},\'%\')
              or e.email like concat(\'%\',#{keyword},\'%\')
          </if>
      </where>
  </sql>
  <!--员工关联部门查询-->
  <select id="selectAll" resultMap="BaseResultMap" >
select e.id, e.username, e.inputtime, e.tel, e.email, e.state, e.admin,
d.id as d_id,
d.`name` as d_name
from employee as e
LEFT JOIN department as d
ON e.dep_id=d.id
<include refid="where_sql"/>    //引用sql片段
order by e.id            //根据id排序
  </select>


    22.    刷新操作
直接在js上添加一个刷新按钮的监控就行了,然后清空搜索框内容,再重载一下.
    /*监听刷新点击*/
    $("#reload").click(function () {
        /*清空搜索内容*/
        var keyword = $("[name=\'keyword\']").val(\'\');
        /*重新加载数据*/
        $("#dg").datagrid("load",{});
    })


    23    角色权限关系表建立

 

建立角色与权限的表
    为多对多关系
        角色表
        权限表
        角色与权限中间表

使用代码生成器生成相关mapper

建立权限表permission,三个字段pid,pname,presource.    pid为主键自增

建立角色表role,三个字段rid,rname,rnum.            rid为主键自增

角色与权限中间表rid和pid为双主键

然后用代码逆向工程生成相关的domain,mapper,mapper.xml


    24.    角色列表展示
前端创建role.jsp
<body>
<%--数据表格--%>
<div id="role_dg"></div>
</body>

创建JS  role.js
$(function () {
    /*角色数据列表*/
    $("#role_dg").datagrid({
        url:"/getRoles",
        columns:[[
            {field:\'rnum\',title:\'角色编号\',width:100,align:\'center\'},
            {field:\'rname\',title:\'角色名称\',width:100,align:\'center\'},
        ]],
        fit:true,   /*是否需要填满*/
        fitColumns:true,    /*是否自动伸缩*/
        rownumbers:true,    /*是否需要显示行号*/
        pagination:true,     /*是否需要分页*/
        pageList:[10,20,30,50], /*设置每页显示行数*/
        singleSelect:true,  /*只能选择一行*/
        striped:true,       /*斑马线显示,隔行变色*/
/*        toolbar:"#tb",       /!*添加工具栏标签*!/*/
    });
});

前端控制器 RoleController
    /*注入业务层*/
    @Autowired
    private RoleService roleService;

    @RequestMapping("/role")
    public String Role(){
        return "role";
    }

    /*接收请求角色列表*/
    @RequestMapping("/getRoles")
    @ResponseBody
    public PageListRes getRoles(QueryVo vo){
        /*调用业务层 查询角色列表*/
        PageListRes roles = roleService.getRoles(vo);
        return roles;
    }

RoleService
public interface RoleService {
    /*查询角色列表*/
    public PageListRes getRoles(QueryVo vo);
}

RoleServiceImpl
@Service
public class RoleServiceImpl implements RoleService {

    /*注入mapper*/
    @Autowired
    private RoleMapper roleMapper;

    @Override
    public PageListRes getRoles(QueryVo vo) {
        /*调用mapper查询数据*/
        Page<Object> page = PageHelper.startPage(vo.getPage(), vo.getRows());
        List<Role> roles = roleMapper.selectAll();
        /*封装成pageList*/
        PageListRes pageListRes = new PageListRes();
        pageListRes.setTotal(page.getTotal());
        pageListRes.setRows(roles);
        return pageListRes;
    }
}

    25.    添加角色对话框
添加工具栏
<%--工具栏--%>
<div id="toolbar">
    <a href="#" class="easyui-linkbutton" data-options="iconCls:\'icon-add\',plain:true" id="add">添加</a>
    <a href="#" class="easyui-linkbutton" data-options="iconCls:\'icon-edit\',plain:true" id="edit">编辑</a>
    <a href="#" class="easyui-linkbutton" data-options="iconCls:\'icon-remove\',plain:true" id="remove">删除</a>
</div>
添加对话框
<%--添加/编辑对话框--%>
<div id="dialog">
    <form id="myform">
        <table align="center" style="border-spacing: 20px 30px">
            <input type="hidden" name="id">
            <tr align="center">
                <td>角色编号:<input type="text" name="username" class="easyui-validatebox"></td>
                <td>角色名称:<input type="text" name="username" class="easyui-validatebox"></td>
            </tr>
            <tr>
                <td><div id="role_data1"></div></td>
                <td><div id="role_data2"></div></td>
            </tr>
        </table>
    </form>
</div>

修改role.js
    /*添加编辑对话框*/
    $("#dialog").dialog({
        width:500,
        height:550
    });
    
    /*添加角色*/
    $("#add").click(function () {
    });

    /*权限列表*/
    $("#role_data1").datagrid({
        title:"所有权限",
        width:200,
        height:300,
        fitColumns: true,
        columns: [[
            {field:\'pname\',title:\'权限名称\',width:100,align:\'center\'}
        ]]
    });

    /*选中权限*/
    $("#role_data2").datagrid({
        title:"已选权限",
        width:200,
        height:300,
        fitColumns: true,
        columns: [[
            {field:\'pname\',title:\'权限名称\',width:100,align:\'center\'}
        ]]
    });


    26.    修改EasyUI默认样式
    <style>
        #dialog #myform .panel-header{
            height:20px;
        }
        #dialog #myform .panel-title{
            color: black;
            margin-top: -5px;
            text-align: center;
        }
    </style>
单独对某部分的样式进行修改,复写css样式

    27.    加载权限列表
    /*权限列表*/
    $("#role_data1").datagrid({
        title:"所有权限",
        width:200,
        height:300,
        fitColumns: true,
        singleSelect:true,
        url:\'/permissionList\',
        columns: [[
            {field:\'pname\',title:\'权限名称\',width:100,align:\'center\'}
        ]]
    });

新建一个PermissionController
@Controller
public class PermissionController {
    /*注入业务*/
    @Autowired
    private PermissionService permissionService;

    @RequestMapping("/permissionList")
    @ResponseBody
    public List<Permission> permissionList(){
        List<Permission> permission = permissionService.getPermission();
        return permission;
    }
}

PermissionService
public interface PermissionService {
    public List<Permission> getPermission();
}

PermissionServiceImpl
@Service
public class PermissionServiceImpl implements PermissionService {
    @Autowired
    private PermissionMapper permissionMapper;

    @Override
    public List<Permission> getPermission() {
        List<Permission> permissions = permissionMapper.selectAll();
        return permissions;
    }
}
由于权限选项不多,不用做分页,直接使用list<permission>接收值,不用使用pageListRes


    28.    添加权限.
给第一个权限列表添加点击响应
    /*权限列表*/
    $("#role_data1").datagrid({
        onClickRow:function (rowIndex,rowData) {/*点击一行时回调*/
            /*判断是否已经存在该权限*/
            /*取出所有的已选权限*/
            var allRows = $("#role_data2").datagrid("getRows");
            /*取出每一个进行判断*/
            for(var i=0;i<allRows.length;i++){
                /*取出每一行*/
                var row = allRows[i];
                if(rowData.pid == row.pid){/*已经存在该权限*/
                    /*让已经存在权限称为选中状态*/
                    /*获取已经称为选中状态当前角标*/
                    var index = $("#role_data2").datagrid("getRowIndex",row);
                    /*让该行成为选中状态*/
                    $("#role_data2").datagrid("selectRow",index);
                    return;
                }
            }
            /*把当前选中的,添加到已选权限*/
            $("#role_data2").datagrid("appendRow",rowData);
        }}
    });

给第二个选中权限添加点击响应
    /*已经选中的权限*/
    $("#role_data2").datagrid({
        onClickRow:function (rowIndex,rowData) {
            /*是否删除当前选中的行*/
            $("#role_data2").datagrid("deleteRow",rowIndex);
        }
    });

    29.    保存角色提交
role.js里添加保存和关闭按钮
   /*添加编辑对话框*/
    $("#dialog").dialog({
        width:500,
        height:550,
        buttons:[{
            text: \'保存\',
            handler: function () {
                /*提交表单*/
                $("#myform").form("submit",{
                    url:"saveRole",
                    onSubmit:function(param){   /*传递额外参数,已选择的权限*/
                        /*获取已选权限*/
                        var allRows = $("#role_data2").datagrid("getRows");
                        /*遍历出每一个权限*/
                        for(var i=0; i<allRows.length; i++){
                            /*取出每一个权限*/
                            var row = allRows[i];
                            /*封装到集合中*/
                            param["permissions["+i+"].pid"] =row.pid;
                        }
                        },
                    success:function (data) {
                    }
                });
            }
        },{
            text:\'关闭\',
            handler:function () {
                $("#dialog").dialog("close");
            }
        }]
    });


RoleController里添加
    /*接收 保存角色请求地址*/
    @RequestMapping("/saveRole")
    @ResponseBody
    public void saveRole(Role role){
        System.out.println(role);
    }

由于role还需要接收权限所有修改Role.java添加一个参数,使用一个ArrayList来接收权限.
    /*一个角色可以有多个权限*/
    private List<Permission> permissions =new ArrayList<>();



关键点学习使用onSubmit:function(param){ /*传递额外参数*/传递额外的参数.}


    30.    保存角色与权限
RoleController.java里,使用roleService.saveRole方法进行保存
    /*接收 保存角色请求地址*/
    @RequestMapping("/saveRole")
    @ResponseBody
    public void saveRole(Role role){
        /*调用业务层,保存角色和权限*/
        roleService.saveRole(role);
    }

RoleService
    /*保存角色和权限*/
    public void saveRole(Role role);


@Transactional
RoleServicelmpl
    /*保存角色和权限*/
    @Override
    public void saveRole(Role role) {
        /*1.保存角色*/
        roleMapper.insert(role);    /*rid:3  pid:1,2*/
        /*2.保存权限之间的关系*/
        for (Permission permission : role.getPermissions()) {
            roleMapper.insertRoleAndPermission(role.getRid(),permission.getPid());
        }
    }

RoleMapper.java        记得使用@Param不然只能使用param1,param2.
    /* 保存角色与权限关系*/
    void insertRoleAndPermission(@Param("rid") Long rid, @Param("pid") Long pid);


修改RoleMapper.xml添加一个useGeneratedKeys="true"获取生成的key 和keyProperty="rid" 赋值给哪个属性
    <!--保存角色-->
  <insert id="insert" parameterType="com.itlike.domain.Role" useGeneratedKeys="true" keyProperty="rid">
    insert into role (rid, rnum, rname)
    values (#{rid,jdbcType=BIGINT}, #{rnum,jdbcType=VARCHAR}, #{rname,jdbcType=VARCHAR})
  </insert>

    <!--保存角色与权限关系-->
    <insert id="insertRoleAndPermission">
        insert into role_permission_rel(rid,pid)values (#{rid},#{pid})
    </insert>

再在Controller层注入
    /*注入一个AjaxRes类*/
    @Autowired
    private AjaxRes ajaxRes;
添加一个ajaxRes回调提示成功与否.
    /*接收 保存角色请求地址*/
    @RequestMapping("/saveRole")
    @ResponseBody
    public AjaxRes saveRole(Role role){
        try {
            /*调用业务层,保存角色和权限*/
            roleService.saveRole(role);
            ajaxRes.setMsg("保存成功");
            ajaxRes.setSuccess(true);
        }catch (Exception e){
            ajaxRes.setMsg("保存失败");
            ajaxRes.setSuccess(false);
        }
        return ajaxRes;
    }

在js页面提示数据转换成json格式然后提示并重载
                    success:function (data) {
                        data=$.parseJSON(data)
                        if(data.success){
                            $.messager.alert("温馨提示",data.msg);
                            /*关闭对话框*/
                            $("#dialog").dialog("close");
                            /*重载数据表格*/
                            $("#role_dg").datagrid("reload");
                        }else {
                            $.messager.alert("温馨提示",data.msg);
                        }
                    }

添加一个        closed:true,让对话框开始为关闭,点击时才打开.

    31.    角色编辑回显
让对话框添加时为空,而编辑角色时,才有回显.
修改role.js,让添加时为空
    /*添加角色*/
    $("#add").click(function () {
        /*清空表单*/
        $("#myform").form("clear");
        /*清空已选权限*/
        $("#role_data2").datagrid("loadData",{rows:[]});
        /*设置标题*/
        $("#dialog").dialog("setTitle","添加角色");
        /*打开对话框*/
        $("#dialog").dialog("open");
    });

role.js 让编辑时回显.
    /*监听编辑按钮*/
    $("#edit").click(function () {
        var rowData = $("#role_dg").datagrid("getSelected");
        console.log(rowData);
        if(!rowData){
            $.messager.alert("提示","选择一行数据进行编辑");
            return;
        }
        /*回显表单*/
        $("#myform").form("load",rowData);
        /*设置标题*/
        $("#dialog").dialog("setTitle","编辑角色");
        /*打开对话框*/
        $("#dialog").dialog("open");
    });


    32.    角色权限回显
前面只实现了角色编号和角色名称的回显,本次需要实现角色已选权限的回显.
role.js
    /*监听编辑按钮*/
    $("#edit").click(function () {
        var rowData = $("#role_dg").datagrid("getSelected");
        console.log(rowData);
        if(!rowData){
            $.messager.alert("提示","选择一行数据进行编辑");
            return;
        }

        /*加载当前角色的权限*/
        var options = $("#role_data2").datagrid("options");
        options.url="/getPermissionByRid?rid="+rowData.rid;

        /*重新加载数据*/
        $("#role_data2").datagrid("load");
        /*回显表单*/
        $("#myform").form("load",rowData);
        /*设置标题*/
        $("#dialog").dialog("setTitle","编辑角色");
        /*打开对话框*/
        $("#dialog").dialog("open");
    });

在控制器PermissionController写对应的requestMapping
    /*根据角色查询对应权限*/
    @RequestMapping("/getPermissionByRid")
    @ResponseBody
    public List<Permission> getPermissionByRid(Long rid){
        System.out.println(rid);
        List<Permission> permissions= permissionService.getPermissionByRid(rid);
        return permissions;
    }

service层PermissionService
    /*根据角色查询对应的权限*/
    public List<Permission> getPermissionByRid(Long rid);

impl  PermissionServiceImpl
    /*根据角色查询对应的权限*/
    @Override
    public List<Permission> getPermissionByRid(Long rid) {
        List<Permission> permissions = permissionMapper.selectPermissionByRid(rid);
        return permissions;
    }

Mapper 
    /*根据角色查询对应的权限*/
    List<Permission> selectPermissionByRid(Long rid);

PermissionMapper.xml
    <!--根据角色查询对应的权限-->
    <select id="selectPermissionByRid" resultType="com.itlike.domain.Permission">
        SELECT pid,pname,presource from permission where pid in(SELECT pid FROM role_permission_rel where rid=#{rid});
    </select>

    33.    更新角色权限
在role.js中保存按钮时,判断它到底是保存还是编辑
        buttons:[{
            text: \'保存\',
            handler: function () {
                /*判断当前是保存还是编辑*/
                var rid = $("[name=\'rid\']").val();
                var url;
                if(rid){
                /*如果rid有值则是编辑操作*/
                    url="updateRole";
                }else {
                /*如果rid没值则是保存操作*/
                    url="saveRole";
                }
                /*提交表单*/
                $("#myform").form("submit",{
                    url:url,

controller层
    /*更新操作*/
    @RequestMapping("/updateRole")
    @ResponseBody
    public AjaxRes updateRole(Role role){
        /*调用更新角色业务*/
        try {
            roleService.updateRole(role);
            ajaxRes.setMsg("更新成功");
            ajaxRes.setSuccess(true);
        }catch (Exception e){
            ajaxRes.setSuccess(false);
            ajaxRes.setMsg("保存失败");
        }
        return ajaxRes;
    }

service
    /*更新角色*/
    void updateRole(Role role);

impl实现类
    /*更新角色*/
    @Override
    public void updateRole(Role role) {
        /*先打破角色与权限的关系,先删除*/
        roleMapper.deletePermissionRel(role.getRid());
        /*更新角色*/
        roleMapper.updateByPrimaryKey(role);
        /*重新建立与权限的关系*/
        for (Permission permission : role.getPermissions()) {
            roleMapper.insertRoleAndPermission(role.getRid(),permission.getPid());
        }
    }

mapper
    /*先打破角色与权限的关系,先删除*/
    void deletePermissionRel(Long rid);

mapper.xml
    <!--先打破角色与权限的关系,先删除-->
    <delete id="deletePermissionRel">
        delete from role_permission_rel where rid=#{rid};
    </delete>


    34.    删除角色
role.js监听删除按钮
    /*监听删除点击*/
    $("#remove").click(function () {
        /*获取当前选中行*/
        var rowData = $("#role_dg").datagrid("getSelected");
        console.log(rowData);
        if(!rowData){
            $.messager.alert("提示","选择一行数据进行删除")
            return;
        }
        /*提示一次是否确认删除*/
        $.messager.confirm("确认","是否删除",function (res) {
            if(res) {
                $.get("/deleteRole?rid=" + rowData.rid, function (data) {
                    if (data.success) {
                        $.messager.alert("温馨提示", data.msg);
                        /*关闭对话框*/
                        $("#dialog").dialog("close");
                        /*重载数据表格*/
                        $("#role_dg").datagrid("reload");
                    } else {
                        $.messager.alert("温馨提示", data.msg);
                    }
                })
            }
        });
    })

roleController
    /*删除操作*/
    @RequestMapping("/deleteRole")
    @ResponseBody
    public AjaxRes deleteRole(Long rid){
        /*删除角色业务*/
        try{
            roleService.deleteRole(rid);
            ajaxRes.setMsg("删除成功");
            ajaxRes.setSuccess(true);
        }catch (Exception e){
            ajaxRes.setSuccess(false);
            ajaxRes.setMsg("删除失败");
        }
        return ajaxRes;
    }

roleService
    /*删除角色*/
    void deleteRole(Long rid);

roleServiceImpl
    /*删除角色*/
    @Override
    public void deleteRole(Long rid) {
        /*删除关联权限*/
        roleMapper.deletePermissionRel(rid);
        /*删除角色*/
        roleMapper.deleteByPrimaryKey(rid);
    }

    35.    添加员工角色加载
employee.jsp添加一行
            <tr>
                <td>选择角色:</td>
                <td><input id="role" name="role.rid" placeholder="选择角色"></td>
            </tr>

employee.js
    /*选择角色 下拉列表*/
    $("#role").combobox({
        width:150,
        panelHeight:\'auto\',
        editable:false,
        url:\'roleList\',
        textField:\'rname\',
        valueField:\'rid\',
        multiple:true,/*是否支持多选*/
        onLoadSuccess:function () {/*数据加载完毕后回调显示提示*/
            $("#role").each(function(i){
                var span =$(this).siblings("span")[i];
                var targetInput = $(span).find("input:first");
                if(targetInput) {
                    $(targetInput).attr("placeholder", $(this).attr("placeholder"));
                }
            })
        }
    });

roleController
    /*获取角色列表 在employee中使用*/
    @RequestMapping("/roleList")
    @ResponseBody
    public List<Role> roleList(){
        return roleService.roleList();
    }

roleService
    /*查询角色列表,不做分页*/
    List<Role> roleList();

roleServiceImpl
    @Override
    public List<Role> roleList() {
        return roleMapper.selectAll();
    }


    36.    保存员工角色关系
先让选择角色支持多选
        multiple:true,/*是否支持多选*/

设置一个员工角色关系表,eid和rid为双主键
包含eid,rid,eid关联employee的id,rid关联role的rid.

在Employee中添加相关字段.
    private List<Role> roles = new ArrayList<>();;

修改employee.js
                /*提交表单*/
                $("#employeeForm").form("submit",{
                    url:url,
                    onSubmit:function(param){/*添加额外参数*/
                      /*获取选中角色*/
                        var values = $("#role").combobox("getValues");
                        for(var i=0;i<values.length;i++){
                            var rid = values[i];
                            param["roles["+i+"].rid"] =rid;
                        }
                    },

给EmployeeMapper.xml的保存员工添加参数
    <!--添加员工-->
  <insert id="insert" parameterType="com.itlike.domain.Employee" useGeneratedKeys="true" keyProperty="id">
    insert into employee (id, username, password, inputtime,
      tel, email, state, admin, 
      dep_id)
    values (#{id}, #{username},#{password}, #{inputtime},
      #{tel}, #{email}, #{state}, #{admin},
      #{department.id})
  </insert>
添加useGeneratedKeys和keyProperty

修改employeeServiceImpl,添加保存角色关系表
    /*保存员工*/
    @Override
    public void saveEmployee(Employee employee) {
        /*保存员工*/
        employeeMapper.insert(employee);
        /*保存角色关系表*/
        for (Role role : employee.getRoles()) {
            employeeMapper.insertEmployeeAndRoleRel(employee.getId(),role.getRid());
        }
    }

EmployeeMapper
    /*保存员工和角色关系*/
    void insertEmployeeAndRoleRel(@Param("id") Long id, @Param("rid") Long rid);

EmployeeMapper.xml
    <!--保存员工和角色 关系-->
    <insert id="insertEmployeeAndRoleRel">
        insert into  employee_role_rel(eid,rid) values (#{id},#{rid});
    </insert>

    37.    编辑员工角色回显
在编辑按钮中设置回显employee.js
        /*设置角色的回显 根据当前用户id查询当前角色id*/
        $.get("/getRoleByEid?id="+rowData.id,function (data) {
            /*设置下拉列表数据回显*/
            $("#role").combobox("setValues",data);
        })

RoleController
    /*根据用户id查询对应的角色*/
    @RequestMapping("/getRoleByEid")
    @ResponseBody
    public List<Long> getRoleByEid(Long id){
        return roleService.getRoleByEid(id);
    }

RoleService
    /*根据用户id查询对应的角色*/
    List<Long> getRoleByEid(Long id);

RoleServiceImpl
    /*根据用户id查询对应的角色*/
    @Override
    public List<Long> getRoleByEid(Long id) {
        return roleMapper.getRoleWithId(id);
    }

RoleMapper
    /*根据用户id查询对应的角色*/
    List<Long> getRoleWithId(Long id);

RoleMapper.xml
   <!-- 根据用户id查询对应的角色 -->
    <select id="getRoleWithId" resultType="java.lang.Long">
        select rid from employee_role_rel where eid=#{id};
    </select>

    38.    编辑员工角色更新
修改serviceImpl层
    /*编辑员工*/
    @Override
    public void updateEmployee(Employee employee) {
        /*打破和角色间的关系*/
        employeeMapper.deleteRoleRel(employee.getId());
        /*调用业务层更新员工*/
        employeeMapper.updateByPrimaryKey(employee);
        /*更新角色与员工的关系*/
        for (Role role : employee.getRoles()) {
            employeeMapper.insertEmployeeAndRoleRel(employee.getId(),role.getRid());
        }
    }
添加Mapper
    /*打破角色与员工的关系*/
    void deleteRoleRel(Long id);

Mapper.xml
    <!--打破员工与角色关系-->
    <delete id="deleteRoleRel">
        delete from employee_role_rel where eid=#{id}
    </delete>

 

    39.    Shiro概述
apache shiro Java的安全框架 轻量级
spring中有spring security,但是过于依赖spring,没有shiro使用简单
权限管理实现用户访问系统的控制
用户可以访问而且只能访问自己被授权的资源
只要有用户和密码的系统,权限管理几乎都会出现

权限管理分为
登录认证:对于需要访问控制的资源用,首先经过身份认证
判断一个用户是否为合法用户的处理过程.

授权:认证通过用户具有资源的访问权限-方可访问
控制能够访问哪些资源

shrio概述
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

    40.    Shiro核心概念
核心类:
Authentication(认证)    身份认证/登录,验证用户是否拥有相应身份
Authorization(授权)    授权,即授权验证,验证某个已认证用户是否拥有某个权限
前两个是重点
Session Manager        会话管理,用户登录后就是一次会话,没有退出前,所有信息都在会话里
Cryptography        加密,保护数据安全
Web Support        Web支持,可以非常容易的集成到web环境
Caching            缓存,比如用户登录后,其用户信息,拥有权限/角色不用每次检查,提高效率
Concurrency        shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去.
Testing            提供测试支持
Run As            允许一个用户假装另外一个用户(如果他们允许)的身份进行访问
Remember Me        记住我,这个是非常常见的功能,即一次登录后,下次再来不用登录了

主要概念:
Subject:        当前的操作用户:可以是人,爬虫,当前跟软件交互的东西
            在shiro当中我们可以统称"用户"
            在代码的任何地方,你都能轻易的获取shiro Subject.
            一旦获取subject,你就可以立即获得你希望用shiro为当前用户做的90%的事情

SecurityManager:    SecurityManager则管理所有用户的安全操作
            引用了多个内部嵌套安全组件,是shiro框架的核心
            你可以把它看成DispatcherServlet前端控制器(springmvc)
            用于调度各种shiro框架的服务

Realms:            realms则是用户的信息认证器和用户的权限认证器
            执行认证(登录)和授权(访问控制)时,shiro会从应用配置的realm中查找很多内容
            realm可以理解为读取用户信息,角色及权限的DAO
            SecurityManager要验证用户身份和权限,那么它需要从Realm获取相应的信息进行比较以确定用户身份是否合法
            可以将Realm看成dateSource,即安全数据源.

Shiro框架:

subject主体:        主体可以是程序,主体要访问系统,系统需要对主体进行认证,授权
securityManager安全管理器:
authenticator认证器:
authorizer授权器:
sessionManager会话管理
sessionDao:
cacheManager缓存管理器
realm领域
cryptography密码管理:

    41.    Shiro工程依赖添加
什么是认证:    身份认证,就是判断一个用户是否为合法用户的处理过程
        通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确

关键对象:
subject主体  用户
Principal    身份信息    是主体Subject进行身份认证的标识,标识必须具有唯一性,如用户名,手机号,邮箱地址等
credential   凭证信息    是只有主体自己知道的安全信息,如密码,证书等.

使用ini完成认证:    1.在Maven中添加依赖jar包
            2.添加shiro.ini配置文件
            3.登录与退出

创建一个shiro项目 maven quickstart
添加shiro相关依赖
    <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.4.0</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.24</version>
    </dependency>

添加ini文件
[users]  
itlike=1234
my=1234

    42.    shiro认证
    public static void main( String[] args )
    {
        /*1.构建securityManager工厂  ini  加载配置*/
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        /*2.通过工厂创建securityManager*/
        SecurityManager securityManager = factory.getInstance();
        /*3.将securityManager设置到运行环境中*/
        SecurityUtils.setSecurityManager(securityManager);
        /*4.创建一个Subject实例*/
        Subject subject = SecurityUtils.getSubject();
        /*5.创建token令牌*/
        UsernamePasswordToken token = new UsernamePasswordToken("itlike", "1234");
        /*6.用户登录*/
        try {
            subject.login(token);
        }catch (UnknownAccountException e) {
            System.out.println("账号不存在");
            e.printStackTrace();
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
            e.printStackTrace();
        }
        /*如果成功为true,反之为false*/
        System.out.println("是否认证成功"+subject.isAuthenticated());
        /*7.用户退出*/
        subject.logout();
        System.out.println("是否认证成功"+subject.isAuthenticated());
    }

    43    Shiro认证流程源码分析
构造SecurityManager环境

Subject.login()提交认证

SecurityManage.login()执行认证

Authentictor执行认证

Realm根据身份获取认证信息

1.调用subject.login方法进行登录,其会自动委托给securityManager.login方法进行登录;
    
2.securityManager通过Authenticator(认证器)进行认证;
    
3.Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码
    
    
4.IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。
    
5、最后调用Subject.logout进行退出操作。

    44.    Shiro自定义realm
1.创建一个类继承AuthorizingRealm

2.覆盖doGetAuthenticationInfo方法,在此方法当中数据库获取用户,交有验证器去验证
public class MyRealm extends AuthorizingRealm {

    /*认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        /*判断当前用户是否存在*/
        /*获取用户名*/
        String username = (String) token.getPrincipal();
        /*从数据库查出用户名和密码 真实需要从数据库取*/
        String name="itlike";
        String password="123456";

        if(!name.equals(username)){
            return null;
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, this.getName());
        return info;
    }

    /*授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

}
3.在ini文件当中进行配置
myRealm=com.itlike.MyRealm
securityManager.realms=$myRealm

就可以在前面的app实例中使用自己的realm了.


    45.    Shiro散列密码
散列密码
    概述
        散列算法一般用于生成数据的摘要信息,是一种不可逆的算法
        一般适合存储密码之类的数据,常见的散列算法如MD5、SHA等
    使用shiro进行散列密码
        Md5Hash
        SimpleHash
    Md5Hash    示例
    public static void main(String[] args){
        /*散列密码 每次加密结果一致*/
        /*itlike是加盐值,3是3次散列*/
        Md5Hash md5Hash = new Md5Hash("1234","itlike",3);
        System.out.println(md5Hash);

    /*使用SimpleHash*/
        SimpleHash simpleHash = new SimpleHash("md5","1234","itlike",3);
        System.out.println(simpleHash);
    }


    46.    Shiro认证添加散列密码

    realm中配置散列
        在ini文件当中进行散列
                [main]
                #定义凭证匹配器
                credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
                #散列算法
                credentialsMatcher.hashAlgorithmName=md5
                #散列次数
                credentialsMatcher.hashIterations=3
                
                #指定realm
                myRealm=com.itlike.MyRealm
                #配置散列
                myRealm.credentialsMatcher=$credentialsMatcher
                #配置自定义散列
                securityManager.realms=$myRealm
        要保证数据库中的密码是经过散列之后的
还需要在MyRealm中添加盐值
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        /*判断当前用户是否存在*/
        /*获取用户名*/
        String username = (String) token.getPrincipal();
        /*从数据库查出用户名和密码 真实需要从数据库取*/
        String name="itlike";
        String password="c4d626db1e96e4c058c3f088e9f2fc13";

        if(!name.equals(username)){
            return null;
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes("itlike"), this.getName());
        return info;
    }
在SimpleAuthenticationInfo中添加ByteSource.Util.bytes("itlike")盐值为itlike
            
            
    47.    Shiro授权
什么是授权
    授权,即访问控制,控制谁能访问哪些资源。
    主体进行身份认证后需要分配权限,方可访问系统的资源,对于某些资源没有权限是无法访问的。

使用ini形式配置权限信息
    在ini文件中用户、角色、权限的配置规则
        用户名=密码,角色1,角色2...
        首先根据用户名找角色,再根据角色找权限,角色是权限集合。

    权限字符串的规则
        “资源标识符:操作:资源实例标识符”
        对哪个资源的哪个实例具有什么操作
        :”是资源/操作/实例的分割符
        权限字符串也可以使用*通配符

新建一个shiro-permission.ini

[users]
#用户itlike的密码是1234,此用户具有role1和role2两个角色
itlike=1234,role1,role2
myxq=1234,role2

[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create,user:edit


授权实例:
    public static void main( String[] args )
    {
        /*1.构建securityManager工厂  ini  加载配置*/
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");
        /*2.通过工厂创建securityManager*/
        SecurityManager securityManager = factory.getInstance();
        /*3.将securityManager设置到运行环境中*/
        SecurityUtils.setSecurityManager(securityManager);
        /*4.创建一个Subject实例*/
        Subject subject = SecurityUtils.getSubject();
        /*5.创建token令牌*/
        UsernamePasswordToken token = new UsernamePasswordToken("itlike", "1234");
        /*6.用户登录*/
        try {
            subject.login(token);
        }catch (UnknownAccountException e) {
            System.out.println("账号不存在");
            e.printStackTrace();
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
            e.printStackTrace();
        }
        /*如果成功为true,反之为false*/
        System.out.println("是否认证成功"+subject.isAuthenticated());
        /*认证成功之后才做授权*/
        /*判断当前用户是否有某一个角色或者权限*/
        boolean role1 = subject.hasRole("role1");
        /*判断当前用户是否有某一个角色或者权限*/
        System.out.println("判断当前用户是否拥有权限1:"+subject.hasRole("role1"));
        System.out.println("判断当前用户是否拥有权限2:"+subject.hasRole("role2"));
        System.out.println("判断当前用户是否拥有权限3:"+subject.hasRole("role3"));
        /*判断当前用户是否同时具备多个角色 必须同时具备才显为true*/
        System.out.println(subject.hasAllRoles(Arrays.asList("role1","role2","role3")));
        /*判断是否有某一个权限*/
        System.out.println(subject.isPermitted("user:create"));
        /*判断是否有多个权限*/
        System.out.println(subject.isPermittedAll("user:delete","user:update","user:create"));

    }

    48.    Shiro自定义realm授权
修改MyRealm.java
重写它的AuthorizationInfo的返回值,手动赋值给它
    /*授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        /*获取当前的身份信息*/
        Object primaryPrincipal = principalCollection.getPrimaryPrincipal();
        /*假设 用户的角色 授权*/
        ArrayList<String> roles=new ArrayList<>();
        roles.add("role1");
        roles.add("role2");
        ArrayList<String> permissions = new ArrayList<>();
        permissions.add("user:create");
        permissions.add("user:delete");
        /*把角色和权限给添加添加到授权当中*/
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;
    }
在App中测试可用.


    49.    整合web工程认证校验
拷贝一个登陆页面login.jsp
需求,需要认证后才能登陆index.jsp,否则只能在login.jsp
添加shiro的pom相关依赖
  <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.24</version>
    </dependency>
    <dependency>
      <groupId>commons-collections</groupId>
      <artifactId>commons-collections</artifactId>
      <version>3.2.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.4.0</version>
    </dependency>

1.登录拦截,如果没有登录,跳转到登录页面
    1.在web.xml当中配置过滤器拦截所有请求,进行处理
    <!-- 拦截到所有请求,使用spring一个bean来进行处理 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <!-- 是否filter中的init和 destroy-->
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    2.在spring当中配置shiro过滤器和安全管理器
在resources中新建application-shiro.xml
然后在applicationContext中导入配置文件
    <!--导入shiro-->
    <import resource="classpath:application-shiro.xml"/>

application-shiro.xml中配置shiro过滤器
    <!-- 配置shiro过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"></property>

        <!-- 配置shiro过滤器pattern -->
        <property name="filterChainDefinitions">
            <value>
                /static/** = anon   <!--不需要登录验证-->    //anon就是不需要验证可以匿名登陆
                /login.jsp = anon   <!--不需要登录验证-->
                /**=authc     <!--除指定请求外,其它所有的请求都需要身份验证-->    //authc就是需要验证    /**就是除了以上全部需要
            </value>
        </property>
    </bean>

    <!-- 配置shiro安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"></bean>


    50.    配置loginUrl认证路径
由于scrpit比较少直接写在login.jsp中
    <script>
        $(function () {
            $("#loginBtn").click(function () {
                /*发送请求,做登录认证*/
                $.post("/login",$("form").serialize(),function (data) {
                    console.log(data)
                })
            })
        })
    </script>

配置application-shiro
    <!-- 配置shiro过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--
        index
        其它的请求  会判断 当前有没有认证过
        默认情况 ,没有认证,会跳转到login.jsp
        如果配置了 loginUrl  没有认证  执行对应的login请求
        login
        loginUrl:如果发现请求是loginUrl值  会去做认证
        配置登录认证的路径
        -->
        <property name="loginUrl" value="/login"/>

        <property name="securityManager" ref="securityManager"></property>
        <!-- 配置shiro过滤器pattern -->
        <property name="filterChainDefinitions">
            <value>
                /static/** = anon   <!--不需要登录验证-->
                /login.jsp = anon   <!--不需要登录验证-->
                /**=authc     <!--除指定请求外,其它所有的请求都需要身份验证-->
            </value>
        </property>
    </bean>


    51.    配置员工realm
在web下创建一个新的包realm
创建一个新的类EmployeeRealm 继承AuthorizingRealm
重写它的认证方法AuthorizationInfo和授权方法AuthenticationInfo

然后在application_shiro.xml中配置realm
    <!--自定义realm-->
    <bean id="employeeRealm" class="com.itlike.web.realm.EmployeeRealm"></bean>

    <!-- 配置shiro安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--注入realm-->
        <property name="realm" ref="employeeRealm"/>
    </bean>

修改认证EmployeeRealm
public class EmployeeRealm extends AuthorizingRealm {

    @Autowired
    private employeeService employeeService;
    /*认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("来到认证------");
        /*获取身份信息*/
        String username=(String)token.getPrincipal();
        System.out.println(username);
        /*到数据库查询是否有当前用户*/
        Employee employee = employeeService.getEmployeeWithUserName(username);
        if (employee == null){
            return null;
        }
        /*认证*/
        /*参数:主体,正确的密码,加的盐值*/
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(employee,employee.getPassword(),this.getName());

        return null;
    }
}

    52    登录用户查询
employeeService.getEmployeeWithUserName(username);方法的实现

serviceimpl层
    @Override
    public Employee getEmployeeWithUserName(String username) {
        return employeeMapper.getEmployeeWithUserName(username);
    }

mapper层
    <!--根据用户名查询-->
    <select id="getEmployeeWithUserName" resultType="com.itlike.domain.Employee">
        select * from employee where username=#{username}
    </select>


    53.    监听登录认证结果
在web层添加一个过滤器
MyFormFilter
继承一个FormAuthenticationFilter 方法,重写它的onLoginSuccess和onLoginFailure
public class MyFormFilter extends FormAuthenticationFilter {
    /*当认证成功时,调用*/
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        /*响应给浏览器*/
        System.out.println("认证成功");
        return false;
    }

    /*认证失败时,调用*/
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        /*响应给浏览器*/
        System.out.println("认证失败");
        return false;
    }
}


在application-shiro.xml中配置
先将过滤器交给spring来管理
    <!-- 配置过滤器-->
    <bean id="myFormFilter" class="com.itlike.web.filter.MyFormFilter"/>
然后它添加到shiro过滤器中去
  <!-- 配置shiro过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--
        index
        其它的请求  会判断 当前有没有认证过
        默认情况 ,没有认证,会跳转到login.jsp
        如果配置了 loginUrl  没有认证  执行对应的login请求
        login
        loginUrl:如果发现请求是loginUrl值  会去做认证
        配置登录认证的路径
        -->
        <property name="loginUrl" value="/login"/>
        <!--配置表单监听的过滤器-->
        <property name="filters">
            <map>
                <entry key="authc" value-ref="myFormFilter"/>
            </map>
        </property>
        <property name="securityManager" ref="securityManager"></property>
        <!-- 配置shiro过滤器pattern -->
        <property name="filterChainDefinitions">
            <value>
                /static/** = anon   <!--不需要登录验证-->
                /login.jsp = anon   <!--不需要登录验证-->
                /**=authc     <!--除指定请求外,其它所有的请求都需要身份验证-->
            </value>
        </property>
    </bean>

    54.    登录成功处理.
*Ajax发送请求,做登录认证,是没办法跳转服务器当中的请求只能通过在浏览器当中来做跳转

前端的js修改
        $(function () {
            $("#loginBtn").click(function () {
                /**Ajax发送请求,做登录认证,是没办法跳转服务器当中的请求
                 * 只能通过在浏览器当中来做跳转*/
                $.post("/login",$("form").serialize(),function (data) {
                    /*把data json格式的字符串 转为json数据*/
                    data = $.parseJSON(data);
                    if (data.success) {
                        /*成功跳转到首页*/
                        window.location.href ="/index.jsp"
                    }else {
                    }
                })
            })
        })

MyFormFilter
    /*当认证成功时,调用*/
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        /*响应给浏览器*/
        response.setCharacterEncoding("utf-8");
        System.out.println("认证成功");
        AjaxRes ajaxRes = new AjaxRes();
        ajaxRes.setSuccess(true);
        ajaxRes.setMsg("登录成功");
        /*把对象转成json格式字符串*/
        String jsonString = new ObjectMapper().writeValueAsString(ajaxRes);

        response.getWriter().print(jsonString);
        return false;
    }

    55.    登录失败处理
MyFormFilter
   /*认证失败时,调用*/
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        /*响应给浏览器*/
        System.out.println("认证失败");
        AjaxRes ajaxRes = new AjaxRes();
        ajaxRes.setSuccess(false);
        if (e!=null){
            /*获取异常名称*/
            String name = e.getClass().getName();
            if (name.equals(UnknownAccountException.class.getName())){
                /*没有账号*/
                ajaxRes.setMsg("账号错误");
            }else if (name.equals(IncorrectCredentialsException.class.getName())){
                /*密码错误*/
                ajaxRes.setMsg("密码错误");
            }else {
                ajaxRes.setMsg("未知异常");
                /*未知异常*/
            }
        }
        try {
            /*把对象转成json格式字符串*/
            String jsonString = new ObjectMapper().writeValueAsString(ajaxRes);
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(jsonString);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return false;
    }
然后前端jsp
在else 
  alert(data.msg)

    56    退出功能
index.jsp
添加shiro的标签库在顶部
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>

<%--顶部--%>
<div data-options="region:\'north\'" style="height:100px; background: #ec4e00; padding: 20px 20px; position: relative;">
    <img src="static/images/main_logo.png" alt="">

    <div style="position: absolute; right: 50px; top: 30px;">
        <img src="./static/images/user.png" style="vertical-align: middle; margin-right: 10px;" >
        <%--显示当前登录用户名--%>
        <span style="color: white; font-size: 20px; margin-right: 5px;"><shiro:principal property="username" /> </span>
        <%--取消认证  跳转到 登录页面  在shiro配置文件当中  配置   /logout = logout --%>
        <a style="font-size: 18px; color: white;text-decoration: none;" href="${pageContext.request.contextPath}/logout">注销</a>
    </div>

</div>

引用shiro的标签<shiro:principal property="username" />

在application-shiro.xml的配置中
添加                /logout = logout <!--取消认证 注销功能-->
这个路径请求的时候logout
在前端页面index.jsp中
    <%--取消认证  跳转到 登录页面  在shiro配置文件当中  配置   /logout = logout --%>
    <a style="font-size: 18px; color: white;text-decoration: none;" href="${pageContext.request.contextPath}/logout">注销</a>


    57.    注解授权添加
EmpoleeController中添加注解    @RequiresPermissions("employee:index")
    @RequestMapping("/employee")
    @RequiresPermissions("employee:index")
    public String employee(){
        return "employee";
    }

EmpoleeRealm
    /** 授权
    web doGetAuthorizationInfo 什么时候调用
     1.发现访问路径对应的方法上面有授权的注解 就会调用doGetAuthorizationInfo授权方法
     2.页面中有授权的标签 也会调用doGetAuthorizationInfo授权方法
    */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("授权调用index---------");
        return null;
    }

1.在配置文件application-shiro.xml当中添加Shiro注解扫描
<!--
    配置为true即使用cglib继承的方式,
    false为jdk的接口动态代理   控制器没有实现接口
    -->
    <aop:config proxy-target-class="true" ></aop:config>

    <!-- 使用第三方去扫描shiro的注解 -->
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor ">
        <property name="securityManager" ref="securityManager"></property>
    </bean>

    58.    授权角色添加
在realm中添加授权信息
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("授权调用index---------");
        /*获取用户的身份信息*/
        Employee employee = (Employee) principals.getPrimaryPrincipal();
        /*根据当前用户,查询角色权限*/
        List<String> roles = new ArrayList<>();
        List<String> permissions = new ArrayList<>();
        /*查询角色*/
        roles = employeeService.getRolesById(employee.getId());
        /*给授权信息*/
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);


        return info;
    }

employeeService
    /*根据id查询角色*/
    List<String> getRolesById(Long id);
employeeServicelmpl
    /*根据id查询角色编号名称*/
    @Override
    public List<String> getRolesById(Long id) {
        return employeeMapper.getRolesById(id);
    }
EmployeeMapper
    /*根据id查询用户角色编号*/
    List<String> getRolesById(Long id);
EmployeeMapper.xml
    <!--根据用户id查询角色编号-->
    <select id="getRolesById" resultType="java.lang.String">
        SELECT r.rnum FROM employee_role_rel as er
        LEFT JOIN role as r
        ON er.rid=r.rid WHERE eid=#{id}
    </select>

    59.    授权权限添加
EmployeeRealm 添加        /*查询权限*/permissions = employeeService.getPermissionsById(employee.getId());

    /** 授权
    web doGetAuthorizationInfo 什么时候调用
     1.发现访问路径对应的方法上面有授权的注解 就会调用doGetAuthorizationInfo授权方法
     2.页面中有授权的标签 也会调用doGetAuthorizationInfo授权方法
    */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("授权调用index---------");
        /*获取用户的身份信息*/
        Employee employee = (Employee) principals.getPrimaryPrincipal();
        /*根据当前用户,查询角色权限*/
        List<String> roles = new ArrayList<>();
        List<String> permissions = new ArrayList<>();
        /*查询角色*/
        roles = employeeService.getRolesById(employee.getId());
        System.out.println("role==="+roles);
        /*查询权限*/
        permissions = employeeService.getPermissionsById(employee.getId());
        /*给授权信息*/
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(roles);
        info.addStringPermissions(permissions);
        return info;
    }

employeeService
    /*根据id查询权限名称*/
    List<String> getPermissionsById(Long id);

employeeServicelmpl
    /*根据id查询权限*/
    @Override
    public List<String> getPermissionsById(Long id) {
        return employeeMapper.getPermissionsById(id);
    }

employeeMapper.xml SQL语句通过DISTINCT 去除重复的值

    <!--根据用户id查询权限资源名称-->
    <select id="getPermissionsById" resultType="java.lang.String">
        SELECT DISTINCT p.presource FROM role_permission_rel as rp
        LEFT JOIN permission as p ON rp.pid = p.pid
        where rid in (SELECT rid FROM employee_role_rel where eid=#{id});
    </select>