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
相关产品推荐
相关产品推荐

