zoukankan      html  css  js  c++  java
  • 使用 Shiro 设计基于用户、角色、权限的通用权限管理系统

    一、前言

    在大型的信息管理系统中,经常涉及到权限管理系统
    下面来个 demo,很多复杂的系统的设计都来自它
    代码已经放到github上了,地址:https://github.com/larger5/shiro_urp.git

    2018.4.3 版本0.5 在 SpringBoot 中使用 Shiro+MySQL 做登录拦截
    2018.4.6 版本1.0 使用 Shiro 设计基于用户、角色、权限的通用权限管理系统
    以后继续更新,如用户、角色、权限 CRUD 等,真正做到所谓的权限管理系统

    二、数据库表的设计

    下面使用 SpringBoot + JPA 的,自动生成如下的表
    用户角色多对多、角色权限多对多,设计一个通用的权限系统(无论初衷是一个用户多个角色还是一个角色)

    三、效果

    说明:

    ①UI:

      使用了 LayUI 简单优化一下界面
    

    ②角色所含权限:

       p:select
       ip:select、insert
       vip:select、insert、update、delete
    

    ③权限:

       select
       insert
       update
       delete
    

    以使用 itaem (VIP)为例

    图解:
    有:① select、insert、update、delete 权限 ② vip 角色
    无:ip角色、p角色,点击后都是没有反应的

    四、代码(github 平台上看)

    ① Controller(重点)

    package com.cun.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.http.HttpSession;
    import javax.validation.Valid;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.apache.shiro.subject.Subject;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.cun.entity.User;
    
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    @EnableSwagger2
    @RestController
    @RequestMapping("/user")
    public class UserController {
    
        @PostMapping("/login")
        public Map<String, Object> login(@Valid User user, BindingResult bindingResult, HttpSession session) {
            Map<String, Object> map = new HashMap<String, Object>();
    
            // 1、JSR303
            if (bindingResult.hasErrors()) {
                map.put("success", false);
                map.put("errorInfo", bindingResult.getFieldError().getDefaultMessage());
                return map;
            }
    
            // 2、Shiro
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());
            try {
                subject.login(token);
                map.put("success", true);
                return map;
            } catch (Exception e) {
                e.printStackTrace();
                map.put("success", false);
                map.put("errorInfo", "用户名或者密码错误!");
                return map;
            }
        }
    

    ④ ShiroConfig(重点)

    package com.cun.config;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    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.DefaultWebSecurityManager;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.apache.shiro.mgt.SecurityManager;
    import com.cun.realm.MyRealm;
    
    /**
     * Shiro配置类
     * @author linhongcun
     *
     */
    @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.html");
    
            // 拦截器.
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
            // 配置不会被拦截的链接 顺序判断
            filterChainDefinitionMap.put("/static/**", "anon");
            filterChainDefinitionMap.put("/user/login", "anon");
            //测试权限用
            filterChainDefinitionMap.put("/swagger-ui.html", "anon");
    
            // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
            filterChainDefinitionMap.put("/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());
            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)即可实现此功能
         * @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;
        }
    }
    

    ⑤ MyRealm(重点)

    package com.cun.realm;
    
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    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 org.springframework.beans.factory.annotation.Autowired;
    
    import com.cun.dao.PermissionDao;
    import com.cun.dao.RoleDao;
    import com.cun.dao.UserDao;
    import com.cun.entity.Permission;
    import com.cun.entity.Role;
    import com.cun.entity.User;
    
    public class MyRealm extends AuthorizingRealm {
    
        @Autowired
        private UserDao userDao;
    
        @Autowired
        private RoleDao roleDao;
    
        @Autowired
        private PermissionDao permissionDao;
    
        /**
         * 授权
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            String userName=(String) SecurityUtils.getSubject().getPrincipal();
            SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
            Set<String> roles=new HashSet<String>();
            List<Role> rolesByUserName = roleDao.getRolesByUserName(userName);
            for(Role role:rolesByUserName) {
                roles.add(role.getRoleName());
            }
            List<Permission> permissionsByUserName = permissionDao.getPermissionsByUserName(userName);
            for(Permission permission:permissionsByUserName) {
                info.addStringPermission(permission.getPermissionName());
            }
            info.setRoles(roles);
            return info;
        }
    
        /**
         * 认证
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("token.getPrincipal:" + token.getPrincipal());
            System.out.println("token.getCredentials:" + token.getCredentials());
            String userName = token.getPrincipal().toString();
            User user = userDao.getUserByUserName(userName);
            if (user != null) {
                // Object principal, Object credentials, String realmName
                AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), getName());
                return authcInfo;
            } else {
                return null;
            }
        }
    
    }
    

    五、其他

    ① sql

    CREATE DATABASE /*!32312 IF NOT EXISTS*/`urp` /*!40100 DEFAULT CHARACTER SET utf8 */;
    
    USE `urp`;
    
    /*Table structure for table `t_permission` */
    
    DROP TABLE IF EXISTS `t_permission`;
    
    CREATE TABLE `t_permission` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `permission_name` varchar(50) DEFAULT NULL,
      `remarks` varchar(1000) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
    
    /*Data for the table `t_permission` */
    
    insert  into `t_permission`(`id`,`permission_name`,`remarks`) values (1,'select','查询'),(2,'insert','增加'),(3,'update','更新'),(4,'delete','删除');
    
    /*Table structure for table `t_role` */
    
    DROP TABLE IF EXISTS `t_role`;
    
    CREATE TABLE `t_role` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `remarks` varchar(1000) DEFAULT NULL,
      `role_name` varchar(100) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    
    /*Data for the table `t_role` */
    
    insert  into `t_role`(`id`,`remarks`,`role_name`) values (1,'普通角色','p'),(2,'重要角色','ip'),(3,'超级角色','vip');
    
    /*Table structure for table `t_role_permission` */
    
    DROP TABLE IF EXISTS `t_role_permission`;
    
    CREATE TABLE `t_role_permission` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `remarks` varchar(1000) DEFAULT NULL,
      `permission_id` int(11) DEFAULT NULL,
      `role_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `FKjobmrl6dorhlfite4u34hciik` (`permission_id`),
      KEY `FK90j038mnbnthgkc17mqnoilu9` (`role_id`),
      CONSTRAINT `FK90j038mnbnthgkc17mqnoilu9` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`),
      CONSTRAINT `FKjobmrl6dorhlfite4u34hciik` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
    
    /*Data for the table `t_role_permission` */
    
    insert  into `t_role_permission`(`id`,`remarks`,`permission_id`,`role_id`) values (1,'授予普通角色select权限',1,1),(2,'授予重要角色select权限',1,2),(3,'授予重要角色insert权限',2,2),(4,'授予超级角色select权限',1,3),(5,'授予超级角色insert权限',2,3),(6,'授予超级角色update权限',3,3),(7,'授予超级角色delete权限',4,3);
    
    /*Table structure for table `t_user` */
    
    DROP TABLE IF EXISTS `t_user`;
    
    CREATE TABLE `t_user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `password` varchar(100) NOT NULL,
      `remarks` varchar(1000) DEFAULT NULL,
      `user_name` varchar(100) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    
    /*Data for the table `t_user` */
    
    insert  into `t_user`(`id`,`password`,`remarks`,`user_name`) values (1,'123','JKing团队','jking'),(2,'123','网维团队','wteam'),(3,'123','ITAEM团队','itaem');
    
    /*Table structure for table `t_user_role` */
    
    DROP TABLE IF EXISTS `t_user_role`;
    
    CREATE TABLE `t_user_role` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `remarks` varchar(1000) DEFAULT NULL,
      `role_id` int(11) DEFAULT NULL,
      `user_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `FKa9c8iiy6ut0gnx491fqx4pxam` (`role_id`),
      KEY `FKq5un6x7ecoef5w1n39cop66kl` (`user_id`),
      CONSTRAINT `FKq5un6x7ecoef5w1n39cop66kl` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`),
      CONSTRAINT `FKa9c8iiy6ut0gnx491fqx4pxam` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    
    /*Data for the table `t_user_role` */
    
    insert  into `t_user_role`(`id`,`remarks`,`role_id`,`user_id`) values (1,'授予JKing团队普通角色',1,1),(2,'授予网维团队重要角色',2,2),(3,'授予ITAEM团队超级角色',3,3);
    

    转载https://blog.csdn.net/larger5/article/details/79838212

  • 相关阅读:
    Python多线程笔记(三),queue模块
    Python多线程笔记(二)
    Python多线程笔记(一)
    Python3对时间模块的操作
    解决 ImportError: No module named 'pip._internal'问题
    Python字符串的操作
    Python 的AES加密与解密
    第十一周学习进度报告
    各组建议
    用户场景分析
  • 原文地址:https://www.cnblogs.com/sunBinary/p/11010677.html
Copyright © 2011-2022 走看看