springboot+easyui+jpa實現動態權限角色的后台管理系統(一)


最近因為多次需要使用easyui的后台管理系統,所以自己寫了一個easyui后台管理系統的模版,可修改權限增加角色

(這里先放創建數據庫和加載菜單,配置攔截器的方法和遇到的問題)

1.先創建數據庫(我是在本地創建的數據庫)

資源表:存放菜單資源

角色表:存放角色數據

角色和資源的中間表:關聯角色表和資源表

用戶表:存放用戶數據

角色和用戶中間表:關聯角色和用戶

2.創建完數據庫和數據表后在項目中創建相應的實體類,控制層,服務層和數據訪問層

3.寫前端的login.ftl和index.ftl

login.ftl就不放了,放上index.ftl,在加載頁面的時候調用js打開一級菜單,在一級菜單打開的時候獲取二級菜單

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title>ace Admin</title>
    <meta name="description" content=""/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <#include "inc/inc.ftl"/>
</head>
<body>
<div id="test"></div>
<div class="easyui-layout" data-options="fit:true">
    <div data-options="region:'north'" class="head_body" style="overflow:hidden;">
        <div>
            <div class="head_left"></div>
            <div class="head_right">
                <div class="huanying">
                    歡迎 <span>${Session.userInfo.username}</span> 登陸 | 現在是<span id="show_date"></span>
                </div>
                <div class="anniu">
                    <div class="bb">
                        <a href="javascript:logout();" class="logout"></a>
                        <a id="open_change_password" class="changePwd" href="javascript:editPassword();"></a>
                    </div>
                    <div class="bs">
                        <a class="styleswitch a1" style="CURSOR: pointer" title="黑灰色" rel="black"></a>
                        <a class="styleswitch a2" style="CURSOR: pointer" title="天藍色" rel="default"></a>
                        <a class="styleswitch a3" style="CURSOR: pointer" title="灰色" rel="bootstrap"></a>
                        <a class="styleswitch a4" style="CURSOR: pointer" title="淺灰色" rel="gray"></a>
                        <a class="styleswitch a5" style="CURSOR: pointer" title="白色" rel="metro"></a>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div data-options="region:'west',split:true,title:'導航菜單'" style="width:188px;">
        <div class="easyui-accordion" id="main-accordion" data-options="fit:true,border:false">
        </div>
    </div>

    <div id="mainPanel" data-options="region:'center'">
        <div id="index_tabs" class="easyui-tabs" data-options="fit:true,border:false,tabHeight:30"
             style="height:600px;">
            <div title="Home">
                <div style="padding:10px 0 10px 10px">
                    <h2>系統介紹</h2>
                    <div class="light-info">

                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<div id="dlg" class="easyui-dialog" style="width:450px;height:auto;padding:10px 20px" buttons="#dlg-buttons"
     data-options="closed:true,modal:true">
    <form id="fm" method="post" enctype="multipart/form-data">
        <input type="hidden" id="userId" name="userId"/>
        <table class="grid">
            <tr>
                <td>密碼:</td>
                <td>
                    <input type="text" name="password" class="easyui-passwordbox" iconWidth="28"
                           data-options="required:true,validType:['length[0,20]']"/>
                </td>
            </tr>
        </table>
    </form>
</div>
<div id="dlg-buttons">
    <a href="javascript:void(0)" class="easyui-linkbutton c8" data-options="iconCls:'icon-ok'"
       onclick="javascript:saveUser();" style="width:90px;">保存</a>
    <a href="javascript:void(0)" class="easyui-linkbutton c2" data-options="iconCls:'icon-cancel'"
       onclick="javascript:$('#dlg').dialog('close');" style="width:90px;">取消</a>
</div>
</body>
</html>

<script type="text/javascript">
    $(document).ready(function () {
//         設置tab高度,瀏覽器當前窗口可視區域高度 -頭部高度
//         $("#index_tabs").height($(window).height()-$("#navbar").height());
        //顯示系統時間
        var myDate = new Date();
        var week = ['日', '一', '二', '三', '四', '五', '六'];
        var month = myDate.getMonth() + 1;
        var weekDay = " 星期" + week[myDate.getDay()];
        var showDate = myDate.getFullYear() + "年" + month + "月" + myDate.getDate() + "日" + weekDay;
        $("#show_date").text(showDate);
        loadFirstMenu();
//        loadSecondMenu();
//        $('#main-accordion').accordion('getSelected').panel('collapse');//默認全部關閉

    });

    function loadFirstMenu() {
        $.ajax({
            url: '${request.contextPath}/resource/first_menu?userId=' +${Session.userInfo.userId},
            type: 'get',
            success: function (data) {
                for (var i in data) {//不使用過濾
                    var mc = $('#main-accordion').accordion('add', {
                        title: data[i].name,
                        content: $.formatString('<ul id=\'{0}\' class="easyui-tree menu_tree" style="padding:10px;"></ul>', "tree" + data[i].resourceId)
                    });
                    $('#' + "tree" + data[i].resourceId).tree({
                        url: '${request.contextPath}/resource/second_menu?resourceId=' + data[i].resourceId + '&userId=' +${Session.userInfo.userId},
                        method: 'get',
                        parentField: 'parentId',
                        onSelect: function (node) {
                            var start = node.path.indexOf("http");
                            var path = '${request.contextPath}' + node.path;
                            if (start == 0) {
                                var path = node.path;
                            }
                            addTab(node.text, path, node.iconCls);
                        }
                    });
                }
            }
        });
    }

    //初始化TABS組件
    $('#index_tabs').tabs({
        fit: true,
        border: false,
        tabHeight: 30,
        tools: [{
            iconCls: 'icon-reload',
            handler: function () {
                var currTab = $('#index_tabs').tabs('getSelected');
                var index = $('#index_tabs').tabs('getTabIndex', currTab);
                if (index != 0) {
                    var updateUrl = $(currTab.panel('options').content).attr('src');
                    $('#index_tabs').tabs('update', {
                        tab: currTab,
                        options: {
                            content: refreshTab(updateUrl)
                        }
                    });
                }
            }
        }, {
            iconCls: 'icon-clear',
            handler: function () {
                $.messager.confirm('提示', '確定要全部關閉選項卡?', function (r) {
                    if (r) {
                        var tabTitle = new Array();
                        var tabs = $('#index_tabs').tabs("tabs");
                        var tCount = tabs.length;
                        if (tCount > 0) {
                            for (var i = 0; i < tCount; i++) {
                                tabTitle.push(tabs[i].panel('options').title);
                            }
                            for (var i = 0; i < tabTitle.length; i++) {
                                if (tabTitle[i] != '首頁') {
                                    $('#index_tabs').tabs("close", tabTitle[i]);
                                }
                            }
                        }
                    }
                });
            }
        }]
    });

    function editPassword() {
        $('#dlg').dialog('open').dialog('setTitle', '修改');
        $('#userId').val(${Session.userInfo.userId});
    }

    function saveUser() {
        $('#fm').form('submit', {
            url: "${request.contextPath}/sys/user_save",
            success: function (result) {
                var result = eval('(' + result + ')');
                if (result.code == 0) {
                    $.messager.show({
                        title: '提示',
                        msg: result.message
                    });
                    $('#dlg').dialog('close');
                }
            }
        });
    }
</script>

4.將獲取一級菜單和二級菜單的http請求發送到對應controller層

package com.lk.modeleasyui.controller;

import com.lk.modeleasyui.beans.MenuDTO;
import com.lk.modeleasyui.domain.SysResource;
import com.lk.modeleasyui.service.SysResourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Author: Lukizzz
 * @Date: 2018/9/11 11:39
 * @Description:
 */
@RestController
@RequestMapping(value = "/resource")
public class SysResourceController {

    @Autowired
    private SysResourceService sysResourceService;

    @GetMapping(value = "/first_menu")
    public List<SysResource> getFirstMenu(Long userId) throws Exception {
        return sysResourceService.getFirstMenu(userId);
    }

    @GetMapping(value = "/second_menu")
    public List<MenuDTO> getSecondMenu(Long resourceId) throws Exception {
        return sysResourceService.getSecondMenu(resourceId);
    }

}

注意使用restController的注解 不然前端無法接收到json

5.service:

package com.lk.modeleasyui.service;

import com.lk.modeleasyui.beans.MenuDTO;
import com.lk.modeleasyui.beans.TreeBean;
import com.lk.modeleasyui.dao.SysResourceDAO;
import com.lk.modeleasyui.dao.SysRoleResourceDAO;
import com.lk.modeleasyui.dao.SysRoleUserDAO;
import com.lk.modeleasyui.domain.SysResource;
import com.lk.modeleasyui.domain.SysRoleResource;
import com.lk.modeleasyui.domain.SysRoleUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: Lukizzz
 * @Date: 2018/9/11 11:51
 * @Description:
 */
@Service
public class SysResourceService {
    @Autowired
    private SysResourceDAO sysResourceDAO;
    @Autowired
    private SysRoleUserDAO sysRoleUserDAO;
    @Autowired
    private SysRoleResourceDAO sysRoleResourceDAO;

    public List<SysResource> getFirstMenu(Long userId) {
        List<SysRoleUser> list = sysRoleUserDAO.findAllByUserId(userId);
        List<Long> roleIds = new ArrayList<>();
        for (SysRoleUser sysRoleUser :list){
            roleIds.add(sysRoleUser.getRoleId());
        }
        List<SysRoleResource> resourceIds = sysRoleResourceDAO.findAllByRoleIdIn(roleIds);
        List<SysResource> sysResources = new ArrayList<>();
        for (SysRoleResource sysRoleResource:resourceIds){
            SysResource sysResource = new SysResource();
            sysResource.setResourceId(sysRoleResource.getResourceId());
            sysResources.add(sysResource);
        }
        return sysResourceDAO.findAllByResourceIds(sysResources);
    }

    public List<MenuDTO> getSecondMenu(Long resourceId) {
        List<SysResource> list = sysResourceDAO.findAllBylist(resourceId);
        List<MenuDTO> menus = new ArrayList<>();
        for(SysResource resource : list){
            MenuDTO menu = new MenuDTO();
            menu.setText(resource.getName());
            menu.setPath(resource.getUrl());
            menu.setIconCls(resource.getIcon());
            menus.add(menu);
        }
        return menus;
    }
}

這里創建了兩個類:MenuDTO TreeBean

MenuDTO:

public class MenuDTO extends TreeBean {
    private String path;

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}
TreeBean:
@Data
public class TreeBean {

    private String id;
    private String text;
    private String state = "open";
    private boolean checked = false;
    private Object attributes;
    private String parentId;
    private String iconCls;

}

在測試過程中,如果沒有登錄直接打開index的頁面會出現亂碼,原因是沒有登錄的話session獲取不到userInfo,因此我設置了一個攔截器,創建一個LoginInterceptor類和一個工具類WebAppConfig

LoginInterceptor:

public class LoginInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        if (session.getAttribute("userInfo") == null) {
            response.sendRedirect(request.getContextPath() + "/login");
            return false;
        }
        return super.preHandle(request, response, handler);
    }
}

如果session獲取的userInfo為空的話就重定向到登陸界面

WebAppConfig:

@Configuration
public class WebAppConfig implements WebMvcConfigurer {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        String[] excludes = new String[]{"/static/**"};
        registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/login").excludePathPatterns("/do_login").excludePathPatterns(excludes);
        WebMvcConfigurer.super.addInterceptors(registry);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        WebMvcConfigurer.super.addResourceHandlers(registry);
    }

}

在配置攔截器的時候遇到幾個小問題:

1.WebMvcConfigurerAdapter過時:

在網絡上的大部分配置攔截器的教程都是繼承WebMvcConfigurerAdapter,課時現在版本的WebMvcConfigurerAdapter已經被spring認為過時,我找了很久找到了它的替代品,也是官方推薦我們去使用的,實現 WebMvcConfigurer,具體的代碼可見上面的WebAppConfig.

2.攔截器是配置成功了,但是登陸界面的靜態資源也被攔截了

這個問題的解決方法WebAppConfig代碼里也已經有了,首先String[] excluds定義加載的靜態資源的路徑,excludePathPatterns("/login")這個"/login"代表着這個路徑不被攔截,所以我們在下面的代碼中加入excludePathPatterns("excluds"),跳過攔截

 

 

在創建登陸的時候還遇到個小問題

    @RequestMapping(value = "/index")
    public String index() {
        return "index";
    }

    @RequestMapping(value = "/")
    public String toIndex() {
        return "index";
    }

本意是想跳轉到index頁面

但是一直出現報錯,錯誤的大概意思是包名和視圖名重復了,修改多次index的名字后無果后來發現是freemarker的依賴沒有添加

 

放上效果圖:

 

其他的問題和過程寫在下一篇的隨筆里

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM