一、项目背景
作为程序猿的你,是否在大学课堂上听到老师讲权限管理一脸懵逼;是否在互联网上看到炫酷的权限管理系统一脸羡慕;是否在公司学习使用权限管理一脸激动。那么,今天你看到这个教程之后,请你不要再懵逼,请不要再羡慕,请肆无忌惮的激动吧。好嗨哟,即将带你走上人生的巅峰。下面将手把手的教你实现基于shiro权限框架的权限管理系统,真正意义上的在按钮级别上完成权限控制。注:本教程侧重点在于shiro框架的使用与权限控制逻辑的实现,对于其它知识点不在重点讨论范围内。如需学习更多关于shiro的知识可在我的文章查看。
二、技术栈
后台:spring+springmvc+mybatis+shiro+kaptcha+fastjson+log4j+druid+maven+echcache+pageHelper等
前端:vue+jquery+iview+ztree等
数据库:mysql
其它:IntelliJ IDEA 2018.2.4 x64+tomcat8.0+jdk1.8
三、项目结构
四、项目预览
图1 登录首页
图2 系统首页
图3 用户列表
图4 用户新增
图5 角色列表
图6 角色新增
图7 菜单列表
图8 菜单新增
五、数据库设计
权限管理系统基础表有五张:sys_user(用户表)、sys_role(角色表)、sys_menu(资源表)、sys_user_role(用户角色关联表)、sys_role_menu(角色资源关联表),用户与角色和角色与资源都是多对多的关系,用户与资源必须通过授予角色才能建立关系。
(1)用户表详细设计
(2)角色表详细设计
(3)资源表详细设计
(4)用户角色表详细设计
(5)角色资源表详细设计
六、功能设计
(1)角色授权
此处新建一个角色为“测试”,授予用户管理菜单权限、新增按钮权限、删除按钮权限。
(2)分配角色
此处新增新建一个用户“a”,分配“测试”角色。
(3)新用户登录测试
因为“a”用户分配的角色为“测试”,“测试”角色拥有用户管理菜单、新增按钮、删除按钮权限,用该用户登录系统后只能看到用户管理菜单、新增按钮、删除按钮;不会看到其它的系统菜单和修改按钮。
七、项目代码示例
该项目的基础代码github地址为:https://github.com/tmAlj/shiro/tree/master/ssms,以下的给出的配置文件为本项目改变的内容,其余相同的部分未给出。
(1)pom.xml依赖管理配置
4.0.0
com.wsd
ssms1
1.0-SNAPSHOT
org.apache.maven.plugins
maven-compiler-plugin
7
7
org.springframework
spring-core
4.3.3.RELEASE
org.springframework
spring-context
4.3.3.RELEASE
org.springframework
spring-aspects
4.3.3.RELEASE
org.springframework
spring-context-support
4.3.3.RELEASE
org.springframework
spring-beans
4.3.3.RELEASE
org.springframework
spring-expression
4.3.3.RELEASE
org.springframework
spring-jdbc
4.3.3.RELEASE
org.springframework
spring-tx
4.3.3.RELEASE
mysql
mysql-connector-java
5.1.40
com.alibaba
druid
1.0.26
org.mybatis
mybatis
3.4.1
org.mybatis
mybatis-spring
1.3.0
com.github.pagehelper
pagehelper
4.1.6
org.springframework
spring-webmvc
4.3.3.RELEASE
jstl
jstl
1.2
taglibs
standard
1.1.2
org.slf4j
slf4j-api
1.7.19
org.slf4j
slf4j-log4j12
1.7.19
log4j
log4j
1.2.17
junit
junit
4.12
com.alibaba
fastjson
1.2.20
javax.servlet
javax.servlet-api
3.1.0
commons-codec
commons-codec
1.10
commons-configuration
commons-configuration
1.10
commons-lang
commons-lang
2.6
commons-fileupload
commons-fileupload
1.3.1
commons-io
commons-io
2.5
commons-logging
commons-logging
1.2
org.apache.shiro
shiro-core
1.3.2
org.apache.shiro
shiro-ehcache
1.3.2
org.apache.shiro
shiro-spring
1.3.2
org.apache.shiro
shiro-web
1.3.2
com.github.axet
kaptcha
0.0.9
(2)web.xml配置
tm-cli
login.jsp
contextConfigLocation
classpath:spring-config.xml
org.springframework.web.context.ContextLoaderListener
encode
org.springframework.web.filter.CharacterEncodingFilter
encoding
utf-8
encode
/*
appServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc-config.xml
1
true
appServlet
/
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
shiroFilter
/*
(3)shiro配置文件spring-shiro-config.xml配置
/statics/** = anon
/plugins/** = anon
/login.jsp = anon
/login = anon
/logout = logout
/captcha.jpg = anon
/** = authc
(4)springmvc配置文件spring-mvc-config.xml配置
application/json;charset=UTF-8
text/html;charset=UTF-8
WriteMapNullValue
QuoteFieldNames
no
black
5
八、部分功能解析
(1)登录认证(认证参考)
1.1 前端实现
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
用户登录
欢迎使用tm-cli管理系统
点击刷新
登 录
Copyright © 2018 All Rights Reserved
1.2 controller实现
package com.wsd.controller;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.wsd.utils.ResultData;
import com.wsd.utils.ShiroUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* Created by tm on 2018/8/26.
* 登录controller
*/
@Controller()
public class LoginController {
@Autowired
private Producer producer; //验证码操作对象
/**
* 生成验证码
* @param response
* @throws ServletException
* @throws IOException
*/
@RequestMapping("captcha.jpg")
public void captcha(HttpServletResponse response)throws ServletException, IOException {
//页面不用缓存
response.setHeader("Cache-Control", "no-store, no-cache");
response.setContentType("image/jpeg");
//生成文字验证码
String text = producer.createText();
//生成图片验证码
BufferedImage image = producer.createImage(text);
//验证码存入session,用于登录时做对比
ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
ServletOutputStream out = response.getOutputStream();
//输出验证码
ImageIO.write(image, "jpg", out);
}
/**
* 登录
*/
@ResponseBody
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResultData login(String account, String password, String code, String remember)throws IOException {
String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY); //session中获取保存的验证码内容
if(!kaptcha.equalsIgnoreCase(code)){
return ResultData.error("验证码不正确");
}
try{
Subject subject = ShiroUtils.getSubject();
password = new Sha256Hash(password).toHex();
UsernamePasswordToken token = new UsernamePasswordToken(account, password);
// 开启记住我的功能
if(remember.equals("false")){
token.setRememberMe(false);
}else{
token.setRememberMe(true);
}
subject.login(token);
}catch (UnknownAccountException e) {
return ResultData.error(e.getMessage());
}catch (IncorrectCredentialsException e) {
return ResultData.error(e.getMessage());
}catch (LockedAccountException e) {
return ResultData.error(e.getMessage());
}catch (AuthenticationException e) {
return ResultData.error("账户验证失败");
}catch (Exception e) {
return ResultData.error();
}
return ResultData.ok();
}
}
1.3 自定义loginRealm
package com.wsd.shiro;
import com.wsd.model.Menu;
import com.wsd.model.User;
import com.wsd.service.MenuService;
import com.wsd.service.UserService;
import com.wsd.service.impl.LoginServiceImpl;
import com.wsd.service.impl.MenuServiceImpl;
import com.wsd.service.impl.UserServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.*;
/**
* Created by tm on 2018/8/26.
* 自定义realm
* 注:需要在spring-shiro-config.xml中配置
*/
public class LoginRealm extends AuthorizingRealm {
@Autowired LoginServiceImpl lsi; //注入登录service
@Autowired MenuServiceImpl msi; //注入菜单service
@Autowired UserServiceImpl usi; //注入用户service
/*授权*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User)principalCollection.getPrimaryPrincipal();
Long userId = user.getUserId();
List permsList = null;
//系统管理员,拥有最高权限
if(userId == 1){
List
(2)权限控制(授权参考)
2.1 前端实现(通过shiro的标签控制前端权限标签参考)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
用户管理
新增
修改
删除
2.2 controller实现(通过shiro的权限注解控制请求注解参考)
package com.wsd.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.wsd.base.BaseController;
import com.wsd.model.User;
import com.wsd.service.impl.LoginServiceImpl;
import com.wsd.service.impl.UserAndRoleServiceImpl;
import com.wsd.service.impl.UserServiceImpl;
import com.wsd.utils.ResultData;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* Created by tm on 2018/8/26.
* 系统用户controller
*/
@Controller
@RequestMapping("sys/user")
public class UserController extends BaseController {
@Autowired UserServiceImpl usi; //注入系统用户service
@Autowired LoginServiceImpl lsi; //注入登录service
@Autowired UserAndRoleServiceImpl uarsi; //注入系统用户角色service
/**
* 访问user.jsp页面
* @return
*/
@RequestMapping()
public String goUserPage(){
return "user";
}
/**
* 访问user_add.jsp页面
* @return
*/
@RequestMapping("/user_add")
public String goUserAddPage(){
return "user_add";
}
/**
* 获取用户列表
* @param page 当前页码
* @param limit 每页显示条数
* @return
*/
@ResponseBody
@RequestMapping("/list")
@RequiresPermissions("sys:user:list")
public ResultData getUserList(Integer page, Integer limit, String userName){
//PageHelper分页插件
PageHelper.startPage(page, limit);
List userList = usi.queryList(userName);
PageInfo p = new PageInfo(userList);
return ResultData.ok().put("page", p);
}
/**
* 用户信息
*/
@ResponseBody
@RequestMapping("/info")
@RequiresPermissions("sys:user:info")
public ResultData info(Long userId){
//执行查询
User userInfo = usi.queryObject(userId);
//获取用户所属的角色列表
List roleIdList = uarsi.queryRoleIdList(userId);
userInfo.setRoleIdList(roleIdList);
return ResultData.ok().put("userInfo", userInfo);
}
/**
* 保存用户
* @param user 用户实体
* @return
*/
@ResponseBody
@RequestMapping("/save")
@RequiresPermissions("sys:user:save")
public ResultData saveUsers(@RequestBody User user){
//判断用户名称是否可用
User u = lsi.queryByUserName(user.getUsername());
if(u != null){
return ResultData.error("当前用户名称不能使用!");
}
usi.save(user);
return ResultData.ok();
}
/**
* 修改用户
*/
@ResponseBody
@RequestMapping("/update")
@RequiresPermissions("sys:user:update")
public ResultData update(@RequestBody User user){
usi.update(user);
return ResultData.ok();
}
/**
* 删除用户
* @param userIdList 用户id数组
* @return
*/
@ResponseBody
@RequestMapping("/delete")
@RequiresPermissions("sys:user:delete")
public ResultData deleteUsers(@RequestBody Long[] userIdList){
if(ArrayUtils.contains(userIdList, 1L)){
return ResultData.error("系统管理员不能删除");
}
if(ArrayUtils.contains(userIdList, getUserId())){
return ResultData.error("当前用户不能删除");
}
usi.deleteUser(userIdList);
return ResultData.ok();
}
}
九、参考文档
iview参考文档
vue参考文档
ztree参考文档
shiro参考文档
十、获取该项目源代码
A:微信扫描下方二维码,打赏一杯咖啡钱
B:打赏的时候留下您的邮箱地址,二十四小时内通过邮箱发送给您