java项目权限控制的理解和示例(基于shiro和传统拦截器filter两种方式)

1.概念

  • 个人理解,权限就是做到对不同用户进行访问限制,前提是保证在权限需求设计范围内不会出现非法也能访问到不该访问到的东西.因此按数据表设计将权限分为:部门,用户,角色,角色权限中间表,权限.(个人觉得一般项目可以考虑部门和角色合为一个),这里用一个具体访问的url地址代表一个权限(也可以使用别的方式).下面用一个例子通过来分析.

2.场景

  • 一个超级管理员admin
    • 可以访问并且操作网站所有功能
  • 经理manager
    • 比超级管理员权限少一点
  • 销售员soler和采购员buy
    • 只能使用部分功能,比经理权限少
  • 普通用户customer
    • 能使用的功能更少

针对以上来做各种变通分析权限控制的需求

3.需求

  • 表设计:

    • 用户表t_user
    • 角色表t_role(有父子关系)
    • 权限表t_auth(有父子关系)
    • 角色和权限中间表t_role_auth
  • 使用技术

    • 拦截器来处理
    • shiro框架处理
  • 表的使用

    • 不管是什么身份的用户,都是通过角色来分配权限,也就是用户选择了角色就相当于选中了应有的权限,禁止用户对t_auth表直接关联
    • 权限表中是一条信息对应一个访问地址,并设置上下级关系
  • 对不同用户分配角色

    • 超级管理员:
    • 这是一个特例,一个项目就一个,可以通过代码来控制,不过为了便于维护,在角色表添加一个叫admin的角色,然后在这个角色下面添加所有的访问地址
    • 经理:
    • 在角色表里面创建一个叫manager的经理角色,再对这个角色添加对应权限
    • 销售员soler和采购员buy
    • 在角色表里面分别创建一个soler和buy角色,并对两个角色添加对应权限
    • 普通用户customer
    • 在角色表中创建一个customer角色,并添加对应权限
  • 相关问题

    1. 要是一个销售员除了有soler角色外还有一些其他权限怎么办呢?比如销售员还有部分采购员一部分权限

      • 这种情况可以采用量两种方式解决这个问题:

        1. 创建一个新角色(比如soler2)来包含原本soler角色里面的所有权限和新拥有的权限

        2. 创建一个新角色,仅包含新拥有权限即可,然后设置该新角色的父级为soler角色,形成从属关系

    2. 还有没有其他特殊情况没考虑进来的吗?

      • 个人觉得在一个超级管理员情况下,在上面设计里面变通几乎能满足所有常规的中小型项目的权限设计方案.

4.相关技术实现(在spring+springMVC框架下maven项目)

  • 使用拦截器实现

    1. 在spring配置文件中添加拦截器配置

      
      <mvc:interceptors>
          <mvc:interceptor>
              <mvc:mapping path="/**"/>
              <bean id="li" class="cn.util.LoginIntercepter" >bean>
          mvc:interceptor>
      mvc:interceptors>

    2.拦截器处理权限类

    • 继承HandlerInterceptorAdapter类
    • HandlerInterceptorAdapter类的preHandle()在执行controller对应访问方法前执行,也就是在这个方法里面做权限判断,返回false后权限验证失败,返回true的话进入controller对应的访问方法
    • HandlerInterceptorAdapter类的postHandle()在controller对应访问方法后执行
    public class LoginIntercepter extends HandlerInterceptorAdapter {
    private static String [] urls = {"uc/checklogin"};
    public static boolean checkUrl(String requestname){
        //筛选静态资源
        if(requestname==null||requestname.equals("")){
            return true;
        }   if(requestname.endsWith(".js")||requestname.endsWith(".css")||requestname.endsWith(".jpg")){
            return true;
        }
        for (int i = 0; i < urls.length; i++) {
            if(urls[i].equals(requestname)){
                return true;
            }
        }
        return false;
    }
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        //1.登录拦截
        //2.权限验证,验证当前用户是否拥有当前请求这个权限
        HttpSession session = request.getSession();
        //获取当前请求的名字
        String uri = request.getRequestURI();
        String contextpath = request.getContextPath();
        String requestname = uri.substring(contextpath.length()+1, uri.length());
        //动态获取项目的网络地址
        String basepath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+contextpath+"/";
        if(checkUrl(requestname)){
            return true;
        }
        //验证用户是否登录
        Object obj = session.getAttribute("users");
        if(obj==null){
            response.sendRedirect(basepath+"index.jsp");
            return false;
        }
        //某些请求(比如修改自己的基本信息,退出登录...),是只要求登录就可以访问,而没有纳入权限管理范围的
        //对于这部分请求,应该只做登录验证,而不进入权限验证范围
        ServletContext application = session.getServletContext();
        List allurl = (List) application.getAttribute("allurl");
        if(!allurl.contains(requestname)){
            return true;
        }
        //如果程序执行到这里,用户已经登录,并且当前请求不是请求的静态资源
        //所以这里需要开始权限判断,判断当前用户是否拥有当前请求的权限
        Users users = (Users) obj;
        if(users.getRole()==null){
            response.sendRedirect(basepath+"nopriviliage.jsp");
            return false;
        }
        //得到当前用户所拥有的的所有权限信息
        List prilist = users.getRole().getPrilist();
        if(!checkpri(prilist, requestname)){
            response.sendRedirect(basepath+"nopriviliage.jsp");
            return false;
        }
        return true;
    }
    public static boolean checkpri(List prilist,String requestname){
        for (int i = 0; i < prilist.size(); i++) {
            Priviliage pri = prilist.get(i);
            if(pri.getPriUrl()!=null&&pri.getPriUrl().equals(requestname)){
                return true;
            }
        }
        return false;
    }
    
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
    }
  • 使用shiro安全框架实现

    1. 导入shiro支持包jar包

      
       <dependency>
         <groupId>org.apache.shirogroupId>
         <artifactId>shiro-coreartifactId>
         <version>1.2.5version>
       dependency>
       
       <dependency>
         <groupId>org.apache.shirogroupId>
         <artifactId>shiro-webartifactId>
         <version>1.2.5version>
       dependency>
       
       <dependency>
         <groupId>org.apache.shirogroupId>
         <artifactId>shiro-springartifactId>
         <version>1.2.5version>
       dependency>
       
       <dependency>
         <groupId>commons-logginggroupId>
         <artifactId>commons-loggingartifactId>
         <version>1.2version>
       dependency>
       <dependency>
         <groupId>log4jgroupId>
         <artifactId>log4jartifactId>
         <version>${log4j.version}version>
       dependency>
       <dependency>
         <groupId>org.slf4jgroupId>
         <artifactId>slf4j-apiartifactId>
         <version>${slf4j.version}version>
       dependency>
       <dependency>
         <groupId>org.slf4jgroupId>
         <artifactId>slf4j-log4j12artifactId>
         <version>${slf4j.version}version>
       dependency>

    2.在web.xml下配置过滤器

    
    <filter>
        <filter-name>shiroFilterfilter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
        
        <init-param>
            <param-name>targetFilterLifecycleparam-name>
            <param-value>trueparam-value>
        init-param>
        
        <init-param>
            <param-name>targetBeanNameparam-name>
            <param-value>shiroFilterparam-value>
        init-param>
    filter>
    <filter-mapping>
        <filter-name>shiroFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>

    3.在spring配置文件中配置shiro支持(这里使用自定义realm)

    
    
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        
        <property name="loginUrl" value="/login.action" />
        
        <property name="successUrl" value="/first.action"/>
        <property name="unauthorizedUrl" value="/refuse.jsp" />
        
        <property name="filterChainDefinitions">
            <value>
                
                /images/**=anon
                /js/**=anon
                /style/**=anon
    
                
                /**=anon
    
            value>
        property>
    bean>
    
    
    
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm" />
    bean>
    
    
    <bean id="myRealm" class="com.lumingshan.web.utils.MyRealm">
    bean>

    4.自定义realm类

    • 这里是先执行下面的doGetAuthenticationInfo()方法,再执行doGetAuthorizationInfo()方法
    • 密码不需要手动验证,shiro帮忙验证,按下面对应传参即可
    • 这里的验证分为角色验证,权限验证,从数据库查询角色和权限分别装在一个set集合中,在doGetAuthorizationInfo()方法中的调用authorizationInfo.setRoles()和authorizationInfo.setStringPermissions()把seet集合放进去,然后shiro会与上面spring配置文件的filterChainDefinitions属性配置文件对应比较权限是否符合
    public class MyRealm extends AuthorizingRealm {
    
      @Resource(name = "roleServiceImpl")
      private RoleService roleServiceImpl;
      @Resource(name = "userServiceImpl")
      private UserService userServiceImpl;
    
      @Resource(name = "authServiceImpl")
      private AuthService authServiceImpl;
    
      // 为当前登陆成功的用户授予权限和角色,已经登陆成功了
      @Override
      protected AuthorizationInfo doGetAuthorizationInfo(
              PrincipalCollection principals) {
          String username = (String) principals.getPrimaryPrincipal(); //获取用户名
          SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
          User loginUser = new User();
          loginUser.setUsername(username);
          User user = userServiceImpl.login(loginUser);
          Role role = roleServiceImpl.getRoleByRoleId(user.getRole().getRoleId());
          //装成set集合
          HashSet roleSet = new HashSet<>();
          roleSet.add(role.getEnName());
          authorizationInfo.setRoles(roleSet);
    
          List authList = authServiceImpl.getAuthByRoleId(role.getRoleId());
          HashSet authSet = new HashSet<>();
          for (Auth auth : authList) {
              authSet.add(auth.getEnName());
          }
    
          authorizationInfo.setStringPermissions(authSet);
          return authorizationInfo;
      }
    
      // 验证当前登录的用户,获取认证信息
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(
              AuthenticationToken token) throws AuthenticationException {
          String username = (String) token.getPrincipal(); // 获取用户名
          User loginUsers = new User();
          loginUsers.setUsername(username);
          User user = userServiceImpl.login(loginUsers);
          if(user != null) {
              //此步验证密码
              AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getUserpwd(), "myRealm");
              return authcInfo;
          } else {
              return null;
          }
      }
    }

    5.controller调用realm

    @Controller
    @RequestMapping("user")
    public class UserController {
      @RequestMapping("login")
      @ResponseBody
      public Object login(@RequestBody User user,HttpServletRequest request) {
          Subject subject = SecurityUtils.getSubject();
          UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getUserpwd());
          try{
              subject.login(token);//跳到自定义的realm中
              request.getSession().setAttribute("user", user);
              return new Result<>(null);
          }catch(Exception e){
              e.printStackTrace();
              return new Result<>(0,null);
          }
      }
    }

你可能感兴趣的:(java项目)