专业的JAVA编程教程与资源

网站首页 > java教程 正文

SpringBoot中使用拦截器实现基于角色的接口访问控制

temp10 2025-03-10 20:31:21 java教程 136 ℃ 0 评论

1、背景

控制接口的访问权限在应用开发中至关重要,主要体现在以下几个方面:

  • 防止数据泄露:应用程序通常存储和处理大量敏感数据,如用户个人信息(姓名、身份证号、银行卡号等)、商业机密(产品研发计划、财务数据等)。如果接口没有访问权限控制,恶意用户可能通过直接调用接口获取这些敏感数据,导致数据泄露,给用户和企业带来严重损失。例如,黑客可能通过无权限控制的用户信息查询接口,获取大量用户的隐私数据,进行非法售卖或诈骗活动。
  • 确保数据完整性:有权限的用户按照既定规则对数据进行操作,能够维护数据的完整性。若任何人都能随意调用修改数据的接口,数据可能被恶意篡改或误操作,破坏数据的一致性和准确性。比如,财务系统中,若财务数据修改接口无权限控制,可能导致账目数据被随意篡改,影响企业财务核算和决策。
  • 避免资源滥用:限制接口访问权限可以防止恶意或过度的请求,避免系统资源(如 CPU、内存、网络带宽等)被耗尽。如果没有权限控制,攻击者可能通过大量并发请求某个接口,使服务器不堪重负,导致系统瘫痪,影响正常用户的使用。例如,某些抢购类接口,若不限制访问权限,恶意用户可能利用脚本进行大量抢购请求,占用过多服务器资源,使真正的用户无法正常参与抢购。
  • 保障关键业务流程:对于一些关键业务接口,如支付接口、订单处理接口等,严格的权限控制可以确保这些接口只能被合法的业务流程调用,保障业务的正常运转。否则,非法调用可能导致业务逻辑混乱,产生错误的交易记录或订单状态,给企业和用户带来经济损失。

常见的权限模型有自主访问控制(DAC,Discretionary Access Control)、强制访问控制(MAC,Mandatory Access Control)、基于角色的访问控制(RBAC,Role - Based Access Control)、基于属性的访问控制(ABAC,Attribute - Based Access Control)等,一般开发比较常遇到的是基于角色的访问控制(RBAC,Role - Based Access Control)。

SpringBoot中使用拦截器实现基于角色的接口访问控制

下面的示例中,我们将使用SpringBoot中的拦截器,对接口请求实现基于角色的访问控制。

2、自定义接口的角色权限注解

首先,创建一个自定义注解,用于标记需要进行权限控制的接口,并指定允许访问的角色。

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RoleRequired {
    String[] roles();
}

在上述代码中,@Retention(RetentionPolicy.RUNTIME) 表示该注解在运行时可用,@Target(ElementType.METHOD) 表示该注解用于方法上。roles() 方法用于指定允许访问该方法的角色数组。

在设计接口时,一般会定义好接口的角色权限,这样就可以使用注解的方式为接口指定允许访问的角色,在接口请求时,拦截器中可以通过注解获取到角色信息,与当前登录的用户角色进行比较。

如果要灵活一些,可以把接口(url)和角色(编码)的映射关系存放在数据库的表中,并提供界面进行维护。在应用启动时,加载接口和角色的关系到缓存中加速访问;有变更时,定时或实时刷新缓存,这个可能时实际开发中非常普遍的方式,在这个示例中,简单起见,用注解来实现。

3、创建接口鉴权拦截器

实现 HandlerInterceptor 接口,在拦截器中获取当前登录用户的角色,并与接口注解中指定的角色进行比较。

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * RoleBasedAccessInterceptor
 *
 * @author Bruce.CH
 * @since 2025年03月05日
 */
public class RoleBasedAccessInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取被调用方法的注解
        Method method = ((org.springframework.web.method.HandlerMethod) handler).getMethod();
        RoleRequired roleRequired = method.getAnnotation(RoleRequired.class);

        if (Objects.nonNull(roleRequired)) {
            // 获取接口允许访问的角色列表
            List allowedRoles = Arrays.asList(roleRequired.roles());

            // 获取当前登录用户拥有的角色列表
            List userRole = getUserRoles(request);

            // 如果接口允许访问的角色列表与用户的角色列表存在交集:说明用户有访问权限
            boolean hasIntersection = allowedRoles.stream().anyMatch(userRole::contains);

            // 判断是否存在交集: 如果没有交集,说明没有权限
            if (!hasIntersection) {
                // 简单起见,直接返回403
                // 在实际应用中,可以返回约定的json数据格式的相应结果,比如{"code":"4003", "message":"Access Denied"}
                response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
                return false;
            }
        }

        // 如果用户请求的是没有注解权限访问的接口即不要求权限控制的接口 或用户有访问权限,则交由接口继续处理请求
        return true;
    }

    private List getUserRoles(HttpServletRequest request) {
        // 假设拥有TL、SE、PM三个角色:实际开发中,应从后台查询或从经过认证后的用户会话或缓存中获取
        return Arrays.asList("TL", "SE", "PM");
    }
}

在 preHandle 方法中:

  • 首先获取被调用方法上的 RoleRequired 注解,如果注解存在,从注解中解析接口允许访问的角色列表
  • 然后,从请求头中获取当前登录用户的角色(实际应用中应从后台查询或从经过认证后的用户会话或缓存中获取)。
  • 将注解中允许的角色与用户角色进行比较。如果用户角色为空或不在允许列表中即没有交集,返回 403 禁止访问错误;否则,允许请求继续。

4、注册接口鉴权拦截器

创建一个配置类,继承 WebMvcConfigurer 接口,在 addInterceptors 方法中注册拦截器。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RoleBasedAccessInterceptor())
              .addPathPatterns("/**");
    }
}

addPathPatterns("/**") 表示拦截所有请求路径。如果只想拦截特定路径,可以修改路径模式,例如 addPathPatterns("/api/**") 只拦截 /api 开头的路径。

5、使用注解标记接口

在需要进行权限控制的接口方法上使用 RoleRequired 注解,并指定允许访问的角色。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class ExampleController {

    @GetMapping("/protected")
    @RoleRequired(roles = {"ADMIN", "SE"})
    public String protectedResource() {
        return "This is a protected resource.";
    }
}

在上述示例中,/api/protected 接口只有拥有 ADMIN 或 SE角色的用户可以访问。如果在拦截器中,获取到的登录用户拥有其中任意一个角色,则可以继续请求该接口的资源,否则将返回403错误。


6、总结

通过以上步骤,就可以在 Spring Boot 应用中使用拦截器实现基于角色对接口访问进行权限控制。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表