在包“com.example.demo”中新建一個“LoginInterceptor”類,代碼如下:
package com.example.demo;
import java.io.PrintWriter;
/**
* 登錄攔截器
*/
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import com.example.demo.vo.Json;
import com.fasterxml.jackson.databind.ObjectMapper;
@Component
public class LoginInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ObjectMapper mapper;//Jackson使用ObjectMapper類將POJO對象序列化成JSON字符串,也能將JSON字符串反序列化成POJO對象。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //請求進入這個攔截器
logger.info("進入攔截器...");
HttpSession session = request.getSession();
if(session.getAttribute("user") == null){//判斷session中有沒有user信息
logger.info("session中沒有user信息");
//ajax異步請求
if("XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With"))){
logger.info("ajax異步請求");
Json j = new Json();
j.setSuccess(false);
j.setMsg("session會話超時,請重新登錄!");
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write(mapper.writeValueAsString(j));//將對象轉成字符串
logger.info(mapper.writeValueAsString(j));
writer.close();
response.flushBuffer();
return false;
}else {
logger.info("頁面請求");
response.sendRedirect("/login");//沒有user信息的話進行路由重定向到登錄頁面
return false;
}
}
logger.info("登錄攔截驗證通過。");
return true;//有的話就繼續操作
}
}
每一個配置爲攔截的http請求都會執行preHandle方法,可以在這裏檢查用戶的session是否爲空來進行登錄驗證,因爲請求有可能是異步的,所以對於異步的請求不能直接跳轉頁面,而是返回json數據。
修改在包“com.example.demo”中新建一個“WebConfig”類,代碼如下:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;//登錄攔截器
//圖片存放根路徑,從application.yml中讀取upload
@Value("${upload}")
private String UPLOAD_PATH;
/**
* 文件上傳
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//外部靜態資源映射路徑,用來上傳文件
String filePath = "file:" + UPLOAD_PATH;
registry.addResourceHandler("/upload/**").addResourceLocations(filePath);
}
//攔截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//登錄攔截的管理器
InterceptorRegistration registration = registry.addInterceptor(loginInterceptor);//攔截的對象會進入這個類中進行判斷
registration.addPathPatterns("/**");//所有路徑都被攔截
registration.excludePathPatterns("/","/login","/UserController/login","/kaptcha","/static/**","/UserController/logout","/upload/**");//添加不攔截路徑
}
}
在WebConfig中增加了攔截器的設置。
修改在包“com.example.demo.controller”中新建一個“HomeController”類,代碼如下:
package com.example.demo.controller;
/**
* 後臺管理主頁,登錄頁面,驗證碼生成
*/
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.example.demo.model.User;
import com.example.demo.service.PermissionService;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
@Controller
public class HomeController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private Producer captchaProducer;//驗證碼
@Autowired
private PermissionService permissionService;// 注入業務層的service
// 未加入@ResponseBody用來返回數據給頁面
@RequestMapping("/index")
public String index(HttpServletRequest request,Model model) {
HttpSession session = request.getSession();
User user = (User)session.getAttribute("user");
logger.info(user.getUsername());
logger.info(user.getId());
model.addAttribute("username", user.getUsername());
model.addAttribute("userid", user.getId());
model.addAttribute("menulist", permissionService.getUserMenu(user.getId()));
return "admin/index";
}
// 未加入@ResponseBody用來返回數據給頁面
@RequestMapping("/home")
public String home(Model model) {
return "admin/home";
}
// 未加入@ResponseBody用來返回數據給頁面
@RequestMapping("/")
public String login(Model model) {
return "admin/login";
}
// 未加入@ResponseBody用來返回數據給頁面
@RequestMapping("/login")
public String login2(Model model) {
return "admin/login";
}
@RequestMapping("/kaptcha")
public ModelAndView getKaptchaImage(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpSession session = request.getSession();
String code = (String)session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
logger.info("******************驗證碼是: " + code + "******************");
response.setDateHeader("Expires", 0);
// Set standard HTTP/1.1 no-cache headers.
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
// Set IE extended HTTP/1.1 no-cache headers (use addHeader).
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
// Set standard HTTP/1.0 no-cache header.
response.setHeader("Pragma", "no-cache");
// return a jpeg
response.setContentType("image/jpeg");
// create the text for the image
String capText = captchaProducer.createText();
// store the text in the session
session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
// create the image with the text
BufferedImage bi = captchaProducer.createImage(capText);
ServletOutputStream out = response.getOutputStream();
// write the data out
ImageIO.write(bi, "jpg", out);
try {
out.flush();
} finally {
out.close();
}
return null;
}
}
增加了登錄頁面路徑映射以及驗證碼生成接口。
修改在包“com.example.demo”中新建一個“MisApplication”類,代碼如下:
package com.example.demo;
import java.util.Properties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
@SpringBootApplication
public class MisApplication {
public static void main(String[] args) {
SpringApplication.run(MisApplication.class, args);
}
/**
* 驗證碼
* @return
*/
@Bean
public DefaultKaptcha captchaProducer() {
DefaultKaptcha captchaProducer = new DefaultKaptcha();
Properties properties = new Properties();
// properties.setProperty(Constants.KAPTCHA_BORDER, "yes");
// properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "red");
// properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "50");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "宋體,楷體,微軟雅黑");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, "0123456789");
properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "120");
properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "50");
properties.setProperty(Constants.KAPTCHA_SESSION_CONFIG_KEY, "code");
Config config = new Config(properties);
captchaProducer.setConfig(config);
return captchaProducer;
}
}
對驗證碼的生成進行配置。
修改在包“com.example.demo.controller”中新建一個“UserController”類,代碼如下:
package com.example.demo.controller;
import java.io.File;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import com.example.demo.util.SnowflakeIdWorker;
import com.example.demo.vo.DataTableResult;
import com.example.demo.vo.Json;
import com.example.demo.vo.UserVO;
import com.google.code.kaptcha.Constants;
@Controller
@RequestMapping("/UserController")
public class UserController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private String prefix = "admin/user";// 頁面的路徑,注意admin前面不要有/
// 圖片存放根路徑,從application.yml中讀取upload
@Value("${upload}")
private String UPLOAD_PATH;
@Autowired
private UserService userService;// 注入業務層的service
// 未加入@ResponseBody用來返回數據給頁面
@RequestMapping("view")
public String view(Model model) {
// String str="個人";
// model用來回傳數據給前端頁面
// setTitle(model, new TitleVo("列表", str+"管理", true,"歡迎進入"+str+"頁面", true, false));
return prefix + "/view";
}
// @ResponseBody,直接通過js異步返回數據給頁面
@RequestMapping("list")
@ResponseBody
public DataTableResult list(HttpServletRequest request, UserVO userVO) {
// DataTableResult返回給datatables控件的數據格式
DataTableResult result = new DataTableResult();
// 獲取分頁參數
int start = Integer.parseInt(request.getParameter("start"));
int length = Integer.parseInt(request.getParameter("length"));
// 獲取排序字段
String orderIdx = request.getParameter("order[0][column]");
// 獲取排序字段名
String orderField = request.getParameter("columns[" + orderIdx + "][name]");
// 獲取排序方式,降序desc或者升序asc
String orderDir = request.getParameter("order[0][dir]");
// 調用分頁查詢方法
result = userService.selectUserListPage(userVO, start, length, orderField, orderDir);
// result.setDraw(userVO.getDraw());
return result;
}
// @ResponseBody,直接通過js異步返回數據給頁面
@RequestMapping("insert")
@ResponseBody
public Json insert(User user) {
Json j = new Json();
if (userService.insert(user) > 0) {
j.setSuccess(true);
j.setMsg("添加成功!");
} else {
j.setSuccess(false);
j.setMsg("添加失敗!");
}
return j;
}
// @ResponseBody,直接通過js異步返回數據給頁面
@RequestMapping("update")
@ResponseBody
public Json updateById(User user) {
Json j = new Json();
if (userService.updateById(user) > 0) {
j.setSuccess(true);
j.setMsg("修改成功!");
} else {
j.setSuccess(false);
j.setMsg("修改失敗!");
}
return j;
}
// @ResponseBody,直接通過js異步返回數據給頁面
@RequestMapping("select")
@ResponseBody
public Json selectById(User user) {
Json j = new Json();
j.setSuccess(true);
j.setObj(userService.selectById(user.getId()));
return j;
}
// @ResponseBody,直接通過js異步返回數據給頁面
@RequestMapping("delete")
@ResponseBody
public Json delete(HttpServletRequest request) {
Json j = new Json();
String ids = request.getParameter("ids");
if (!StringUtils.isEmpty(ids)) {
j.setSuccess(true);
j.setObj("成功刪除" + userService.delete(ids) + "條記錄");
} else {
j.setSuccess(false);
j.setMsg("沒有需要刪除的記錄!");
}
return j;
}
// @ResponseBody,直接通過js異步返回數據給頁面
// @RequestParam("file[]") MultipartFile[] file 多個文件
@RequestMapping("upload")
@ResponseBody
public Json upload(HttpServletRequest request, @RequestParam("file") MultipartFile file) {
Json j = new Json();
if (!file.isEmpty()) {
try {
String originalFilename = file.getOriginalFilename();
// 隨機文件名
String newFileName = SnowflakeIdWorker.getUUID()
+ originalFilename.substring(originalFilename.lastIndexOf("."));
// 上傳文件路徑
File upload = new File(UPLOAD_PATH, "images/");
if (!upload.exists())
upload.mkdirs();
String uploadPath = upload + "\\";
logger.info("uploadPath = " + uploadPath);
File uploadfile = new File(uploadPath + newFileName);
// 將上傳文件保存到一個目標文件當中
file.transferTo(uploadfile);
j.setSuccess(true);
j.setObj("/upload/images/" + newFileName);
} catch (IllegalStateException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
j.setSuccess(false);
j.setObj("上傳異常");
}
} else {
j.setSuccess(false);
j.setObj("上傳失敗");
}
return j;
}
/**
* 登錄驗證
* @param request
* @param user
* @return
*/
// @ResponseBody,直接通過js異步返回數據給頁面
@RequestMapping("login")
@ResponseBody
public Json login(HttpServletRequest request, User user) {
Json j = new Json();
HttpSession session = request.getSession();
String code = (String) session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
String kaptcha = request.getParameter("kaptcha");
if (kaptcha.equals(code)) {
// 驗證碼正確
User loginUser = userService.selectByUser(user);
if (loginUser != null) {
j.setSuccess(true);
loginUser.setPassword("");// 密碼不回傳
j.setObj(loginUser);
j.setMsg("登錄成功!");
session.setAttribute("user", loginUser);// 保存session會話
} else {
j.setSuccess(false);
j.setMsg("登錄失敗!");
}
} else {
j.setSuccess(false);
j.setMsg("驗證碼錯誤!");
}
return j;
}
/**
* 註銷
* @param request
* @param model
* @return
*/
// 未加入@ResponseBody用來返回數據給頁面
@RequestMapping("logout")
public String logout(HttpServletRequest request,Model model) {
HttpSession session = request.getSession();
session.removeAttribute("user");
return "admin/login";
}
}
增加了用戶登錄與註銷接口。
添加登錄界面文件,在“admin”文件夾-> 右鍵->new->file,輸入“login.html”,代碼如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title> - 登錄</title>
<meta name="keywords" content="">
<meta name="description" content="">
<link th:href="@{/static/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/static/css/font-awesome.css?v=4.4.0}" rel="stylesheet">
<link th:href="@{/static/css/animate.css}" rel="stylesheet">
<link th:href="@{/static/css/style.css}" rel="stylesheet">
<link th:href="@{/static/css/login.css}" rel="stylesheet">
<!-- toastr -->
<link th:href="@{/static/css/plugins/toastr/toastr.min.css}" rel="stylesheet">
<!--[if lt IE 9]>
<meta http-equiv="refresh" content="0;ie.html" />
<![endif]-->
<script>
if (window.top !== window.self) {
window.top.location = window.location;
}
</script>
</head>
<body class="signin">
<div class="signinpanel">
<div class="row">
<div class="col-sm-12">
<form>
<h4 class="no-margins">登錄:</h4>
<p class="m-t-md">登錄XXX後臺管理系統</p>
<input type="text" name="username" id="username" class="form-control uname" placeholder="用戶名" />
<input type="password" name="password" id="password" class="form-control pword m-b" placeholder="密碼" />
<div class="input-group" style="margin-top:15px;color:#333;">
<input type="text" name="kaptcha" id="kaptcha" class="form-control" style="margin-top:0px;" placeholder="驗證碼" >
<span style="padding:1px;" class="input-group-addon "><img src="/kaptcha" id="kaptchaImage" style="height:30px;vertical-align:middle;" alt="點擊刷新" title="點擊刷新" onclick='$("#kaptchaImage").attr("src","/kaptcha?r="+Math.random())'></span>
</div>
<a href="">忘記密碼了?</a>
<button type="button" id="btn_login" class="btn btn-success btn-block">登錄</button>
</form>
</div>
</div>
<div class="signup-footer">
<div class="pull-left">
© 福建江夏學院電子信息科學學院信息管理系,2019年
</div>
</div>
</div>
<!-- 全局js -->
<script th:src="@{/static/js/jquery.min.js?v=2.1.4}"></script>
<!-- Toastr script -->
<script th:src="@{/static/js/plugins/toastr/toastr.min.js}"></script>
<!-- Page-Level Scripts -->
<script>
$(document).ready(function () {
//toastr選項
toastr.options = {
"positionClass": "toast-bottom-center",
}
$("#btn_login").click(function(){
var username=$("#username").val();
var password=$("#password").val();
var kaptcha=$("#kaptcha").val();
if(username.length==0 || password.length==0 || kaptcha.length==0){
toastr.error("用戶名、密碼或者驗證碼爲空!", '錯誤!');
}else{
//異步添加數據
$.ajax({
type: "post",
data: {
username:$("#username").val(),
password:$("#password").val(),
kaptcha:$("#kaptcha").val(),
},
url: "/UserController/login",//後臺處理地址
success: function (data) {
if(data.success){
toastr.success(data.msg, '登錄成功!');
location.href="/index";
}else{
toastr.error(data.msg, '登錄失敗!');
}
}
}); // end ajax
}
});//end btn_login
});//end ready
</script>
</body>
</html>
修改主界面文件,在“admin”文件夾中的“index.html”,代碼如下:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit">
<title> hAdmin- 主頁</title>
<meta name="keywords" content="">
<meta name="description" content="">
<!--[if lt IE 9]>
<meta http-equiv="refresh" content="0;ie.html" />
<![endif]-->
<link rel="shortcut icon" href="favicon.ico">
<link th:href="@{/static/css/bootstrap.min.css?v=3.3.6}" rel="stylesheet">
<link th:href="@{/static/css/font-awesome.min.css?v=4.4.0}" rel="stylesheet">
<link th:href="@{/static/css/animate.css}" rel="stylesheet">
<link th:href="@{/static/css/style.css?v=4.1.0}" rel="stylesheet">
</head>
<body class="fixed-sidebar full-height-layout gray-bg" style="overflow:hidden">
<div id="wrapper">
<!--左側導航開始-->
<nav class="navbar-default navbar-static-side" role="navigation">
<div class="nav-close"><i class="fa fa-times-circle"></i>
</div>
<div class="sidebar-collapse">
<ul class="nav" id="side-menu">
<li class="nav-header">
<div class="dropdown profile-element">
<a data-toggle="dropdown" class="dropdown-toggle" href="#">
<span class="clear">
<span class="block m-t-xs" style="font-size:20px;">
<i class="fa fa-bank"></i>
<strong class="font-bold">管理系統</strong>
</span>
</span>
</a>
</div>
<div class="logo-element">管理系統
</div>
</li>
<li>
<a class="J_menuItem" href="/home">
<i class="fa fa-home"></i>
<span class="nav-label">主頁</span>
</a>
</li>
<li th:each="menu,menuStat:${menulist}">
<a href="#">
<i th:class="${menu.menu.menuIcon}"></i>
<span class="nav-label" th:text="${menu.menu.menuText}"></span>
<span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li th:each="submenu,submenuStat:${menu.submenus}">
<a class="J_menuItem" th:href="@{${submenu.menuUrl}}" th:text="${submenu.menuText}"></a>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<!--左側導航結束-->
<!--右側部分開始-->
<div id="page-wrapper" class="gray-bg dashbard-1">
<div class="row border-bottom">
<nav class="navbar navbar-static-top" role="navigation" style="margin-bottom: 0">
<div class="navbar-header"><a class="navbar-minimalize minimalize-styl-2 btn btn-info " href="#"><i class="fa fa-bars"></i> </a>
<form role="search" class="navbar-form-custom" method="post" action="search_results.html">
<div class="form-group">
<input type="text" placeholder="請輸入您需要查找的內容 …" class="form-control" name="top-search" id="top-search">
</div>
</form>
</div>
<ul class="nav navbar-top-links navbar-right">
<li>
<span th:text="${username}"></span><span><a href="/UserController/logout">註銷</a></span>
</li>
<li class="dropdown">
<a class="dropdown-toggle count-info" data-toggle="dropdown" href="#">
<i class="fa fa-envelope"></i> <span class="label label-warning">16</span>
</a>
<ul class="dropdown-menu dropdown-messages">
<li class="m-t-xs">
<div class="dropdown-messages-box">
<a href="profile.html" class="pull-left">
<img alt="image" class="img-circle" th:src="@{/static/img/a7.jpg}">
</a>
<div class="media-body">
<small class="pull-right">46小時前</small>
<strong>小四</strong> 是不是隻有我死了,你們纔不罵爵跡
<br>
<small class="text-muted">3天前 2014.11.8</small>
</div>
</div>
</li>
<li class="divider"></li>
<li>
<div class="dropdown-messages-box">
<a href="profile.html" class="pull-left">
<img alt="image" class="img-circle" th:src="@{/static/img/a4.jpg}">
</a>
<div class="media-body ">
<small class="pull-right text-navy">25小時前</small>
<strong>二愣子</strong> 呵呵
<br>
<small class="text-muted">昨天</small>
</div>
</div>
</li>
<li class="divider"></li>
<li>
<div class="text-center link-block">
<a class="J_menuItem" href="mailbox.html">
<i class="fa fa-envelope"></i> <strong> 查看所有消息</strong>
</a>
</div>
</li>
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle count-info" data-toggle="dropdown" href="#">
<i class="fa fa-bell"></i> <span class="label label-primary">8</span>
</a>
<ul class="dropdown-menu dropdown-alerts">
<li>
<a href="mailbox.html">
<div>
<i class="fa fa-envelope fa-fw"></i> 您有16條未讀消息
<span class="pull-right text-muted small">4分鐘前</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a href="profile.html">
<div>
<i class="fa fa-qq fa-fw"></i> 3條新回覆
<span class="pull-right text-muted small">12分鐘錢</span>
</div>
</a>
</li>
<li class="divider"></li>
<li>
<div class="text-center link-block">
<a class="J_menuItem" href="notifications.html">
<strong>查看所有 </strong>
<i class="fa fa-angle-right"></i>
</a>
</div>
</li>
</ul>
</li>
</ul>
</nav>
</div>
<div class="row J_mainContent" id="content-main">
<iframe id="J_iframe" width="100%" height="100%" src="/home" frameborder="0" data-id="/home" seamless></iframe>
</div>
</div>
<!--右側部分結束-->
</div>
<!-- 全局js -->
<script th:src="@{/static/js/jquery.min.js?v=2.1.4}"></script>
<script th:src="@{/static/js/bootstrap.min.js?v=3.3.6}"></script>
<script th:src="@{/static/js/plugins/metisMenu/jquery.metisMenu.js}"></script>
<script th:src="@{/static/js/plugins/slimscroll/jquery.slimscroll.min.js}"></script>
<script th:src="@{/static/js/plugins/layer/layer.min.js}"></script>
<!-- 自定義js -->
<script th:src="@{/static/js/hAdmin.js?v=4.1.0}"></script>
<script type="text/javascript" th:src="@{/static/js/index.js}"></script>
<!-- 第三方插件 -->
<script th:src="@{/static/js/plugins/pace/pace.min.js}"></script>
</body>
</html>
利用“th:each”對後臺返回的"${menulist}"數據進行二重遍歷,動態構造出用戶的菜單。
打開瀏覽器,先輸入地址http://127.0.0.1:8080/index,會發現跳轉到登錄頁面,效果如下:
以不同身份登錄時菜單不同,以用戶名和密碼都爲“e”登錄:
點擊右上角的註銷鏈接後,以用戶名和密碼都爲“a”登錄:
還可以通過後臺動態修改角色的權限和用戶的角色,注意首先是對角色分配權限,然後再把角色分配給用戶。
完整的項目源碼已經分享在GitHub網站上(https://github.com/gjq246/springboot2author)。