003-基于URL的权限管理[不使用shiro]

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

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

003-基于URL的权限管理[不使用shiro]

 

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

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

1、数据库

2、开发环境

3、系统工程框架

三、开发过程步骤

1、系统登录

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

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

   1.2、操作流程:

         用户进行登陆页面

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

         进行用户名和密码校验

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

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

  1.3、**用户基础类

003-基于URL的权限管理[不使用shiro]003-基于URL的权限管理[不使用shiro]
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)

     使用****生成以下表的基础代码:

      003-基于URL的权限管理[不使用shiro]

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

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

    认证过程:

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

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

    003-基于URL的权限管理[不使用shiro]

  1.6、controller(记录session)    

003-基于URL的权限管理[不使用shiro]003-基于URL的权限管理[不使用shiro]
    @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】

  003-基于URL的权限管理[不使用shiro]

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

2.2、编写认证拦截器

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

003-基于URL的权限管理[不使用shiro]003-基于URL的权限管理[不使用shiro]
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

003-基于URL的权限管理[不使用shiro]003-基于URL的权限管理[不使用shiro]
    <!-- 根据用户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

003-基于URL的权限管理[不使用shiro]003-基于URL的权限管理[不使用shiro]
    <!-- 根据用户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 完善实现

003-基于URL的权限管理[不使用shiro]003-基于URL的权限管理[不使用shiro]
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】

003-基于URL的权限管理[不使用shiro]003-基于URL的权限管理[不使用shiro]
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、配置授权拦截器

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

003-基于URL的权限管理[不使用shiro]003-基于URL的权限管理[不使用shiro]
    <!--拦截器 -->
    <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也是一种资源