【问题标题】:How to get AuditorAware to store CreatedBy as UUID from Spring Security?如何让 AuditorAware 将 CreatedBy 存储为 Spring Security 的 UUID?
【发布时间】:2021-02-03 14:25:05
【问题描述】:

我正在尝试从我的登录用户获取 UUID,以将其存储为“CreatedBy”和“LastUpdatedBy”,而不仅仅是字符串。但是,我收到一个演员错误:

类 java.lang.String 不能转换为类 com.example.lims.container.Container

以下所有代码...谢谢!

大编辑:

我更改了 ApplicationUser 以实现 SpringSecurity 中的 UserDetails 并将合法的映射关系添加到我的 BaseEntity。现在我得到了一个新的例外。我很确定这与我从 SecurityAuditAware 返回的内容有关。我不知道如何让它返回正确的 ApplicationUser 实例。

java.lang.ClassCastException:类 java.lang.String 无法转换为类 com.example.lims.user.ApplicationUser(java.lang.String 在加载器“bootstrap”的模块 java.base 中;me.tmpjr .lims.user.ApplicationUser 位于加载器 org.springframework.boot.devtools.restart.classloader.RestartClassLoader @3d3e8a83 的未命名模块中 在 com.example.lims.security.SecurityAuditorAware.getCurrentAuditor(SecurityAuditorAware.java:20) ~[classes/:na]

架构:

CREATE TABLE IF NOT EXISTS "containers" (
    id uuid NOT NULL,
    container_name VARCHAR(100) NOT NULL,
    created_by UUID NOT NULL,
    created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
    updated_by UUID,
    updated_at TIMESTAMP WITHOUT TIME ZONE,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS "users" (
    id uuid NOT NULL,
    username VARCHAR(100) NOT NULL,
    password VARCHAR(100) NOT NULL,
    created_by UUID,
    created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
    updated_by UUID,
    updated_at TIMESTAMP WITHOUT TIME ZONE,
    PRIMARY KEY (id)
);

BaseEntity AuditorAware:

@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected UUID id;

    @CreatedDate
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_at")
    protected Date createdAt;

    @LastModifiedDate
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "updated_at")
    protected Date updatedAt;

    @CreatedBy
    @JoinColumn(name = "created_by", nullable = true, insertable = true, updatable = false)
    @ManyToOne
    protected ApplicationUser createdBy;

    @LastModifiedBy
    @JoinColumn(name = "updated_by", nullable = true, insertable = false, updatable = false)
    @ManyToOne
    protected ApplicationUser updatedBy;
}

应用用户:

@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Valid
@Table(name = "users")
@NoArgsConstructor
public class ApplicationUser extends BaseEntity implements UserDetails 
{
    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String password;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }
}

返回 ApplicationUser 的 Security AuditorAware 实现:

@Component
public class SecurityAuditorAware implements AuditorAware<ApplicationUser> {
    public Optional<ApplicationUser> getCurrentAuditor() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (auth == null || !auth.isAuthenticated()) {
            return Optional.empty();
        }
        // THIS is where I think it's going wrong.. what to return here? How to return ApplicationUser?
        return Optional.ofNullable((ApplicationUser) auth.getPrincipal());
    }
}

UserDetailsS​​erviceImpl:

@RequiredArgsConstructor
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    private final ApplicationUserRepository userRepository;

    @Override
    public ApplicationUser loadUserByUsername(String username) throws UsernameNotFoundException {
        ApplicationUser applicationUser = userRepository.findByUsername(username);
        if (applicationUser == null) {
            throw new UsernameNotFoundException(username);
        }

        return applicationUser;
    }
}

Bean 配置:

@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class PersistenceConfig {
    @Bean
    public AuditorAware<ApplicationUser> auditorAware() {
        return new SecurityAuditorAware();
    }
}

完全例外:

java.lang.ClassCastException: class java.lang.String cannot be cast to class com.example.lims.user.ApplicationUser (java.lang.String is in module java.base of loader 'bootstrap'; com.example.lims.user.ApplicationUser is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @3d3e8a83)
    at com.example.lims.security.SecurityAuditorAware.getCurrentAuditor(SecurityAuditorAware.java:21) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]

有效的解决方法?

我相信我已经找到了问题所在。由于我使用的是 UsernamePasswordAuthenticationToken getPrinciple 不返回 UserDetails 对象。我根本无法将它转换为我的自定义实体类。但是,如果我在 SecurityAuditorAware 中手动使用存储库搜索并直接返回它就可以了!

@Component
@RequiredArgsConstructor
public class SecurityAuditorAware implements AuditorAware<ApplicationUser> {
    private final ApplicationUserRepository userRepository;

    public Optional<ApplicationUser> getCurrentAuditor() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (auth == null || !auth.isAuthenticated()) {
            return Optional.empty();
        }

        return Optional.ofNullable((ApplicationUser) userRepository.findByUsername((auth.getName())));
    }
}

【问题讨论】:

    标签: java spring spring-security spring-data-jpa


    【解决方案1】:
    change your id data type to Long
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    protected Long id;
    

    然后你的安全审计员应该是这样的

    public class SpringSecurityAuditorAware implements AuditorAware<Long> {
    
        
    
        @Override
        @Transactional(readOnly = false, propagation = Propagation.SUPPORTS)
        public Long getCurrentAuditor() {
            Authentication auth = 
            SecurityContextHolder.getContext().getAuthentication();
            
            return auth.getPrincipal().getId();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-02-14
      • 2012-12-22
      • 1970-01-01
      • 2020-02-21
      • 2017-07-08
      • 2014-09-13
      • 2019-06-03
      • 1970-01-01
      • 2010-10-20
      相关资源
      最近更新 更多