You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot/Security中如何实现用户仅访问自身数据并支持分页

问题根因

你写的代码存在3个核心问题,直接导致权限异常和逻辑不符合预期:

  • 方法入参缺失路径变量接收:接口路径定义了{userId}占位符,但方法签名中没有声明被@PathVariable修饰的userId参数,@PreAuthorize里的SpEL表达式无法读取到#userId对应的值,直接触发权限校验失败
  • 校验逻辑类型不匹配:路径参数默认是String类型,而登录凭证中authentication.principal.id通常是Long/Integer类的数值类型,直接用==比对会因为类型不一致永远返回false,抛出403权限异常
  • 业务查询逻辑错误:当前调用的productService.findAll(pageable)是管理员端的全量查询方法,没有加用户维度的过滤条件,就算权限校验通过,也会返回全量产品数据,出现越权问题
修复方案

1. 修正Controller层接口实现

补全路径参数接收,修复权限校验表达式,替换查询方法:

// 注意:如果UserRestController类上已经配置了@RequestMapping("/api/v1")前缀,此处@GetMapping只需要写"/{userId}/products"即可,避免路径重复映射
@GetMapping("/api/v1/{userId}/products")
// 做类型转换后再比对用户ID,避免类型不匹配问题
@PreAuthorize("T(Long).valueOf(#userId) == authentication.principal.id")
public Response<Page<Product>> getProductPage(
    @PathVariable String userId,
    @PageableDefault(sort = "id") Pageable pageable
) {
    Long currentUserId = Long.valueOf(userId);
    // 调用按用户ID过滤的分页查询方法,不要用全量查询
    return Response.ok(productService.findPageByUserId(currentUserId, pageable));
}

提示:如果你的登录用户主体是自定义UserDetails实现,id是私有字段、通过getId()方法对外暴露,请将@PreAuthorize中的authentication.principal.id调整为authentication.principal.getId(),避免SpEL无法读取属性值。

2. 新增用户维度的分页查询能力

不要复用管理员端的全量查询方法,在Service和持久层新增按用户ID过滤的查询逻辑:

  • ProductService层新增方法定义:
Page<Product> findPageByUserId(Long userId, Pageable pageable);
  • Service实现类中调用持久层方法,添加用户ID过滤条件。如果使用Spring Data JPA,可直接在Repository中按规则声明查询方法:
public interface ProductRepository extends JpaRepository<Product, Long> {
    // 若Product实体中存在userId关联字段,直接按方法名规则即可实现过滤查询;如果是多表关联,自行编写JPQL/SQL添加userId过滤条件即可
    Page<Product> findAllByUserId(Long userId, Pageable pageable);
}
优化建议

如果项目中大量使用数值类型的路径参数,可以在Spring MVC配置中注册String转Long的通用类型转换器,之后可以直接将路径参数声明为Long类型,此时权限校验表达式不需要手动做类型转换,写法更简洁:

// 配置转换器后,Controller方法可以直接这么写
@GetMapping("/api/v1/{userId}/products")
@PreAuthorize("#userId == authentication.principal.id")
public Response<Page<Product>> getProductPage(
    @PathVariable Long userId,
    @PageableDefault(sort = "id") Pageable pageable
) {
    return Response.ok(productService.findPageByUserId(userId, pageable));
}

内容的提问来源于stack exchange,提问作者Jack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 11:09:23