zoukankan      html  css  js  c++  java
  • 003-基于URL的权限管理[不使用shiro]

    一、基于url权限管理流程【实现步骤】

      基于url拦截是企业中常用的权限管理方法,实现思路是:将系统操作的每个url配置在权限表中,将权限对应到角色,将角色分配给用户,用户访问系统功能通过Filter进行过虑,过虑器获取到用户访问的url,只要访问的url是用户分配角色中的url则放行继续访问。

    二、环境搭建以及核心代码

    https://github.com/bjlhx15/shiro.git

    1、数据库

    2、开发环境

    3、系统工程框架

    三、开发过程步骤

    1、系统登录

    主要功能:登录、记录用户session【包含用户信息,菜单信息,授权信息】

       1.1、系统登陆相当于用户身份认证,用户成功,要在session中记录用户的身份信息.

       1.2、操作流程:

             用户进行登陆页面

             输入用户名和密码进行登陆

             进行用户名和密码校验

                如果校验通过,在session记录用户身份信息

        根据用户信息记录菜单menus【用于功能菜单展示】,记录权限资源permissions【用户判断功能项】,一起放到session中

      1.3、激活用户基础类

    public class ActiveUser implements java.io.Serializable {
        private String userid;//用户id(主键)
        private String usercode;// 用户账号
        private String username;// 用户名称
    
        private List<SysPermission> menus;// 菜单
        private List<SysPermission> permissions;// 权限
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
    
        public String getUsercode() {
            return usercode;
        }
    
        public void setUsercode(String usercode) {
            this.usercode = usercode;
        }
    
        public String getUserid() {
            return userid;
        }
    
        public void setUserid(String userid) {
            this.userid = userid;
        }
    
        public List<SysPermission> getMenus() {
            return menus;
        }
    
        public void setMenus(List<SysPermission> menus) {
            this.menus = menus;
        }
    
        public List<SysPermission> getPermissions() {
            return permissions;
        }
    
        public void setPermissions(List<SysPermission> permissions) {
            this.permissions = permissions;
        }
    
        
    }
    View Code

      1.4、mapperr接口: 根据用户账号查询用户(sys_user)信息(使用逆向工程生成的mapper)

         使用逆向工程生成以下表的基础代码:

          

      1.5、service(进行用户名和密码校验)

        接口功能:根据用户的身份和密码 进行认证,如果认证通过,返回用户身份信息

        认证过程:

          根据用户身份(账号)查询数据库,如果查询不到用户不存在

          对输入的密码 和数据库密码 进行比对,如果一致,认证通过

        

      1.6、controller(记录session)    

        @RequestMapping("/login")
        public String login(HttpSession session, String randomcode,String usercode,String password)throws Exception{
            
            //校验验证码,防止恶性攻击
            //从session获取正确验证码
            String validateCode = (String) session.getAttribute("validateCode");
            
            //输入的验证和session中的验证进行对比 
            if(!randomcode.equals(validateCode)){
                //抛出异常
                throw new CustomException("验证码输入错误");
            }
            
            //调用service校验用户账号和密码的正确性
            ActiveUser activeUser = sysService.authenticat(usercode, password);
            
            //如果service校验通过,将用户身份记录到session
            session.setAttribute("activeUser", activeUser);
            //重定向到商品查询页面
            return "redirect:/first.action";
        }
    View Code

    2、用户认证拦截

    主要功能:放行匿名url、判断用户session是否存在

    2.1、配置匿名访问url【anonymousURL.properties】

      

    #配置逆向访问的url
    login.action=用户登陆

    2.2、编写认证拦截器

      用于用户认证校验、用户权限校验

    package com.lhx.ssm.controller.interceptor;
    
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import com.lhx.ssm.po.ActiveUser;
    import com.lhx.ssm.util.ResourcesUtil;
    
    /**
     * 
     * <p>Title: HandlerInterceptor1</p>
     * <p>Description: 用户身份认证拦截器</p>
     * @version 1.0
     */
    public class LoginInterceptor implements HandlerInterceptor {
    
        //在执行handler之前来执行的
        //用于用户认证校验、用户权限校验
        @Override
        public boolean preHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler) throws Exception {
            
            //得到请求的url
            String url = request.getRequestURI();
            
            //判断是否是公开 地址
            //实际开发中需要公开 地址配置在配置文件中
            //从配置中取逆名访问url
            
            List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
            //遍历公开 地址,如果是公开 地址则放行
            for(String open_url:open_urls){
                if(url.indexOf(open_url)>=0){
                    //如果是公开 地址则放行
                    return true;
                }
            }
            
            
            //判断用户身份在session中是否存在
            HttpSession session = request.getSession();
            ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
            //如果用户身份在session中存在放行
            if(activeUser!=null){
                return true;
            }
            //执行到这里拦截,跳转到登陆页面,用户进行身份认证
            request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
            
            //如果返回false表示拦截不继续执行handler,如果返回true表示放行
            return false;
        }
        //在执行handler返回modelAndView之前来执行
        //如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
        @Override
        public void postHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println("HandlerInterceptor1...postHandle");
            
        }
        //执行handler之后执行此方法
        //作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
        //实现 系统 统一日志记录
        @Override
        public void afterCompletion(HttpServletRequest request,
                HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("HandlerInterceptor1...afterCompletion");
        }
    
    }
    View Code

    2.3、配置拦截器

      在springmvc.xml中配置拦截器:  

        <!--拦截器 -->
        <mvc:interceptors>
            <mvc:interceptor>
                <!-- 用户认证拦截 -->
                <mvc:mapping path="/**" />
                <bean class="com.lhx.ssm.controller.interceptor.LoginInterceptor"></bean>
            </mvc:interceptor>
        </mvc:interceptors>

    3、授权

    主要功能:放行匿名url、放行公共、获取用户session,授权资源判断

    3.1、公共访问地址【commonURL.properties】

      在此配置文件配置公用访问地址,公用访问地址只要通过用户认证,不需要对公用访问地址分配权限即可访问。

    #配置公用的访问地址
    first.action=系统首页
    logout.action=退出

    3.2、获取用户权限范围菜单

      思路:在用户认证时,认证通过,根据用户id从数据库获取用户权限范围的菜单,将菜单的集合存储在session中。

      po在三中的1.3已添加menus等属性

      mapper xml

        <!-- 根据用户id查询菜单 -->
        <select id="findMenuListByUserId" parameterType="string"
            resultType="com.lhx.ssm.po.SysPermission">
            SELECT
            *
            FROM
            sys_permission
            WHERE TYPE = 'menu'
            AND id IN
            (SELECT
            sys_permission_id
            FROM
            sys_role_permission
            WHERE sys_role_id IN
            (SELECT
            sys_role_id
            FROM
            sys_user_role
            WHERE sys_user_id = #{id}))
        </select>
    View Code

      mapper接口

        //根据用户id查询菜单
        public List<SysPermission> findMenuListByUserId(String userid)throws Exception;

      service接口:根据用户id查询用户权限的菜单

        @Override
        public List<SysPermission> findMenuListByUserId(String userid)
                throws Exception {        
            return sysPermissionMapperCustom.findMenuListByUserId(userid);
        }

    3.3、获取用户权限范围的url

      思路:在用户认证时,认证通过,根据用户id从数据库获取用户权限范围的url,将url的集合存储在session中。
      po在三中的1.3已添加permissions等属性

      mapper xml

        <!-- 根据用户id查询url -->
        <select id="findPermissionListByUserId" parameterType="string"
            resultType="com.lhx.ssm.po.SysPermission">
            SELECT
            *
            FROM
            sys_permission
            WHERE TYPE = 'permission'
            AND id IN
            (SELECT
            sys_permission_id
            FROM
            sys_role_permission
            WHERE sys_role_id IN
            (SELECT
            sys_role_id
            FROM
            sys_user_role
            WHERE sys_user_id = #{id}))
        </select>
    View Code

      mapper接口

    //根据用户id查询权限url
    public List<SysPermission> findPermissionListByUserId(String userid)throws Exception;

      service接口:根据用户id查询用户权限的菜单

        @Override
        public List<SysPermission> findPermissionListByUserId(String userid)
                throws Exception {        
            return sysPermissionMapperCustom.findPermissionListByUserId(userid);
        }

    3.4、用户认证通过后取出菜单和url放入ActiveUser,进而放入session

    这里是三中的1.5 完善实现

    package com.lhx.ssm.service.impl;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.lhx.ssm.exception.CustomException;
    import com.lhx.ssm.mapper.SysPermissionMapperCustom;
    import com.lhx.ssm.mapper.SysUserMapper;
    import com.lhx.ssm.po.ActiveUser;
    import com.lhx.ssm.po.SysPermission;
    import com.lhx.ssm.po.SysUser;
    import com.lhx.ssm.po.SysUserExample;
    import com.lhx.ssm.service.SysService;
    import com.lhx.ssm.util.MD5;
    
    /**
     * 
     * <p>Title: SysServiceImpl</p>
     * <p>Description:认证和授权的服务接口 </p>
     * @version 1.0
     */
    public class SysServiceImpl implements SysService {
        
        @Autowired
        private SysUserMapper sysUserMapper;
        
        @Autowired
        private SysPermissionMapperCustom sysPermissionMapperCustom;
    
        @Override
        public ActiveUser authenticat(String userCode, String password)
                throws Exception {
            /**
        认证过程:
        根据用户身份(账号)查询数据库,如果查询不到用户不存在
        对输入的密码 和数据库密码 进行比对,如果一致,认证通过
             */
            //根据用户账号查询数据库
            SysUser sysUser = this.findSysUserByUserCode(userCode);
            
            if(sysUser == null){
                //抛出异常
                throw new CustomException("用户账号不存在");
            }
            
            //数据库密码 (md5密码 )
            String password_db = sysUser.getPassword();
            
            //对输入的密码 和数据库密码 进行比对,如果一致,认证通过
            //对页面输入的密码 进行md5加密 
            String password_input_md5 = new MD5().getMD5ofStr(password);
            if(!password_input_md5.equalsIgnoreCase(password_db)){
                //抛出异常
                throw new CustomException("用户名或密码 错误");
            }
            //得到用户id
            String userid = sysUser.getId();
            //根据用户id查询菜单 
            List<SysPermission> menus =this.findMenuListByUserId(userid);
            
            //根据用户id查询权限url
            List<SysPermission> permissions = this.findPermissionListByUserId(userid);
            
            //认证通过,返回用户身份信息
            ActiveUser activeUser = new ActiveUser();
            activeUser.setUserid(sysUser.getId());
            activeUser.setUsercode(userCode);
            activeUser.setUsername(sysUser.getUsername());//用户名称
            
            //放入权限范围的菜单和url
            activeUser.setMenus(menus);
            activeUser.setPermissions(permissions);
            
            return activeUser;
        }
        
        //根据用户账号查询用户信息
        public SysUser findSysUserByUserCode(String userCode)throws Exception{
            SysUserExample sysUserExample = new SysUserExample();
            SysUserExample.Criteria criteria = sysUserExample.createCriteria();
            criteria.andUsercodeEqualTo(userCode);
            
            List<SysUser> list = sysUserMapper.selectByExample(sysUserExample);
            
            if(list!=null && list.size()==1){
                return list.get(0);
            }    
            
            return null;
        }
    
        @Override
        public List<SysPermission> findMenuListByUserId(String userid)
                throws Exception {        
            return sysPermissionMapperCustom.findMenuListByUserId(userid);
        }
    
        @Override
        public List<SysPermission> findPermissionListByUserId(String userid)
                throws Exception {
            
            return sysPermissionMapperCustom.findPermissionListByUserId(userid);
        }
    
    }
    View Code

    3.5、菜单的动态显示需要前端配合

    3.6、完整的授权拦截器代码【PermissionInterceptor】

    package com.lhx.ssm.controller.interceptor;
    
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import com.lhx.ssm.po.ActiveUser;
    import com.lhx.ssm.po.SysPermission;
    import com.lhx.ssm.util.ResourcesUtil;
    
    /**
     * 
     * <p>Title: PermissionInterceptor</p>
     * <p>Description: 授权拦截器</p>
     * @version 1.0
     */
    public class PermissionInterceptor implements HandlerInterceptor {
    
        //在执行handler之前来执行的
        //用于用户认证校验、用户权限校验
        @Override
        public boolean preHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler) throws Exception {
            
            //得到请求的url
            String url = request.getRequestURI();
            
            //判断是否是公开 地址
            //实际开发中需要公开 地址配置在配置文件中
            
            //1.从配置中取逆名访问url        
            List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
            //遍历公开 地址,如果是公开 地址则放行
            for(String open_url:open_urls){
                if(url.indexOf(open_url)>=0){
                    //如果是公开 地址则放行
                    return true;
                }
            }
            
            //2.从配置文件中获取公共访问地址
            List<String> common_urls = ResourcesUtil.gekeyList("commonURL");
            //遍历公用 地址,如果是公用 地址则放行
            for(String common_url:common_urls){
                if(url.indexOf(common_url)>=0){
                    //如果是公开 地址则放行
                    return true;
                }
            }
            
            //3.获取session
            HttpSession session = request.getSession();
            ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
            //从session中取权限范围的url
            List<SysPermission> permissions = activeUser.getPermissions();
            for(SysPermission sysPermission:permissions){
                //权限的url
                String permission_url = sysPermission.getUrl();
                if(url.indexOf(permission_url)>=0){
                    //如果是权限的url 地址则放行
                    return true;
                }
            }
            
            //4.执行到这里拦截,跳转到无权访问的提示页面
            request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response);
            
            //如果返回false表示拦截不继续执行handler,如果返回true表示放行
            return false;
        }
        //在执行handler返回modelAndView之前来执行
        //如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手
        @Override
        public void postHandle(HttpServletRequest request,
                HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println("HandlerInterceptor1...postHandle");
            
        }
        //执行handler之后执行此方法
        //作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
        //实现 系统 统一日志记录
        @Override
        public void afterCompletion(HttpServletRequest request,
                HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("HandlerInterceptor1...afterCompletion");
        }
    
    }
    View Code

    3.7、配置授权拦截器

    注意:将授权拦截器配置在用户认证拦截的下边。

        <!--拦截器 -->
        <mvc:interceptors>
            <mvc:interceptor>
                <!-- 用户认证拦截 -->
                <mvc:mapping path="/**" />
                <bean class="com.lhx.ssm.controller.interceptor.LoginInterceptor"></bean>
            </mvc:interceptor>
            <mvc:interceptor>
                <!-- 授权拦截 -->
                <mvc:mapping path="/**" />
                <bean class="com.lhx.ssm.controller.interceptor.PermissionInterceptor"></bean>
            </mvc:interceptor>
        </mvc:interceptors>
    View Code

    四、小结

      使用基于url拦截的权限管理方式,实现起来比较简单,不依赖框架,使用web提供filter就可以实现。

    问题:

      需要将所有的url全部配置起来,有些繁琐,不易维护,url(资源)和权限表示方式不规范。

      如果漏配置,就会无权访问。

       URL也是一种资源

  • 相关阅读:
    AngularJS之页面跳转Route
    ajax上传图片的本质
    JQuery-基础学习1
    Java跨域问题的处理详解
    红黑树
    查找算法
    八大排序算法
    linux下安装nginx
    libevent 和 libev 提高网络应用性能
    Linux下libevent安装与示例
  • 原文地址:https://www.cnblogs.com/bjlhx/p/7118917.html
Copyright © 2011-2022 走看看