IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
Avatar
Ladislav Niderle:18.1.2021 6:33

Ahoj, snažím se v aplikaci, kde je přihlašování pomocí LDAPu napojeit ještě vlastní data k uživately pomocí userDetailsCon­textMapper. To mám a nikde mi to nepadá na chybu, ale když aplikaci pustím a pokusím se přihlásit, tak i když mi to novým kódem projde bez problému, tak mi to vypíše chybu.

Zde je stack co mi to vypíše:

18-Jan-2021 06:25:19.190 SEVERE [http-nio-8080-exec-1] org.springframework.boot.web.servlet.support.ErrorPageFilter.forwardToErrorPage Forwarding to error page from request [/login] due to exception [null]
 java.lang.NullPointerException
        at org.springframework.security.core.authority.mapping.SimpleAuthorityMapper.mapAuthority(SimpleAuthorityMapper.java:78)
        at org.springframework.security.core.authority.mapping.SimpleAuthorityMapper.mapAuthorities(SimpleAuthorityMapper.java:60)
        at org.springframework.security.core.authority.mapping.SimpleAuthorityMapper.mapAuthorities(SimpleAuthorityMapper.java:33)
        at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.createSuccessfulAuthentication(AbstractLdapAuthenticationProvider.java:117)
        at org.springframework.security.ldap.authentication.AbstractLdapAuthenticationProvider.authenticate(AbstractLdapAuthenticationProvider.java:92)
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)
        at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
        at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:130)
        at org.springframework.boot.web.servlet.support.ErrorPageFilter.access$000(ErrorPageFilter.java:66)
        at org.springframework.boot.web.servlet.support.ErrorPageFilter$1.doFilterInternal(ErrorPageFilter.java:105)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.springframework.boot.web.servlet.support.ErrorPageFilter.doFilter(ErrorPageFilter.java:123)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

Zkusil jsem: Snažil jsem se podívat jestli mi to někde nepadá nebo zda někde nenajdu podobný problém na netu, ale nikde jsem to nenašel a už se cítím bezradně, jelikož to prý mělo být velmi jednoduché.

Chci docílit: Snažím se po přihlášení zajistit, abych měl více informací o uživately, než které dostanu z LDAPu. Mám na to vlastní tabulky v DB a to už správně vytahuji, ale jinak to nechce jet.
Předem moc děkuji za pomoc.

Laďa

 
Odpovědět
18.1.2021 6:33
Avatar
Odpovídá na Ladislav Niderle
Matúš Olejník:18.1.2021 10:32

Ahoj, začal by som breakpointom na mieste SimpleAuthori­tyMapper.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.

Nahoru Odpovědět
18.1.2021 10:32
/* I am not sure why this works but it fixes the problem */
Avatar
Odpovídá na Matúš Olejník
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 SimpleAuthori­tyMapper, 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.

 
Nahoru Odpovědět
18.1.2021 11:43
Avatar
Odpovídá na Ladislav Niderle
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.

Nahoru Odpovědět
18.1.2021 16:05
/* I am not sure why this works but it fixes the problem */
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.