一.前言
今天我们用springboot集成shiro框架,实现一个可以通用的后台管理系统;包括用户管理,角色管理,菜单管理三大系统常用管理模块;
二.数据库表准备:
要想实现用户管理+角色管理+菜单管理三大模块,基本上我们常用的解决方案就是如下五个表(sql脚本在最后):
三.集成shiro和配置
1.添加pom依赖。
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
2.编辑shiro配置类:ShiroConfig.java
package com.zjt.config; import com.zjt.realm.MyRealm; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @Author: Zhaojiatao * @Description: Shiro配置类 * @Date: Created in 2018/2/8 13:29 * @param */ @Configuration public class ShiroConfig { /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在 * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过 * 3、部分过滤器可指定参数,如perms,roles * */ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 //shiroFilterFactoryBean.setLoginUrl("/login.ftl"); //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了 shiroFilterFactoryBean.setLoginUrl("/tologin"); shiroFilterFactoryBean.setUnauthorizedUrl("/tologin"); // 拦截器. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //配置记住我或认证通过可以访问的地址(配置不会被拦截的链接 顺序判断) filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/user/login", "anon"); filterChainDefinitionMap.put("/drawImage", "anon"); // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/admin/user/logout", "logout"); // <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了; // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 设置realm. securityManager.setRealm(myRealm()); //注入记住我管理器; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } /** * 身份认证realm; (这个需要自己写,账号密码校验;权限等) * * @return */ @Bean public MyRealm myRealm() { MyRealm myRealm = new MyRealm(); return myRealm; } /** * Shiro生命周期处理器 * @return */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } /** * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 * 不要使用 DefaultAdvisorAutoProxyCreator 会出现二次代理的问题,这里不详述 * @return */ /* @Bean @DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; }*/ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; } /** * cookie对象; * 记住密码实现起来也是比较简单的,主要看下是如何实现的。 * @return */ @Bean public SimpleCookie rememberMeCookie(){ System.out.println("ShiroConfiguration.rememberMeCookie()"); //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); //<!-- 记住我cookie生效时间30天 ,单位秒;--> simpleCookie.setMaxAge(259200); return simpleCookie; } /** * cookie管理对象; * @return */ @Bean public CookieRememberMeManager rememberMeManager(){ System.out.println("ShiroConfiguration.rememberMeManager()"); CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); return cookieRememberMeManager; } }
3.实现自定义MyRealm.java
package com.zjt.realm; import com.zjt.entity.Tmenu; import com.zjt.entity.Trole; import com.zjt.entity.Tuser; import com.zjt.mapper.TmenuMapper; import com.zjt.mapper.TroleMapper; import com.zjt.mapper.TuserMapper; import com.zjt.mapper.TuserroleMapper; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; 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 tk.mybatis.mapper.entity.Example; import javax.annotation.Resource; import java.util.HashSet; import java.util.List; import java.util.Set; /** * 自定义Realm * @author zjt * */ public class MyRealm extends AuthorizingRealm{ @Resource private TuserMapper tuserMapper; @Resource private TroleMapper troleMapper; @Resource private TuserroleMapper tuserroleMapper; @Resource private TmenuMapper tmenuMapper; /** * 授权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String userName=(String) SecurityUtils.getSubject().getPrincipal(); //User user=userRepository.findByUserName(userName); //根据用户名查询出用户记录 Example tuserExample=new Example(Tuser.class); tuserExample.or().andEqualTo("userName",userName); Tuser user=tuserMapper.selectByExample(tuserExample).get(0); SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); //List<Role> roleList=roleRepository.findByUserId(user.getId()); List<Trole> roleList = troleMapper.selectRolesByUserId(user.getId()); Set<String> roles=new HashSet<String>(); if(roleList.size()>0){ for(Trole role:roleList){ roles.add(role.getName()); //List<Tmenu> menuList=menuRepository.findByRoleId(role.getId()); //根据角色id查询所有资源 List<Tmenu> menuList=tmenuMapper.selectMenusByRoleId(role.getId()); for(Tmenu menu:menuList){ info.addStringPermission(menu.getName()); // 添加权限 } } } info.setRoles(roles); return info; } /** * 权限认证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String userName=(String)token.getPrincipal(); //User user=userRepository.findByUserName(userName); Example tuserExample=new Example(Tuser.class); tuserExample.or().andEqualTo("userName",userName); Tuser user=tuserMapper.selectByExample(tuserExample).get(0); if(user!=null){ AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),"xxx"); return authcInfo; }else{ return null; } } }
4.登录、退出、权限限制
登录:subject.login(token);
退出:SecurityUtils.getSubject().logout();
在方法前使用shiro注解实现权限校验,如:@RequiresPermissions(value = {"用户管理"}) 表示当前用户必须拥有用户管理的权限;
四、前端实现及效果展示
1、登录
请求http://localhost:8080/blogmanager/
被shiro拦截后自动跳转到登录界面;
项目地址可以在配置文件中配置:
源码:src\main\resources\templates\login.ftl
用户名:admin
密码:1
2、系统管理-菜单管理
菜单管理页面源码:src\main\resources\templates\power\menu.ftl
里面使用了ztree实现的菜单的新建、编辑、删除;
菜单管理的后台接口:com.zjt.web.MenuController.java
注意一级菜单在顶部显示,且一级菜单名不可为纯数字;
二级三级菜单在左侧显示,且最多只能到三级菜单;
3、系统管理-角色管理
src\main\resources\templates\power\role.ftl
com.zjt.web.RoleAdminController.java
页面使用了jqgrid表格插件;
并可以设置每个角色对应的菜单权限:
4、系统管理-用户管理
src\main\resources\templates\power\user.ftl
com.zjt.web.UserAdminController.java
选择行后可以设置角色:
五、后记
本后台管理系统可作为通用的后台管理系统,她简单纯净;内置完善的菜单管理+角色管理+用户管理;拿来即用;
使用技术涉及:
springboot+springmvc+mysql+mybatis+通用mapper+分页插件+shiro+freemarker+layui+ztree
其中layui模板使用的是layuicms2.0
本项目源码:
https://github.com/zhaojiatao/springboot-zjt-chapter10-springboot-mysql-mybatis-shiro-freemarker-layui.git
sql脚本含在项目sql文件夹中
项目访问地址和端口在配置文件中:application-dev.properties