Java权限校验案例:从零到一构建安全访问控制系统
目录导读
- 权限校验的核心概念与必要性
- 基于RBAC模型的权限设计思路
- 实战案例:Spring Security + JWT实现接口级权限
- 自定义注解实现细粒度方法级权限校验
- 常见权限校验陷阱与解决方案
- Q&A:开发者最关心的权限校验问题
权限校验的核心概念与必要性
权限校验是Java应用安全体系中的第一道防线,根据OWASP Top 10统计,权限管理缺陷长期位列安全风险前五,权限校验就是回答三个问题:你是谁?你能做什么?你被允许这样做吗?

典型场景包括:
- 用户A只能查看自己的订单,不能查看用户B的订单(数据级权限)
- 管理员可以删除帖子,普通用户只能举报(功能级权限)
- API接口限流时,VIP用户拥有更高调用频率(资源级权限)
案例背景:一个在线教育平台,需要实现“教师可以上传课程、学生只能观看、管理员可以审核”的权限体系。
基于RBAC模型的权限设计思路
RBAC(Role-Based Access Control)是目前企业级应用最广泛的权限模型,核心要素包括:
- 用户(User):系统操作主体
- 角色(Role):权限的集合,如ROLE_TEACHER、ROLE_STUDENT
- 权限(Permission):具体操作,如course:create、course:delete
设计步骤:
-
数据库表结构:
user表 (id, username, password)role表 (id, name, description)user_role关联表permission表 (id, name, url_pattern)role_permission关联表
-
权限颗粒度建议:
- 粗粒度:按照URL路径匹配(如
/api/course/**需要course:manage) - 细粒度:方法级注解控制(如
@PreAuthorize("hasPermission('course','update')"))
- 粗粒度:按照URL路径匹配(如
关键设计原则:权限应继承自角色,而非直接分配给用户,便于运维扩展。
实战案例: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配置,再到自定义注解实现数据级权限,每一层都需要精心编码,建议在项目初期就引入权限测试用例,模拟越权访问场景,确保安全防线稳固。