java

一文看懂基于角色的访问控制

2021-05-20  本文已影响0人  sknfie

概述

基于角色的访问控制采用的底层框架:spring3.2.9(mvc) + mybatis3.2.7 + mysql5.5 。

架构设计

RBAC:用户-角色-权限【权限模块-权限组-权限项】

通用

后台权限管理

后台用户管理:1000100:userPermission/userMain
后台角色管理:1000200:userPermission/roleMain
后台菜单管理:1000300:userPermission/menuMain

官网

文章管理

文章菜单管理:1000400:article/articleMenuMain
文章内容管理:1000500:article/articleMain

一面墙管理

一面墙内容管理:1000600:wall/wallMain

内嵌

1、文章菜单:模块 -> 组 -> 文章
2、一面墙:jsonp爬虫

线上线下,差异配置文件

    1、jdbc.properties                   :数据库配置
    2、freemarker.variables.properties   :跟地址配置
    3、config.json                       :ueditor文件上传地址配置
    4、ueditor.properties                :ueditor文件上传参数配置
    5、memcached.properties              :缓存配置

JS插件引用

    001:jquery-easyui-1.4:未做修改
    002:ueditor1_4_3 (上传本项目目录下):
        01:引入ueditor的JAR:ueditor-1.1.1.jar
        02:修改:config.json:常规配置,文件上传/管理路径:修改*UrlPrefix("/5i-admin"或项目域名)和*PathFormat("/uploadfiles/im..."); 
        03:修复bug:controller.jsp:添加部分代码,解决插件自身获取绝对路径bug;
        04:修复bug:ueditor.all.min.js:解决弹框层级冲突:zIndex:999改为zIndex:99999,解决与easyui的遮罩9000冲突;   
    003:七牛云存储 (整合ueditor) :
        01:引入七牛的JAR:qiniu-java-sdk.Jar;
        02:修改:修改ueditor的JAR/加新JAR配置文件:ueditor-1.1.1-qiniu-xxl.jar/ueditor.properties (保存文件时,优先判断保存七牛云)
        03:修改:config.json:修改*UrlPrefix地址:http://7xir7k.com1.z0.glb.clouddn.com/@;

RBAC:基于角色的访问控制

基于角色的访问控制(Role-Based Access Control):

系统元素可分为:

需满足:

RBAC角色访问控制,建表SQL脚本

先建立数据库:

CREATE DATABASE IF NOT EXISTS db_permission default charset utf8 COLLATE utf8_general_ci; 

Tips-五张核心表 :权限表、角色表、用户表、角色权限关联表、用户角色关联表;

CREATE TABLE `admin_menu` (
  `menu_id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) NOT NULL,
  `order` int(11) NOT NULL,
  `name` varchar(50) NOT NULL,
  `url` varchar(50) DEFAULT NULL,
  `permession_num` int(11) DEFAULT NULL,
  PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;

CREATE TABLE `admin_role` (
  `role_id` int(11) NOT NULL AUTO_INCREMENT,
  `order` int(11) NOT NULL,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

CREATE TABLE `admin_role_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) NOT NULL,
  `menu_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;

CREATE TABLE `admin_user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(50) NOT NULL,
  `password` varchar(50) NOT NULL,
  `user_token` varchar(50) DEFAULT NULL,
  `real_name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`user_id`),
  UNIQUE KEY `user_name_unique` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=5014 DEFAULT CHARSET=utf8;

CREATE TABLE `admin_user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

Annotation + 拦截器(Spring MVC),实现权限过滤

1、用户根据角色进行登陆,登陆系统设计可参考“SKILL-用户登陆系统”;
2、根据用户登陆的角色,获取“权限列表”,存放在用户登陆信息缓存LoginIdentity中;
3、用户操作进行拦截器拦截,获取对应功能的权限ID,校验登陆用户是否拥有该权限;

权限注解

package com.xxl.controller.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 权限类型
 * @author xuxueli
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermessionType {
    
    /**
     * 登陆拦截 (默认拦截)
     */
    boolean loginState() default true;
    
    /**
     * 权限编号 (默认不拦截)
     */
    int permessionNum() default 0;
    
    /**
     * 随机验证码拦截 (默认不拦截)
     */
    boolean randCode() default false;
}

为Controller方法,加权限注解

@RequestMapping("/articleMenuQuery")
@ResponseBody
@PermessionType(permessionNum = 1000400)
public List<ArticleMenu> articleMenuQuery() {
    return articleService.articleMenuQuery();
}

在springmvc-context.xml配置中,配置权限拦截器

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.xxl.controller.interceptor.PermissionInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

权限拦截器,源码

package com.xxl.controller.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.google.code.kaptcha.Constants;
import com.xxl.controller.annotation.PermessionType;
import com.xxl.controller.core.LoginIdentity;
import com.xxl.core.constant.CommonDic.HttpSessionKeyDic;
import com.xxl.core.constant.CommonDic.ReturnCodeEnum;
import com.xxl.core.exception.WebException;
import com.xxl.core.util.HttpSessionUtil;

/**
 * “登陆+权限”拦截器
 * @author xuxueli
 */
public class PermissionInterceptor extends HandlerInterceptorAdapter {
    //private static transient Logger logger = LoggerFactory.getLogger(LoginPermissionInterceptor.class);
    
    /*
     * Controller“执行时异步”“执行
     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#afterConcurrentHandlingStarted(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
     */
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        // TODO Auto-generated method stub
        super.afterConcurrentHandlingStarted(request, response, handler);
    }

    /*
     * Controller“执行后”执行
     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#postHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, org.springframework.web.servlet.ModelAndView)
     */
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        super.postHandle(request, response, handler, modelAndView);
    }

    /*
     * Controller“执行前”执行
     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        
        // 请求非法
        if (!(handler instanceof HandlerMethod)) {
            return super.preHandle(request, response, handler);
        }
        
        // 获取“权限注解”
        HandlerMethod method = (HandlerMethod)handler;
        PermessionType permission = method.getMethodAnnotation(PermessionType.class);
        if (permission == null) {
            throw new WebException(ReturnCodeEnum.FAIL.code(), "权限受限");
        }
        boolean loginState = permission.loginState();
        int permessionNum = permission.permessionNum();
        boolean randCode = permission.randCode();
        
        LoginIdentity identity = (LoginIdentity) HttpSessionUtil.get(session, HttpSessionKeyDic.LOGIN_IDENTITY);
        // 01:登陆拦截
        if (loginState) {
            if (identity == null) {
                //response.sendRedirect(request.getContextPath() + "/");
                //return false;
                throw new WebException(ReturnCodeEnum.FAIL.code(), "登录失效,请重新登录");
            }
        }
        // 02:权限拦截器
        if (permessionNum > 0) {
            if (identity == null || identity.getMenePermissionNums() == null || 
                    !identity.getMenePermissionNums().contains(String.valueOf(permessionNum))) {
                throw new WebException(ReturnCodeEnum.FAIL.code(), "权限受限");
            }
        }
        
        // 03:随机验证码拦截
        if (randCode) {
            String randCodeCache = (String) HttpSessionUtil.get(session, Constants.KAPTCHA_SESSION_KEY);
            String randCodeParam = request.getParameter("randCode");
            if (StringUtils.isBlank(randCodeCache) || !StringUtils.equals(randCodeCache, randCodeParam)) {
                throw new WebException(ReturnCodeEnum.FAIL.code(), "验证码错误");
            }
        }
        
        return super.preHandle(request, response, handler);
    }
    
}
上一篇 下一篇

猜你喜欢

热点阅读