other|基于shiro的按钮级别的权限管理系统

一、项目背景
作为程序猿的你,是否在大学课堂上听到老师讲权限管理一脸懵逼;是否在互联网上看到炫酷的权限管理系统一脸羡慕;是否在公司学习使用权限管理一脸激动。那么,今天你看到这个教程之后,请你不要再懵逼,请不要再羡慕,请肆无忌惮的激动吧。好嗨哟,即将带你走上人生的巅峰。下面将手把手的教你实现基于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
三、项目结构
other|基于shiro的按钮级别的权限管理系统
文章图片

other|基于shiro的按钮级别的权限管理系统
文章图片

四、项目预览
other|基于shiro的按钮级别的权限管理系统
文章图片

图1登录首页
other|基于shiro的按钮级别的权限管理系统
文章图片

图2系统首页
other|基于shiro的按钮级别的权限管理系统
文章图片

图3用户列表
other|基于shiro的按钮级别的权限管理系统
文章图片

图4用户新增
other|基于shiro的按钮级别的权限管理系统
文章图片

图5角色列表
other|基于shiro的按钮级别的权限管理系统
文章图片

图6角色新增
other|基于shiro的按钮级别的权限管理系统
文章图片

图7菜单列表
other|基于shiro的按钮级别的权限管理系统
文章图片

图8菜单新增
五、数据库设计
权限管理系统基础表有五张:sys_user(用户表)、sys_role(角色表)、sys_menu(资源表)、sys_user_role(用户角色关联表)、sys_role_menu(角色资源关联表),用户与角色和角色与资源都是多对多的关系,用户与资源必须通过授予角色才能建立关系。
other|基于shiro的按钮级别的权限管理系统
文章图片

(1)用户表详细设计
other|基于shiro的按钮级别的权限管理系统
文章图片

(2)角色表详细设计
other|基于shiro的按钮级别的权限管理系统
文章图片

(3)资源表详细设计
【other|基于shiro的按钮级别的权限管理系统】other|基于shiro的按钮级别的权限管理系统
文章图片

(4)用户角色表详细设计
other|基于shiro的按钮级别的权限管理系统
文章图片

(5)角色资源表详细设计
other|基于shiro的按钮级别的权限管理系统
文章图片

六、功能设计
(1)角色授权
此处新建一个角色为“测试”,授予用户管理菜单权限、新增按钮权限、删除按钮权限。
other|基于shiro的按钮级别的权限管理系统
文章图片

(2)分配角色
此处新增新建一个用户“a”,分配“测试”角色。
other|基于shiro的按钮级别的权限管理系统
文章图片

(3)新用户登录测试
因为“a”用户分配的角色为“测试”,“测试”角色拥有用户管理菜单、新增按钮、删除按钮权限,用该用户登录系统后只能看到用户管理菜单、新增按钮、删除按钮;不会看到其它的系统菜单和修改按钮。
other|基于shiro的按钮级别的权限管理系统
文章图片

七、项目代码示例
该项目的基础代码github地址为:https://github.com/tmAlj/shiro/tree/master/ssms,以下的给出的配置文件为本项目改变的内容,其余相同的部分未给出。
(1)pom.xml依赖管理配置

4.0.0com.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 contextConfigLocationclasspath:spring-config.xml org.springframework.web.context.ContextLoaderListener encode org.springframework.web.filter.CharacterEncodingFilter encodingutf-8 encode /* appServlet org.springframework.web.servlet.DispatcherServlet contextConfigLocationclasspath:spring-mvc-config.xml 1 trueappServlet / shiroFilter org.springframework.web.filter.DelegatingFilterProxy targetFilterLifecycletrue 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 noblack5

八、部分功能解析
(1)登录认证(认证参考)
1.1前端实现
用户登录 - 锐客网 欢迎使用tm-cli管理系统 other|基于shiro的按钮级别的权限管理系统
文章图片
点击刷新登        录记住我忘记密码?Copyright ? 2018 All Rights Reserved

1.2controller实现
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 = "https://www.it610.com/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 menuList = msi.queryList(new HashMap()); permsList = new ArrayList(menuList.size()); for(Menu menu : menuList){ permsList.add(menu.getPerms()); } }else{ permsList = usi.queryAllPerms(userId); }//用户权限列表 Set permsSet = new HashSet(); for(String perms : permsList){ if(StringUtils.isBlank(perms)){ continue; } permsSet.addAll(Arrays.asList(perms.trim().split(","))); } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(permsSet); return info; }/*验证*/ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String account = (String) authenticationToken.getPrincipal(); //获取输入的账户 String password = new String((char[]) authenticationToken.getCredentials()); //获取输入的密码//数据中查询用户信息 User user = lsi.queryByUserName(account); //账号不存在 if(user == null) { throw new UnknownAccountException("账号或密码不正确"); } //密码错误 if(!password.equals(user.getPassword())) { throw new IncorrectCredentialsException("账号或密码不正确"); } //账号锁定 if(user.getStatus() == 0){ throw new LockedAccountException("账号已被锁定,请联系管理员"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName()); return info; } }

(2)权限控制(授权参考)
2.1前端实现(通过shiro的标签控制前端权限标签参考)
用户管理 - 锐客网 新增修改删除

2.2controller实现(通过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:打赏的时候留下您的邮箱地址,二十四小时内通过邮箱发送给您
other|基于shiro的按钮级别的权限管理系统
文章图片


    推荐阅读