问题描述
@Entity
@Table(name="users")
@Getter @Setter
public class usermodel implements Serializable {
@Setter(AccessLevel.NONE)
@Getter(AccessLevel.NONE)
private static final long serialVersionUID = -5608230793232883579L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false,unique = true)
private String userId;
@Column(nullable = false,length = 50)
private String firstName;
@Column(nullable = false,length = 50)
private String lastName;
@Email
@Column(nullable = false,length = 120,unique = true)
private String email;
@Column(nullable = false)
private String encryptedPassword;
private Boolean emailVerificationStatus = false;
private String emailVerificationToken;
@ManyToMany(cascade= { CascadeType.PERSIST },fetch = FetchType.EAGER )
@JoinTable(
name = "user_role",joinColumns = @JoinColumn(name = "user_id",referencedColumnName = "id"),inverseJoinColumns=@JoinColumn(name = "role_id",referencedColumnName = "id"))
private List<RoleModel> roles;
@JsonManagedReference
@OnetoMany(mappedBy = "user")
private List<ProjectModel> projects;
}
@Entity
@Table(name= "projects")
@Getter @Setter
public class ProjectModel implements Serializable {
@Setter(AccessLevel.NONE)
@Getter(AccessLevel.NONE)
public static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(nullable = false,unique = true)
private String projectId;
// ...
@Column
@JsonManagedReference
@OnetoMany(mappedBy = "project")
private List<ObjectiveModel> objectives;
// ...
@JsonBackReference
@ManyToOne(
cascade = { CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH },fetch = FetchType.LAZY
)
private usermodel user;
}
我还使用DTO层与数据库通信:
@Getter @Setter
public class UserDto implements Serializable {
@Setter(AccessLevel.NONE)
@Getter(AccessLevel.NONE)
private static final long serialVersionUID = -5352357837541477260L;
// contains more information than models used for rest
private long id;
private String userId;
private String firstName;
private String lastName;
private String email;
private String password;
private String encryptedPassword;
private String emailVerificationToken;
private Boolean emailVerificationStatus = false;
private List<String> roles;
private List<ProjectDto> projects;
}
每个实体都有自己的Dto等效项。我可以创建一个用户。我的问题是尝试登录。我的userServiceImpl实现了Spring Security UserService。这是我的实现:
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
usermodel usermodel = userRepository.findByEmail(email);
if(usermodel == null)
throw new UsernameNotFoundException("User with email " + email + " not found");
return new UserPrincipalManager(usermodel);
}
我的UserPrincipalManager:
public class UserPrincipalManager implements UserDetails {
private static final long serialVersionUID = 7464059818443209139L;
private usermodel usermodel;
private ProjectModel projectModel;
@Getter @Setter
private String userId;
@Autowired
public UserPrincipalManager(usermodel usermodel) {
this.usermodel = usermodel;
this.userId = usermodel.getUserId();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new HashSet<>();
Collection<AuthorityModel> authorityModelEntities = new HashSet<>();
// get user roles
Collection<RoleModel> roleModels = usermodel.getRoles();
if (roleModels == null) {
return authorities; // null
}
// get user roles
roleModels.forEach((role) ->{
authorities.add(new SimpleGrantedAuthority(role.getName()));
authorityModelEntities.addAll(role.getAuthorities());
});
// get user authorities
authorityModelEntities.forEach(authorityModel -> {
authorities.add(new SimpleGrantedAuthority(authorityModel.getName()));
});
return authorities;
}
@Override
public String getpassword() {
return this.usermodel.getEncryptedPassword();
}
@Override
public String getUsername() {
return this.usermodel.getEmail();
}
// we do not store this information in DB
@Override
public boolean isAccountNonExpired() {
return true;
}
// we do not store this information in DB (yet)
@Override
public boolean isAccountNonLocked() {
return true;
}
// we do not store this information in DB (yet)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// isEnabled depending if account is activated => email verification status value
@Override
public boolean isEnabled() {
return this.usermodel.getEmailVerificationStatus();
}
}
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:31)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:106)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:31)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:106)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:59)
at org.modelmapper.internal.converter.MergingCollectionConverter.convert(MergingCollectionConverter.java:31)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:303)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:110)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:242)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:188)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:106)
最后,应用程序崩溃并返回403错误。
2020-10-05 12:07:22.215 DEBUG 4564 --- [nio-8080-exec-8] o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.Vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.3.3.RELEASE.jar:5.3.3.RELEASE]
解决方法
我对模型映射器一无所知,但我想为您提供替代解决方案,因为我认为这是Blaze-Persistence Entity Views的完美用例。
我创建了该库,以允许在JPA模型与自定义接口或抽象类定义的模型之间轻松进行映射,例如类固醇上的Spring Data Projections。这个想法是,您可以按自己喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(获取器)映射到实体模型。
针对您的用例的DTO模型可能与Blaze-Persistence Entity-Views相似,如下所示:
@EntityView(UserModel.class)
public interface UserDto extends Serializable {
@IdMapping
Long getId();
String getUserId();
String getFirstName();
String getLastName();
String getEmail();
String getPassword();
String getEncryptedPassword();
String getEmailVerificationToken();
Boolean getEmailVerificationStatus();
Set<String> getRoles();
Set<ProjectDto> getProjects();
@EntityView(ProjectModel.class)
interface ProjectDto {
@IdMapping
Long getId();
String getProjectId();
// Other mappings...
}
}
查询是将实体视图应用于查询的问题,最简单的方法就是按ID查询。
UserDto a = entityViewManager.find(entityManager,UserDto.class,id);
Spring Data集成使您可以像使用Spring Data Projections一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
这里最大的好处是,它只会获取实际需要的列,并且会在启动时根据您的JPA模型验证DTO模型,因此不会再有运行时意外!