• 【shiro】(2)---基于RUL的权限管理


    基于RUL的权限管理

         我想在写shiro权限管理认证前,先来一个基于URL实现的权限管理控制。

    一、基于URI的权限业务逻辑

       实现思路:
           将系统操作的每个url配置在权限表中,将权限对应到角色将角色分配给用户用户访问系统功能通过Filter进行过虑过虑器获取到用户访问的url只要访问的url是用户分配角色中的url则放行继续访问
    具体逻辑看下面一张表。

     二、环境搭建(数据表)

       思维已经清晰,接下来通过代码来实现具体业务逻辑。
       开发环境
              JDK1.8
              Eclipse
              Maven
       技术架构:SpringMVC+Mybatis+jQuery easyUI

    数据库

        MySQL数据库中创建表:用户表、角色表权限表(实质是权限和资源的结合)、用户角色关系表、角色权限关系表

    (1)具体表

      sys_user  用户表(这里明文密码都是:111111)

     

      sys_role  角色表

     

      sys_user_role 权限表

     

    sys_permission 权限表

     

     sys_role_permission  角色权限关系表

     

    三、用户认证

       1.login.action用户登录页面

    <BODY >
        <FORM id="loginform" name="loginform" action="?loginsubmit.action"
            method="post">
                    <TABLE class="tab" border="0" cellSpacing="6" cellPadding="8">
                        <TBODY>
                            <TR>
                                <TD>用户名:</TD>
                                <TD colSpan="2"><input type="text" id="usercode"
                                    name="usercode" style="WIDTH: 130px" /></TD>
                            </TR>
                            <TR>
                                <TD>密 码:</TD>
                                <TD><input type="password" id="pwd" name="password" style="WIDTH: 130px" />
                                </TD>
                            </TR>
                            <TR>
                            <TD>验证码:</TD>
                                <TD><input id="randomcode" name="randomcode" size="8" /> <img
                                    id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""
                                    width="56" height="20" align='absMiddle' /> <a
                                    href=javascript:randomcode_refresh()>刷新</a></TD>
                            </TR>
    
                            <TR>
                                <TD colSpan="2" align="center"><input type="sumbit"
                                     value="登 录" />
                                </TD>
                            </TR>
                        </TBODY>
                    </TABLE>
        </FORM>
    </BODY>
    login.jsp

       2、SpringMVC配置过滤器

    <!-- 用户身份校验的拦截器 -->
            <mvc:interceptor>
                <mvc:mapping path="/**" />
                <bean class="com.jincou.controller.interceptor.LoginInterceptor"></bean>
            </mvc:interceptor>

    3、LoginInterceptor过滤器

    public class LoginInterceptor implements HandlerInterceptor {
    
        // 在进入controller方法之前执行
        // 进行身份认证校验拦截,如果拦截不放行,controller方法不再执行
        @Override
        public boolean preHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler) throws Exception {
    
            // 校验用户访问是否是公开资源地址(无需认证即可访问)
            List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
    
            // 用户访问的url(这里用户登录页面login.action,和登录提交页面loginsubmit.action属于公开访问页面,直接发行)
            String url = request.getRequestURI();
            for (String open_url : open_urls) {
                if (url.indexOf(open_url) >= 0) {
                    // 如果访问的是公开 地址则放行
                    return true;
                }
            }
    
            // 校验用户身份是否认证通过
            HttpSession session = request.getSession();
            ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
            if (activeUser != null) {
                // 用户已经登陆认证,放行
                return true;
            }
            // 跳转到登陆页面
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,
                    response);
            return false;
        }
    
    }

    (1)anonymousURL.properties配置文件

    #不需要登录就可以访问的页面:比如登录页面,登陆提交的地址,网站首页等
    login.action=登录页面
    loginsubmit.action=登陆提交按钮的地址
    配置文件

    4、LoginControl控制层

    /**
     * 获得用户名,密码,验证码进行用户验证,验证通过就把该用户放到session中
     */
    @Controller
    public class LoginController {
        
        @Autowired
        private SysService sysService ;
    
        //用户登陆提交
        @RequestMapping("/loginsubmit")
        public String loginsubmit(HttpSession session,String usercode,String password,String randomcode) throws Exception{
    
            //校验验证码
            //从session获取正确的验证码(因为验证码是从前端生成,传到后端)
            String validateCode = (String)session.getAttribute("validateCode");
            if(!randomcode.equals(validateCode)){
                //抛出异常:验证码错误
                throw new CustomException("验证码 错误 !");
            }
            //用户身份认证
            ActiveUser activeUser = sysService.authenticat(usercode, password);
            
            //记录session
            session.setAttribute("activeUser", activeUser);
            
            return "redirect:first.action";
        }
    }

       4.1ActiveUser实体类

    /**
     * 用户身份信息,获取通过用户名得到密码和菜单和页面权限
     */
    public class ActiveUser implements java.io.Serializable {
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private String userid;//用户id
        private String usercode;// 用户账号
        private String username;// 用户名称
    
        private List<SysPermission> menus;// 菜单
        private List<SysPermission> permissions;// 权限
        //set和get方法
    }
    ActiveUser

     5.SysServiceImpl实现类

    //@Override
        public ActiveUser authenticat(String usercode, String password)
                throws Exception {
            
            //账号和密码非空校验 
            //....
            
            SysUserExample sysUserExample = new SysUserExample();
            SysUserExample.Criteria criteria = sysUserExample.createCriteria();
            criteria.andUsercodeEqualTo(usercode);
            List<SysUser> userList = sysUserMapper.selectByExample(sysUserExample);
            if(userList == null || userList.size()<=0){
                throw new CustomException("账号不存在!");
            }
            SysUser sysUser = userList.get(0);
            //密码 
            String password_fromdb = sysUser.getPassword();
            
            //输入 密码 和数据库密码 比较(因为数据库加过密所以这里也加密后进行比较)
            if(!password_fromdb.equalsIgnoreCase(new MD5().getMD5ofStr(password))){
                throw new CustomException("账号或密码 错误 !");
            }
            //认证通过,返回用户身份
            ActiveUser activeUser = new ActiveUser();
            activeUser.setUserid(sysUser.getId());
            activeUser.setUsername(sysUser.getUsername());
            activeUser.setUsercode(sysUser.getUsercode());
            
            //菜单列表
            List<SysPermission> menus = sysPermissionMapperCustom.findMenuByUserid(sysUser.getId());
            activeUser.setMenus(menus);
            //权限列表
            List<SysPermission> permissions = sysPermissionMapperCustom.findPermissionByUserid(sysUser.getId());
            activeUser.setPermissions(permissions);
            //获得用户菜单和权限列表
            return activeUser;
        }

    总结:     

    用户认证的思路很简单:
    (1)判断用户登录是否成功,成功将用户信息放入session中,登录后点击其它页面,过滤器会看session是否存在用户,存在则放行。
    (2)如果你没有登录,点击其它非公共页面,那过滤器会发现session中没有用户信息,则跳转到登录页面。

     三、权限管理

        既你登录认证通过后,根据用户id从数据库中获取用户权限范围的URL,将URL的集合存储在activeUser中并存在session中。

         1.springMVC配置权限认证过滤器

    <!-- 用户登陆成功后的资源权限拦截器 -->
    <mvc:interceptor>
    <mvc:mapping path="/**" />
    <bean class="com.jincou.controller.interceptor.PermissionInterceptor"></bean>
    </mvc:interceptor>

          2、PermissionInterceptor过滤器

    public class PermissionInterceptor implements HandlerInterceptor {
    
        // 在进入controller方法之前执行
        // 用户权限拦截,如果拦截不放行,controller方法不再执行
        // 进入action方法前要执行
        @Override
        public boolean preHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler) throws Exception {
            // TODO Auto-generated method stub
            // 用户访问地址:
            String url = request.getRequestURI();
    
            // 校验用户访问是否是公开资源地址(无需认证即可访问)
            List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
            // 用户访问的url
            for (String open_url : open_urls) {
                if (url.indexOf(open_url) >= 0) {
                    // 如果访问的是公开 地址则放行
                    return true;
                }
            }
            //从 session获取用户公共访问地址(认证通过无需分配权限即可访问)
            List<String> common_urls = ResourcesUtil.gekeyList("commonURL");
            // 用户访问的url
            for (String common_url : common_urls) {
                if (url.indexOf(common_url) >= 0) {
                    // 如果访问的是公共地址则放行
                    return true;
                }
            }
            // 从session获取用户权限信息
    
            HttpSession session = request.getSession();
    
            ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
    
            // 取出session中权限url
            // 获取用户操作权限
            List<SysPermission> permission_list = activeUser.getPermissions();
            // 校验用户访问地址是否在用户权限范围内
            for (SysPermission sysPermission : permission_list) {
                String permission_url = sysPermission.getUrl();
                if (url.contains(permission_url)) {
                    return true;
                }
            }
    
            // 跳转到无权访问页面
            request.getRequestDispatcher("/refuse.jsp").forward(
                    request, response);
            return false;
        }
    
    }

       (1)commonURL配置文件

    #用户公共访问地址指的是用户登录后,有些页面也是不需要配置权限的,每个用户都会有的页面:比如:个人中心,退出页面等
        first.action=系统页面
        logout.action=退出页面
    配置文件

    总结:   

    权限认证的思路:
       (1)当用户登录成功后,因为上面ActiveUser对象,里面有用户可以访问哪些菜单,所以页面也只会显示这些菜单。
       (2)当我用户浏览我没有权限的URL页面,那么PermissionInterceptor会判断我没有这个页面的权限,直接调整到无权访问页面。

    四、一些细节

    1、前段页面是如何做到当前用户可以获得哪些菜单(可以循环遍历)

    <c:if test="${activeUser.menus!=null }">    
        <c:forEach items="${activeUser.menus }" var="menu">
            <a href="${pageContext.request.contextPath }/${menu.url }" >${menu.name }</a>        
        </c:forEach>
    </c:if> 

    2、验证下"zhangsan"用户能看到的菜单是否正确

    (1)先看没有做权限页面的菜单

     

    (2)再看做了权限管理的页面

    (3)那到底是不是这样?(数据查询正确)

      

     github地址 https://github.com/yudiandemingzi/URLquanxian

    想太多,做太少,中间的落差就是烦恼。想没有烦恼,要么别想,要么多做。少校【3】

  • 相关阅读:
    mysql5大引擎之间的区别和优劣之分
    关于集合
    泛型
    集合2
    easyui 入门讲解
    <input>和<textarea>
    php中echo(),print(),print_r(),var_dump()间的区别
    PHP图形操作之生成图像验证码
    JZ046圆圈中最后剩下的数
    Python3连接MySQL
  • 原文地址:https://www.cnblogs.com/qdhxhz/p/9147326.html
Copyright © 2020-2023  润新知