Diskuze: spring boot využití userDetailsContextMapper
V předchozím kvízu, Online test znalostí Java, jsme si ověřili nabyté zkušenosti z kurzu.


Matúš Olejník:18.1.2021 10:32
Ahoj, začal by som breakpointom na mieste SimpleAuthorityMapper.java:78 a pozrel sa čo tam spôsobuje NPE. Čisto podľa názvu niečo s autoritami, rolami atď. Môžeš potom poslať kde konkrétne bola null hodnota hoci tam byť nemala.
Ladislav Niderle:18.1.2021 11:43
Ahoj, už jsem to našel. problém je, že jak si vytahuji z DB informace jaké role má daný uživatel na sobě zavedené v programu, tak je to moje vlastní třída, která implementuje Serializable a dále mám k této roli i jejich jednotlivé permissions a jestli má stav read nebo write. Padá mi to v SimpleAuthorityMapper, kde se snaží vytvořit z tohoto pole mapAuthorities:
public Set<GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {
HashSet<GrantedAuthority> mapped = new HashSet(authorities.size());
Iterator var3 = authorities.iterator();
while(var3.hasNext()) {
GrantedAuthority authority = (GrantedAuthority)var3.next();
mapped.add(this.mapAuthority(authority.getAuthority()));
}
if (this.defaultAuthority != null) {
mapped.add(this.defaultAuthority);
}
return mapped;
}
A problém je ten, že v tom while se mi do proěnné authority dostanou právě ty informace, ale následně vytažení pomocí getAuthority mi vrátí null.
Zde máme ještě to provázání:
User:
@Table(name = "crmdata.user")
public class User {
@Id
private String username;
@Column(name = "last_login")
private LocalDateTime lastLogin;
@OneToMany(mappedBy = "username", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@OnDelete(action = OnDeleteAction.CASCADE)
private Collection<AssignedUserRole> roles;
public static Collection<User> fromDtos(@NonNull Collection<UserDto> users) {
return users.stream()
.map(User::fromDto)
.collect(Collectors.toList());
}
public static User fromDto(@NonNull UserDto user) {
return User.builder()
.username(user.getUsername())
.lastLogin(user.getLastLogin())
.roles(AssignedUserRole.fromDtos(
user.getRoles().stream()
.map(r -> AssignedUserRoleDto.builder()
.username(user.getUsername())
.roleId(r.getName().toUpperCase())
.build())
.collect(Collectors.toList())))
.build();
}
}
Assigned user role
@Table(name = "crmdata.assigned_roles")
public class AssignedUserRole implements Serializable {
@Id
@Column
private String username;
@Column(name = "role_id")
private String roleId;
public static Collection<AssignedUserRole> fromDtos(@NonNull Collection<AssignedUserRoleDto> assignedUserRoleDtos) {
return assignedUserRoleDtos.stream()
.map(AssignedUserRole::fromDto)
.collect(Collectors.toList());
}
private static AssignedUserRole fromDto(@NonNull AssignedUserRoleDto assignedUserRoleDto) {
return AssignedUserRole.builder()
.username(assignedUserRoleDto.getUsername())
.roleId(assignedUserRoleDto.getRoleId())
.build();
}
}
Toto jsou jednotlivé třídy, které jsou propojené, aby to fungovalo podle kolegy správně.
A pak mám samotný mapper, kde to vytahuju:
@Override
@SneakyThrows
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
LdapUserDetails userDetails = (LdapUserDetails) super.mapUserFromContext(ctx, username, authorities);
String userDetailsUsername = userDetails.getUsername();
// inject userService(username)
UserDto userWithRoles = userService.getUserWithRoles(username, userService.getDisplayName(ctx));
if (userWithRoles == null) {
throw new UnsupportedOperationException(String.format("User %s doesn't have access. ", username));
} else {
return userWithRoles;
}
}
Zde je ještě UserDto:
@Data
@Builder
@AllArgsConstructor(staticName = "of")
public class UserDto implements UserDetails {
private final String username;
private final String displayName;
private final Set<UserRole> roles;
private LocalDateTime lastLogin;
public static UserDto fromRequest(DeleteUserRequest deleteUserRequest) {
return UserDto.builder()
.username(deleteUserRequest.getUsername())
.build();
}
public static Collection<UserDto> fromEntities(@NonNull Collection<User> users) {
return users.stream()
.map(user -> fromEntity(user, ""))
.collect(Collectors.toList());
}
public static UserDto fromEntity(@NonNull User user, String displayName) {
return UserDto.builder()
.username(user.getUsername())
.displayName(displayName)
.lastLogin(user.getLastLogin())
.roles(user.getRoles().stream()
.map(role -> UserRole.valueOf(role.getRoleId()))
.collect(Collectors.toSet()))
.build();
}
@JsonValue
private String getInitials() {
String[] names = this.displayName.split(", ");
return String.valueOf(names[names.length - 1].charAt(0) + names[0].charAt(0)).toUpperCase();
}
@Override
public Collection<UserPermission> getAuthorities() {
return this.roles.stream()
.flatMap(r -> r.getUserPermissions().stream())
.collect(Collectors.toSet());
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Zde je pak enum pro samotné role:
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum UserRole {
ADMIN(
"Admin",
"Master of the Universe",
UserPermission.permission(LICENCE_LSM, WRITE),
UserPermission.permission(LICENCE_ASW, WRITE),
UserPermission.permission(LICENCE_ADAM, WRITE),
UserPermission.permission(MAINTENANCE_CZ, WRITE),
UserPermission.permission(MAINTENANCE_SK, WRITE)
),
EDITOR_CZ(
"Editor",
"Editor of the Universe in CZ",
UserPermission.permission(LICENCE_LSM, WRITE),
UserPermission.permission(LICENCE_ASW, WRITE)
),
READER_CZ(
"Reader",
"Reader of the Universe in CZ",
UserPermission.permission(LICENCE_LSM, READ),
UserPermission.permission(LICENCE_ASW, READ)
),
EDITOR_SK(
"Editor",
"Editor of the Universe in SK",
UserPermission.permission(LICENCE_ADAM, WRITE),
UserPermission.permission(MAINTENANCE_SK, WRITE)
),
READER_SK(
"Reader",
"Reader of the Universe in SK",
UserPermission.permission(LICENCE_ADAM, READ)
);
@JsonCreator
public static UserRole forValues(
@JsonProperty("name") String name
) {
for (UserRole userRole : UserRole.values()) {
if (userRole.name.equals(name)) {
return userRole;
}
}
return null;
}
@JsonValue
private final String name;
@JsonValue
private final String description;
@JsonValue
private final Set<UserPermission> userPermissions;
UserRole(String name, String description, UserPermission... userPermissions) {
this(name, description, new HashSet<>(Arrays.asList(userPermissions)));
}
}
Nevím jak docílit toho, aby mi ta metoda nepadala. Kdyby jsi měl nějakou radu, tak budu jedině rád, jelikož já jsem v JAVE málo kovaný (dělla jsem hlavně klienta) a se sprignem ještě míň, takže samotná securita je pro mně docela složitá bohužel.
Matúš Olejník:18.1.2021 16:05
Ťažko takto radiť :/ Hoď breakpoint tam kde mapuješ tie autority, celý čas v debugu pozeraj usera a jeho autority, prípadne hoď breakpoint aj do getterov a setterov a postupne krokuj až kým neuvidíš že sa ti v nejakom kroku vymazali autority na užívateľovi.
Zobrazeno 4 zpráv z 4.