Search code examples
javaspringspring-bootintellij-ideaspring-security

Springboot application not recognizing any @RestController or @Service, and i think the @Repository too


I have springboot application which i was working on, on a different computer, i cloned the project from github and set up my intellij, installed all dependencies and tried to run the program.

The first thing i notice is that the security configuration is bypassed. A password is generated for me despite me having a Config file in place. When i open localhost:8080 and login, all endpoints dont work either.

All of these worked on my previous computer on the same version of intellij.

  • I have tried Using @ComponentScan but I still get the same result.
  • I have added this line to my application.properties logging.level.org.springframework.web=TRACE and it only shows that one endpoint is avalible, "/error"

this is my pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.appholdings</groupId>
    <artifactId>appname</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>appname</name>
    <description>appname</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
            <version>3.0.6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.restdocs</groupId>
            <artifactId>spring-restdocs-mockmvc</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.0.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.24</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk</artifactId>
            <version>1.12.429</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.asciidoctor</groupId>
                <artifactId>asciidoctor-maven-plugin</artifactId>
                <version>2.2.1</version>
                <executions>
                    <execution>
                        <id>generate-docs</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>process-asciidoc</goal>
                        </goals>
                        <configuration>
                            <backend>html</backend>
                            <doctype>book</doctype>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.restdocs</groupId>
                        <artifactId>spring-restdocs-asciidoctor</artifactId>
                        <version>${spring-restdocs.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>testCompile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Security Config

package com.mootoholdings.ecommserver.config;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {

    private final JwtAuthenticationFilter jwtAuthFilter;
    private final AuthenticationProvider authenticationProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http ) throws Exception {
        http.cors().and()
                .authorizeHttpRequests(request -> request
                        .requestMatchers("/api/v1/auth/**").permitAll()
                        .requestMatchers(HttpMethod.GET, "/api/products/**").permitAll()
                        .requestMatchers(HttpMethod.GET, "/api/categories", "/api/categories/*").permitAll()
                        .requestMatchers(HttpMethod.GET, "/").permitAll()
//                        .anyRequest().authenticated()
                                .anyRequest().permitAll()
                )
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
                .csrf().disable();

        return http.build();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(List.of("*"));
        configuration.setAllowedMethods(List.of("HEAD",
                "GET", "POST", "PUT", "DELETE", "PATCH"));
        // setAllowCredentials(true) is important, otherwise:
        // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
        configuration.setAllowCredentials(false);
        // setAllowedHeaders is important! Without it, OPTIONS preflight request
        // will fail with 403 Invalid CORS request
        configuration.setAllowedHeaders(List.of("*"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

Authentication Filter

import com.mootoholdings.ecommserver.service.JWTService;

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JWTService jwtService;
    private final UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(
            @NonNull HttpServletRequest request,
            @NonNull HttpServletResponse response,
            @NonNull FilterChain filterChain
    ) throws ServletException, IOException {

        final String authHeader = request.getHeader("Authorization");
        final String jwt;
        final String userEmail;

        if (authHeader == null || !authHeader.startsWith("Bearer")){
            filterChain.doFilter(request, response);
            return;
        }

        jwt = authHeader.substring(7);

        userEmail = jwtService.decodeUsername(jwt);

        if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null){
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail);
            if(jwtService.isTokenValid(jwt, userDetails)){
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails,
                        null,
                        userDetails.getAuthorities()
                );
                authToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                );
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
        }
         try {
            // something may throw an exception
            filterChain.doFilter(request, response);
         } catch (ServletException | IOException e) {
             log.error(e.getMessage());
         }
    }

}

my Application entry point

package com.mootoholdings.ecommserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class EcommserverApplication {
    public static void main(String[] args) {
        SpringApplication.run(EcommserverApplication.class, args);
    }
}

jwt service

package com.mootoholdings.ecommserver.service;

@Slf4j
@Service
public class JWTService {
    private final SupplierRepository supplierRepository;

    private final UserRepository userRepository;
    private final RiderRepository riderRepository;
    private final AdminRepository adminRepository;
    private final DistributorRepository distributorRepository;

    public JWTService(UserRepository userRepository,
                      RiderRepository riderRepository,
                      AdminRepository adminRepository,
                      DistributorRepository distributorRepository,
                      SupplierRepository supplierRepository) {
        this.userRepository = userRepository;
        this.riderRepository = riderRepository;
        this.adminRepository = adminRepository;
        this.distributorRepository = distributorRepository;

        this.supplierRepository = supplierRepository;
    }

    @Value("${SECRET_KEY}")
    private String SECRET_KEY;

    // Extracts Username
    public String decodeUsername(String token){
        return extractClaim(token, Claims::getSubject);
    }

    public String decodeUserId(String token){
        return extractClaim(token, Claims::getId);
    }

    public String extractFirstRole(String token) {
        List<SimpleGrantedAuthority> rolesClaim = extractClaim(token, claims ->
                ((List<?>) claims.get("roles")).stream()
                        .map(authority -> new SimpleGrantedAuthority(authority.toString()))
                        .collect(Collectors.toList())
        );
        if (!rolesClaim.isEmpty()) {
            String roleString = rolesClaim.get(0).getAuthority().split("=")[1];
            return roleString.substring(0, roleString.length() - 1);
        } else {
            return null;
        }
    }


    // Extract claims from token
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver){
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    // Makes new token
    public String genToken(UserDetails userDetails, String role){
        return generateToken(new HashMap<>(), userDetails, role);
    }

    // Makes new token
    public String generateToken(
            Map<String, Object> extraClaims,
            UserDetails userDetails,
            String role
    ){
        if (Objects.equals(role, "DISTRIBUTOR")){
            log.info("In distributor");
            Distributor distributor = distributorRepository.findDistributorByEmail(userDetails.getUsername());
            return getJwt(extraClaims, userDetails, distributor.getId(), distributor.getFirstName(), distributor.getLastName());
        }
        else if (Objects.equals(role, "SUPPLIER")){
            log.info("In supplier");
            Supplier supplier = supplierRepository.findSupplierByEmail(userDetails.getUsername());
            log.info("attempting get JWT");
            return getJwt(extraClaims, userDetails, supplier.getId(), supplier.getFirstName(), supplier.getLastName());
        }
        else if (Objects.equals(role, "RIDER")){
            log.info("In rider");
            Rider rider = riderRepository.findRiderByEmail(userDetails.getUsername());
            log.info("attempting get JWT");
            return getJwt(extraClaims, userDetails, rider.getId(), rider.getFirstName(), rider.getLastName());
        }
        else if (Objects.equals(role, "ADMIN")){
            log.info("In admin");
            Admin admin = adminRepository.findAdminByEmail(userDetails.getUsername());
            return getJwt(extraClaims, userDetails, admin.getId(), admin.getFirstName(), admin.getLastName());
        }
        else {
            log.info("In User");
            User user = userRepository.findUserByEmail(userDetails.getUsername());
            return getJwt(extraClaims, userDetails, user.getId(), user.getFirstName(), user.getLastName());
        }


    }

    private String getJwt(Map<String, Object> extraClaims, UserDetails userDetails, long id, String firstName, String lastName) {
        return Jwts.builder()
                .setClaims(extraClaims)
                .setSubject(userDetails.getUsername())
                .claim ("id", id)
                .claim("firstName", firstName)
                .claim("lastName", lastName)
                .claim("roles", userDetails.getAuthorities())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 5))
                .signWith(getSignInKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    // Validate token
    public boolean isTokenValid(String token, UserDetails userDetails){
        final String username = decodeUsername(token);
        return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
    }

    private boolean isTokenExpired(String token){
        return extractExpiration(token).before(new Date());
    }

    private Date extractExpiration(String token){
        return extractClaim(token, Claims::getExpiration);
    }

    private Claims extractAllClaims (String token){
        return Jwts
                .parserBuilder()
                .setSigningKey(getSignInKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    private Key getSignInKey() {
        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
        return Keys.hmacShaKeyFor(keyBytes);
    }
}

application.properties

 spring.datasource.url=jdbc:mysql://localhost:3306/ecommdb?sessionVariables=sql_mode='NO_ENGINE_SUBSTITUTION'&jdbcCompliantTruncation=false
 spring.datasource.username=user
 spring.datasource.password=user
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true

logging.level.org.springframework.web=TRACE

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true

spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=2KB
spring.servlet.multipart.max-file-size=200MB
spring.servlet.multipart.max-request-size=215MB

Application Config

package com.mootoholdings.ecommserver.config;


@Slf4j
@Configuration
public class ApplicationConfig {

    private final UserRepository userRepository;
    private final AdminRepository adminRepository;
    private final RiderRepository riderRepository;
    private final DistributorRepository distributorRepository;
    private final SupplierRepository supplierRepository;

    public ApplicationConfig(UserRepository userRepository, AdminRepository adminRepository,
            RiderRepository riderRepository, DistributorRepository distributorRepository,
            SupplierRepository supplierRepository) {
        this.userRepository = userRepository;
        this.adminRepository = adminRepository;
        this.riderRepository = riderRepository;
        this.distributorRepository = distributorRepository;
        this.supplierRepository = supplierRepository;
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            Optional<Rider> rider = riderRepository.findByEmail(username);
            if (rider.isPresent()) {
                log.info("authenticating rider");
                return new org.springframework.security.core.userdetails.User(rider.get().getEmail(),
                        rider.get().getPassword(), new ArrayList<>());
            }
            Optional<User> user = userRepository.findByEmail(username);
            if (user.isPresent()) {
                log.info("authenticating user");
                return new org.springframework.security.core.userdetails.User(user.get().getEmail(),
                        user.get().getPassword(), new ArrayList<>());
            }
            Optional<Admin> admin = adminRepository.findByEmail(username);
            if (admin.isPresent()) {
                log.info("authenticating admin");
                return new org.springframework.security.core.userdetails.User(admin.get().getEmail(),
                        admin.get().getPassword(), new ArrayList<>());
            }
            Optional<Distributor> distributor = distributorRepository.findByEmail(username);
            if (distributor.isPresent()) {
                log.info("authenticating dist");
                return new org.springframework.security.core.userdetails.User(distributor.get().getEmail(),
                        distributor.get().getPassword(), new ArrayList<>());
            }
            Optional<Supplier> supplier = supplierRepository.findByEmail(username);
            if (supplier.isPresent()) {
                log.info("authenticating supplier");
                return new org.springframework.security.core.userdetails.User(supplier.get().getEmail(),
                        supplier.get().getPassword(), new ArrayList<>());
            }
            throw new UsernameNotFoundException("User not found");
        };
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

folder structure FolderStructure

What could i be doing wrong?

UPDATE: Included package names. UPDATE: Run it on github codespaces and its working, so i guess its something to do with my machines setup


Solution

  • Did a complete debug and it boils down to the Laptop name. The original name has a space in it and so when it starts to search for repositories within the project it fails and cancels the search. To fix this i just created another account and moved the project to the Public folder cause im unable to change the laptop name, now everything works just fine.