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.
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();
}
}
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
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.