关于细粒度控制接口权限的设计思路

前言:

目前安全问题越来越重要,以前只需要前端权限控制,目前前端的权限控制已经无法满足当前的安全需要了,一个更细粒度的控制,也就是接口粒度的控制变得越来越重要。

思路:

权限控制还是传统的RBAC(Role-Based Access Control: 基于角色访问控制)思想,添加了给角色绑定某个菜单的某些行为的能力。
用户,角色,菜单,行为
用户和角色绑定。
角色决定 有哪些菜单的哪些行为。
比如。 拥有某个菜单的行为( 添加、删除、修改、导出、导入、打印)
每个菜单在设计时,必须指定一个id,如xxxx_A,菜单定义为常量。行为定义为枚举类,权限由菜单和行为构成。
每个接口,可以有多个权限。也就是可以归属多个菜单的多个行为。

具体操作:

菜单常量

public interface MenuConstant {
    String XXXX_XXX_A = "XXXX_XXX_A";
    String XXXX_XXX_B = "XXXX_XXX_B";
    String XXXX_XXX_C = "XXXX_XXX_C";
}

行为枚举

public enum ActionEnum {
    QUERY,
    ADD,
    UPDATE,
    DELETE,
    EXPORT,
    IMPORT,
    PRINT;
}

功能注解

public @interface Permission{
    String menu();
    ActionEnum[] actions();
}

认证注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuth {
    Permission[] permissions();
}

使用举例

@RestController
@RequestMapping("/test")
public class Controller {
    @PreAuth(permissions= {@Permission(menu = MenuConstant.XXXX_XXX_A, actions = {ActionEnum.ADD, ActionEnum.IMPORT})})
    @RequestMapping("/methodA")
    public void methodA() {
    }
}

切面处理

public class UnPassPreAuthException extends Exception {
    public UnPassPreAuthException() {
        super();
    }
    public UnPassPreAuthException(String message) {
        super(message);
    }
}
@Aspect
@Component
public class PreAuthAspect {

    @Pointcut("@annotation(com.luoq.auth.annotion.PreAuth)")
    public void preAuthPointcut() {
    }

    @Before(value = "preAuthPointcut()")
    public void aspectBefore(JoinPoint joinPoint) throws UnPassPreAuthException{
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        PreAuth preAuth = method.getAnnotation(PreAuth.class);
        if (preAuth != null) {
            Permission[] permissions= preAuth.permissions();
            boolean hasAuth = validateHasAuth(permissions);
            if (!hasAuth) {
                String methodInfo = getMethodInfo(method);
                YnLogger.error("PreAuth > {}, Permissions: {}, Not Pass PreAuth!", methodInfo, permissions);
                throw new UnPassPreAuthException(methodInfo + " is not passing preAuth !");
            }
    }
    
    private boolean validateHasAuth(Permission[] permissions){
        // todo 验证当前用户是否有权限
        return false;
    }
    
    private String getMethodInfo(Method method) {
        String[] attr = method.toString().split(" ");
        return attr[attr.length - 1];
    }
}

下面的处理很灵活,根据自己系统的框架进行处理,可以是filter,可以是spring的统一异常处理。

public class PreAuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (Exception e) {
            Throwable causeOne = e.getCause();
            Throwable cause = null;
            if (causeOne instanceof UnPassPreAuthException) {
                cause = causeOne;
            } else if (causeOne.getCause() instanceof UnPassPreAuthException) {
                cause = causeOne.getCause();
            }
            if (cause != null) {
                log.error("403 Access Forbidden: {}", cause.getMessage(), cause);
                HttpServletResponse response = (HttpServletResponse) servletResponse;
                response.setStatus(403);
                response.getOutputStream().write(("403 Access Forbidden!").getBytes(StandardCharsets.UTF_8));
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值