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

使用JPA关联Permissions与Users表时遇Access Denied问题求助

JPA关联Permissions与Users表触发AccessDeniedException的排查与优化方案

问题背景

通过createdBy字段使用JPA关联Permissions表和Users表时,触发org.springframework.security.access.AccessDeniedException: Access Denied异常,取消关联配置后系统恢复正常。需排查异常原因,并找到更优的关联方式获取用户信息。

报错栈信息

org.springframework.security.access.AccessDeniedException: Access Denied
        at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:98) ~[spring-security-web-6.1.3.jar:6.1.3]
        ...

相关实体类代码

User实体类(实现UserDetails)

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collection;

@Entity(name = "users")
@Data
public class User implements UserDetails {
    ...
}

Permission实体类

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;

@Entity(name = "permissions")
@Data
@NoArgsConstructor
public class Permission {
    ...
}

异常原因分析

  1. 关联加载触发权限校验:当JPA自动加载关联的User实体时,User类实现了UserDetails,其内部权限相关字段(如authorities)的加载或访问可能触发Spring Security的权限校验逻辑,若当前认证用户无对应权限则抛出异常。
  2. 延迟加载的上下文缺失:若使用懒加载(默认关联策略),在视图渲染阶段(OpenSessionInView模式下)延迟加载User实体时,Security的认证上下文可能已失效,导致权限校验失败。
  3. 实体权限注解冲突:若User或Permission实体的关联字段上标注了@PreAuthorize等权限注解,关联加载时会触发这些注解的校验,不符合条件则拒绝访问。
  4. 查询未做权限过滤:关联查询时未添加用户权限过滤条件,查询到了当前用户无权访问的User数据,触发Security的全局权限拦截。

优化关联方案与解决措施

1. 懒加载+显式控制加载时机

将关联设置为懒加载,仅在需要用户信息的Service层主动加载,避免自动触发不必要的权限校验:

@Entity(name = "permissions")
@Data
@NoArgsConstructor
public class Permission {
    // 存储创建者ID,用于手动加载
    @Column(name = "created_by", updatable = false)
    private Long createdById;

    // 懒加载关联,仅在主动调用时加载User
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "created_by", insertable = false, updatable = false)
    private User createdBy;
}

在Service层需要用户信息时,通过userRepository.findById(permission.getCreatedById())主动获取,确保操作在有效认证上下文内执行。

2. 使用DTO封装查询结果

避免直接关联实体,通过自定义JPQL查询仅获取所需用户字段,封装到DTO中,减少权限校验触发点:

// 定义DTO类
public class PermissionWithUserDto {
    private Long permissionId;
    private String permissionName;
    private String creatorUsername;
    private String creatorEmail;

    // 构造方法
    public PermissionWithUserDto(Long permissionId, String permissionName, String creatorUsername, String creatorEmail) {
        this.permissionId = permissionId;
        this.permissionName = permissionName;
        this.creatorUsername = creatorUsername;
        this.creatorEmail = creatorEmail;
    }

    // Getter
}

// 在PermissionRepository中添加查询方法
@Repository
public interface PermissionRepository extends JpaRepository<Permission, Long> {
    @Query("SELECT new com.example.dto.PermissionWithUserDto(p.id, p.name, u.username, u.email) " +
           "FROM permissions p JOIN users u ON p.createdById = u.id WHERE p.id = :id")
    PermissionWithUserDto findPermissionWithCreatorInfo(@Param("id") Long permissionId);
}

3. 调整权限配置允许关联访问

若必须关联加载,确保当前用户拥有访问User实体的权限:

  • 在User实体类或字段上添加权限注解:
@Entity(name = "users")
@Data
@PreAuthorize("hasAuthority('VIEW_USER_BASIC_INFO')")
public class User implements UserDetails {
    ...
}
  • 确保当前认证用户被授予VIEW_USER_BASIC_INFO权限。

4. 调整OpenSessionInView过滤器顺序

若使用OpenSessionInView,将其放在Security过滤器链之后,确保延迟加载时认证上下文有效:

@Bean
public FilterRegistrationBean<OpenSessionInViewFilter> openSessionInViewFilter() {
    FilterRegistrationBean<OpenSessionInViewFilter> registration = new FilterRegistrationBean<>();
    registration.setFilter(new OpenSessionInViewFilter());
    // 设置在Security过滤器之后执行
    registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE - 1);
    return registration;
}

内容的提问来源于stack exchange,提问作者Khả Nam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:08:09