美文网首页服务端开发实战
01-shiro权限系统-项目搭建

01-shiro权限系统-项目搭建

作者: 该叫什么昵称好 | 来源:发表于2018-11-13 10:39 被阅读25次

    权限系统的必要性

    权限管理系统是众多大型项目的标配。可以说,如果没有权限系统的支持,很多功能都会变得难以实现。

    OA 系统为例,各个部门要有不同的操作界面、资料。比如,人事部只要公司的人员资料录入,销售部只要公司的销售资料录入。这时候,如果没有限制各个部门的权限,系统就很容易出现:人员误操作、系统的学习成本高昂等情况。

    总结上述例子,权限系统有以下优点:

    1. 安全性:避免误操作、人为破坏、数据泄露等;
    2. 数据隔离:不同权限能看到、操作不同的数据;
    3. 明确职责:可以分配:运营、客服等不同角色,领导及下属等不同等级;

    权限系统分类

    用户—权限

    适合人员少,功能固定,或者特别简单的系统

    例如,Mysql中用户对数据库的操作权限

    RBAC(用户->角色->权限)

    用户->角色->权限,都适用。整体关系,如下图:

    用户角色.png

    文章中,我们用 shiro 来实现 RBAC 权限系统,下面我们先搭建一个基本的业务系统。

    业务系统

    创建数据表

    shiro 是一个 RBAC 型的权限系统,适用于:用户->角色->权限。所以,我们需要新建的数据表有:

    1. 用户表,包括:id、用户名、密码;
    2. 角色表,包括:id、英文名、中文名;
    3. 权限表,包括:id、权限标识、对应url;
    4. 用户、角色关联表,包括:用户id、角色id;
    5. 角色、权限关联表,包括:角色id、权限id;

    SQL脚本如下:
    init.sql

    实体类

    根据数据库中的表,我们可以得出下面 3 个实体类,他们的关系:用户 ——> 角色 ——> 权限,如 UML 图:

    实体类及关系.PNG

    他们的代码,即下列所示:

    1. 用户对象
        import java.io.Serializable;
        import java.util.HashSet;
        import java.util.Set;
        
        public class User implements Serializable {
        
            // 唯一编号
            private Integer uid;
        
            // 用户名
            private String username;
        
            // 密码
            private String password;
        
            // 角色列表
            private Set<Role> roles = new HashSet<>();
        
        }
    
    1. 角色对象
        import java.io.Serializable;
        import java.util.HashSet;
        import java.util.Set;
    
        public class Role implements Serializable {
        
            // 唯一编号
            private Integer rid;
        
            // 角色名称
            private String rname;
        
            // 角色包含的权限集合
            private Set<Permission> permissions = new HashSet<>();
            
            /** 省略 getter 和 setter 方法 **/
        }
    
    1. 权限对象
        import java.io.Serializable;
    
        public class Permission implements Serializable {
            // 唯一编号
            private Integer pid;
        
            // 权限标识
            private String name;
        
            // 权限对应的url
            private String url;
        
            /** 省略 getter 和 setter 方法 **/
        }
    

    数据库查询代码

    程序使用 SpringBoot + Mybatis,这里我们只关注 XML 中的主要 SQL,其它请参考下载项目代码:

        <!-- 拼装返回结果,用于映射 User 对象 -->
        <resultMap id="userMap" type="com.mmall.demo2.model.User">
            <id property="uid" column="uid" />
            <result property="username" column="username" />
            <result property="password" column="password" />
            
            <!-- 拼装角色列表 -->
            <collection property="roles" ofType="com.mmall.demo2.model.Role">
                <id property="rid" column="rid" />
                <result property="rname" column="rname" />
                
                <!-- 拼装权限列表 -->
                <collection property="permissions" ofType="com.mmall.demo2.model.Permission">
                    <id property="pid" column="pid" />
                    <result property="name" column="name"/>
                    <result property="url" column="url" />
                </collection>
            </collection>
        </resultMap>
    
        <!-- 根据用户名查询用户、角色、权限 -->
        <select id="findByUsername" parameterType="string" resultMap="userMap">
          SELECT u.*, r.*, p.*
          FROM user u
            INNER JOIN user_role ur on ur.uid = u.uid
            INNER JOIN role r on r.rid = ur.rid
            INNER JOIN permission_role pr on pr.rid = r.rid
            INNER JOIN permission p on pr.pid = p.pid
          WHERE u.username = #{username}
        </select>
    

    控制层

    项目使用 SpringMVC 提供“视图及数据交互”接口,属于主流技术,这里不再详细说明,直接贴出整个 Controller :

        import com.mmall.demo2.model.User;
        import org.apache.shiro.SecurityUtils;
        import org.apache.shiro.authc.UsernamePasswordToken;
        import org.apache.shiro.subject.Subject;
        import org.springframework.stereotype.Controller;
        import org.springframework.ui.Model;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RequestParam;
        import org.springframework.web.bind.annotation.ResponseBody;
        
        @Controller
        public class TestController {
        
            /**
             * 登陆页面
             * @return
             */
            @RequestMapping("/login")
            public String login() {
                return "login";
            }
        
            /**
             * 主页
             * @param model
             * @return
             */
            @RequestMapping("/index")
            public String index(Model model) {
                Subject subject = SecurityUtils.getSubject();
                User user = (User) subject.getPrincipal();
        
                model.addAttribute("user", user);
                return "index";
            }
        
            /**
             * 用户登陆逻辑
             * @param username
             * @param password
             * @return
             */
            @RequestMapping("/loginUser")
            public String loginUser(@RequestParam("username") String username,
                                    @RequestParam("password") String password) {
                UsernamePasswordToken token = new UsernamePasswordToken(username, password);
                Subject subject = SecurityUtils.getSubject();
                try {
                    subject.login(token);
                    return "redirect:/index";
                } catch (Exception e) {
                    return "login";
                }
            }
        
            /**
             * 用户注销逻辑
             * @return
             */
            @RequestMapping("/logout")
            public String logout() {
                Subject subject = SecurityUtils.getSubject();
                if (subject != null) {
                    subject.logout();
                }
                return "login";
            }
        
            /**
             * 无权限访问页面
             * @return
             */
            @RequestMapping("unauthorized")
            public String unauthorized() {
                return "unauthorized";
            }
        
            /**
             * 管理员专属页面,只有管理员角色可以访问
             * @return
             */
            @ResponseBody
            @RequestMapping("/admin")
            public String admin() {
                return "admin";
            }
        
            /**
             * 拥有“修改”权限,才能访问该接口
             * @return
             */
            @ResponseBody
            @RequestMapping("/edit")
            public String edit() {
                return "edit success";
            }
        
            /**
             * 拥有“查询”权限,才能访问该接口
             * @return
             */
            @ResponseBody
            @RequestMapping("/query")
            public String query() {
                return "query success";
            }
        
        }
    

    写在最后

    我们已经完成了权限系统的基础搭建。下篇文章,我们就开始把 Shiro 整合到项目里面。这里附上项目源码:

    shiro单服务器版地址

    相关文章

      网友评论

        本文标题:01-shiro权限系统-项目搭建

        本文链接:https://www.haomeiwen.com/subject/pkykfqtx.html