Java权限校验案例如何编写

wen java案例 32

Java权限校验案例:从零到一构建安全访问控制系统

目录导读

  1. 权限校验的核心概念与必要性
  2. 基于RBAC模型的权限设计思路
  3. 实战案例:Spring Security + JWT实现接口级权限
  4. 自定义注解实现细粒度方法级权限校验
  5. 常见权限校验陷阱与解决方案
  6. Q&A:开发者最关心的权限校验问题

权限校验的核心概念与必要性

权限校验是Java应用安全体系中的第一道防线,根据OWASP Top 10统计,权限管理缺陷长期位列安全风险前五,权限校验就是回答三个问题:你是谁?你能做什么?你被允许这样做吗?

Java权限校验案例如何编写

典型场景包括:

  • 用户A只能查看自己的订单,不能查看用户B的订单(数据级权限)
  • 管理员可以删除帖子,普通用户只能举报(功能级权限)
  • API接口限流时,VIP用户拥有更高调用频率(资源级权限)

案例背景:一个在线教育平台,需要实现“教师可以上传课程、学生只能观看、管理员可以审核”的权限体系。


基于RBAC模型的权限设计思路

RBAC(Role-Based Access Control)是目前企业级应用最广泛的权限模型,核心要素包括:

  • 用户(User):系统操作主体
  • 角色(Role):权限的集合,如ROLE_TEACHER、ROLE_STUDENT
  • 权限(Permission):具体操作,如course:create、course:delete

设计步骤:

  1. 数据库表结构:

    • user表 (id, username, password)
    • role表 (id, name, description)
    • user_role关联表
    • permission表 (id, name, url_pattern)
    • role_permission关联表
  2. 权限颗粒度建议:

    • 粗粒度:按照URL路径匹配(如 /api/course/** 需要 course:manage
    • 细粒度:方法级注解控制(如 @PreAuthorize("hasPermission('course','update')")

关键设计原则:权限应继承自角色,而非直接分配给用户,便于运维扩展。


实战案例:Spring Security + JWT实现接口级权限

以Spring Boot 3 + Spring Security 6为例,实现JWT令牌鉴权与URL路径权限控制。

Step 1:依赖配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.5</version>
</dependency>

Step 2:自定义过滤器验证JWT

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) {
        String token = extractToken(request);
        if (token != null && JwtUtil.validate(token)) {
            UsernamePasswordAuthenticationToken auth = 
                new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities()
                );
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        filterChain.doFilter(request, response);
    }
}

Step 3:配置权限规则

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(auth -> auth
        .requestMatchers("/api/admin/**").hasRole("ADMIN")
        .requestMatchers("/api/course/create").hasAuthority("course:create")
        .requestMatchers("/api/course/view").authenticated()
        .anyRequest().permitAll()
    ).addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    return http.build();
}

测试案例:

  • 普通学生访问 /api/course/create → 返回403
  • 教师携带正确JWT访问 → 成功创建课程

自定义注解实现细粒度方法级权限校验

当需要控制“只能修改自己的课程”这类数据级权限时,URL匹配无法满足,此时采用Spring AOP + 注解实现。

创建权限校验注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckOwner {
    String resourceType(); // 如 "Course"
    String idField();      // 请求参数中的ID字段名
}

AOP切面实现

@Around("@annotation(checkOwner)")
public Object checkOwnerAccess(ProceedingJoinPoint joinPoint, 
                                CheckOwner checkOwner) throws Throwable {
    // 1. 从参数中获取资源ID
    Object[] args = joinPoint.getArgs();
    Long resourceId = extractId(args, checkOwner.idField());
    // 2. 查询资源所属人
    Course course = courseService.findById(resourceId);
    // 3. 对比当前用户ID
    Long currentUserId = SecurityUtils.getCurrentUserId();
    if (!course.getUserId().equals(currentUserId)) {
        throw new AccessDeniedException("只能操作自己的资源");
    }
    return joinPoint.proceed();
}

使用方式

@CheckOwner(resourceType = "Course", idField = "courseId")
public Course updateCourse(@RequestParam Long courseId, @RequestBody Course course) {
    // 业务逻辑
}

常见权限校验陷阱与解决方案

陷阱1:使用@Secured@RolesAllowed混用

问题:部分框架版本不兼容,导致注解失效。
解决:统一使用@PreAuthorize,表达式更灵活(如#id == authentication.principal.id)。

陷阱2:Token未校验权限变更

问题:用户被撤销角色后,已签发的JWT依然有效。
解决:每次请求从数据库查询最新权限(可缓存但设置较短TTL)。

陷阱3:跨域请求中忽略OPTIONS预检

问题:浏览器发送OPTIONS请求时无Token,被拦截。
解决:在Security配置中放行OPTIONS:

http.cors().configurationSource(request -> {
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedMethod("OPTIONS");
    return config;
});

Q&A:开发者最关心的权限校验问题

Q1:微服务架构中如何实现统一权限校验?
A:采用网关层统一鉴权(如Spring Cloud Gateway + JWT),服务间调用使用内部Token,每个服务仅校验业务权限,不重复解析用户信息。

Q2:权限数据量大时如何优化性能?
A:采用两级缓存:本地缓存(Caffeine)存储用户角色,Redis缓存角色权限列表,注意在权限变更时要刷新缓存。

Q3:如何防止权限绕过攻击?
A:三层防护:1)前端菜单隐藏;2)后端接口必做权限校验;3)敏感操作添加二次验证(如短信验证码)。

Q4:RBAC能否支持权限继承?
A:可以设计父子角色关系,如“超级管理员”继承“管理员”的所有权限,实现时在查询权限时递归查询父角色权限。


权限校验是Java开发中的“看似简单实则致命”的环节,从数据库表设计到Spring Security配置,再到自定义注解实现数据级权限,每一层都需要精心编码,建议在项目初期就引入权限测试用例,模拟越权访问场景,确保安全防线稳固。

抱歉,评论功能暂时关闭!