SpringBoot使用拦截器实现Restful URL权限拦截

本文介绍了如何在SpringMVC中利用内置拦截器实现基于角色的访问控制(RBAC),涉及关键点包括拦截器的使用、正则表达式、权限模型设计以及如何通过用户角色匹配URL权限。适合快速上手的轻量级权限管理方案。
摘要由CSDN通过智能技术生成

前言

RBAC 是基于角色的访问控制(Role-Based Access Control )。权限管理是在任何一个系统里都无法绕过的问题。没有权限控制管理的系统是非常不安全的。
在Spring框架体系中。有非常多的RBAC框架,如Spring Shiro、Spring Security。
而本文介绍的是使用SpringMVC自带的拦截器来实现RBAC权限控制。功能虽然不够全面,但胜在够轻量,能够实现最基本的权限拦截。

实现的关键点:
1.拦截器的使用
2.正则表达式的使用,反向生成权限URL的正则表达式。
3.建立正确的RBAC权限模型

权限数据模型

本文教程使用通用的RBAC权限模型:
用户、角色、权限、用户-角色、角色-权限。如下图所示:
在这里插入图片描述

关于RBAC权限模型的更多资料,推荐几个链接:
权限系统与RBAC模型概述[绝对经典]
http://csrc.nist.gov/groups/SNS/rbac/index.html
https://csrc.nist.gov/projects/role-based-access-control

拦截器的实现

这里记录几个要点:

1.拦截器继承自:HandlerInterceptorAdapter
2.拦截器需要加上@Component注解,为了被Ioc容器扫描到
3.拦截器要对不鉴权的URL进行放行
4.给出的代码并非完整代码,只记录了大概的思路。

方法伪代码:

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import lombok.extern.slf4j.Slf4j;

/**
* @author ljx
*/
@Slf4j
@Component
public class ValidInterceptor extends HandlerInterceptorAdapter {
 	//公开的URL前缀  
    private static final String[] PUBLIC_URL_PREFIXES = new String[]{
            "/upload",
            "/error",
            "/web",
    };
    // 公开的URL前缀中要过滤的接口,即公开前缀中需要鉴权的URL
    private static final String[] PUBLIC_URLS_EXCLUDES = new String[]{};
    private static final String HEADER_AUTHORIZATION = "Authorization";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	// 对 Options 方法放行
        if (HttpMethod.OPTIONS.matches(request.getMethod())) {
            return true;
        }
        String method = request.getMethod();
        String uri = request.getRequestURI();
        if (StringKit.isNotBlank(uri)) {
	        // uri的一些处理
            uri = uri.replace("/tomcat", "");
        }
        // 不拦截公开的URL
        if (isPublicUrl(uri)) {
            return true;
        }

        // 从请求中获取访问者信息,通常为用户信息
        // 可以获取Session或者token来识别信息,如果没有用户信息则可以判定为无权限,返回false
        // 用户信息通常保存到一个全局的地方。可以是全局缓存(Redis或者内存缓存中)或者数据库中
        // 代码略

        // RBAC校验
        // 由获取到的用户信息,去查询用户拥有的角色,再根据角色去获取对应的权限信息,
        // 然后对URL和权限列表进行匹配。
        // 这里每一次都会去数据库查询权限数据,好处是可以动态改变权限,不好的地方是会频繁查库。
        // 优化方法:
        // 1.拦截器第一次对某进行拦截时,将该用户的权限信息放到缓存,后面可以从缓存中直接取数据。(无法动态改变权限信息)
        // 2.没想到,读者自行补充(滑稽脸)
        Set<String> userRoles;	// 用户的角色ID
        Set<String> roleItems;	// Set保证权限ID唯一
        List<PermissionEntity> permissions;	// 由权限ID集合获取到所有的权限具体信息(不包含菜单类型的URL)
        // Permission是权限具体信息。必须包含权限的URL,对应的Http方法。
        // hasPermission方法是对Restful URL进行鉴权的关键方法
        if (!hasPermission(uri, method, permissions)) {
            throw new RuntimeException("access.denied");
        }
        return super.preHandle(request, response, handler);
    }

    private boolean hasPermission(String url, String method, List<PermissionEntity> permissions) {
        if (ArrayKit.isNullOrEmpty(permissions)) {
            return false;
        }
        for (PermissionEntity permission : permissions) {
        	// getItem() 返回的是权限URL
            if (StringKit.isBlank(permission.getItem())) {  
                continue;
            }
            // 将Restful风格的url占位符部分改成正则表达式,去匹配url。
            // 例如/users/{uid}/role的正则表达式为:/users/[^/]*/role
            // url和method都匹配成功视为有权限
            String matchRegex = permission.getItem().replaceAll("\\{[^/]*\\}", "[^/]*");
            if (url.matches(matchRegex)) {
                // URL的请求方式为*则视为支持所有请求方式
                if ("*".equals(permission.getMethod()) ||
                        method.toUpperCase(Locale.ROOT).equals(StringKit.toUpperCaseWithSafe(permission.getMethod()))) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 是否公开的URL
     *
     * @param url 链接地址
     * @return 是否公开
     */
    private boolean isPublicUrl(String url) {
        for (String publicUrl : PUBLIC_URL_PREFIXES) {
            if (url.startsWith(publicUrl)) {
                for (String publicUrlExclude : PUBLIC_URLS_EXCLUDES) {
                    if (url.contains(publicUrlExclude)) {
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }
}

总结:

1.要有一个设计良好的RBAC权限模型
2.对于Restful风格的URL权限一定要描述其URL和Method才能够准确鉴权(因为Restful风格的方法存在URL相同,Method不同的情况)。
3.框架的拦截器设计思想。
4.正则表达式的运用,反向生成权限URL的正则表达式。
5.URL放行的匹配可以利用Ant路径表达式来实现(待改进)

以上是工作时的经验所得。如有不正确的地方,还请各位读者指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值