diff --git a/pom.xml b/pom.xml
index ea04cde..2f65b9c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.1.3
+ 3.0.1
com.example
@@ -14,7 +14,7 @@
holiday-planner
Holiday Planner
- 16
+ 17
@@ -26,6 +26,7 @@
org.springframework.boot
spring-boot-starter-web
+ 3.1.3
@@ -52,21 +53,23 @@
- io.springfox
- springfox-swagger2
- 3.0.0
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.0.2
+
- io.springfox
- springfox-swagger-ui
- 3.0.0
+ org.springdoc
+ springdoc-openapi-security
+ 1.7.0
- io.springfox
- springfox-boot-starter
- 3.0.0
+ javax.servlet
+ javax.servlet-api
+ 4.0.1
+ provided
@@ -129,12 +132,6 @@
runtime
-
- org.springframework.data
- spring-data-mongodb
- 3.3.0
-
-
org.mongodb
diff --git a/src/main/java/com/example/holidayplanner/HolidayPlannerApplication.java b/src/main/java/com/example/holidayplanner/HolidayPlannerApplication.java
index 3317d36..2d502b9 100644
--- a/src/main/java/com/example/holidayplanner/HolidayPlannerApplication.java
+++ b/src/main/java/com/example/holidayplanner/HolidayPlannerApplication.java
@@ -1,5 +1,6 @@
package com.example.holidayplanner;
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@@ -7,7 +8,7 @@
@SpringBootApplication()
@EnableScheduling
-@EnableMongoRepositories
+@OpenAPIDefinition
public class HolidayPlannerApplication {
public static void main(String[] args) {
diff --git a/src/main/java/com/example/holidayplanner/availableDates/AvailableDatesController.java b/src/main/java/com/example/holidayplanner/availableDates/AvailableDatesController.java
index 59babf7..ad9d8cf 100644
--- a/src/main/java/com/example/holidayplanner/availableDates/AvailableDatesController.java
+++ b/src/main/java/com/example/holidayplanner/availableDates/AvailableDatesController.java
@@ -1,9 +1,9 @@
package com.example.holidayplanner.availableDates;
import com.fasterxml.jackson.core.JsonProcessingException;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
+import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
@@ -15,7 +15,7 @@
@RestController
@RequestMapping("/api/v1.0/availabledates")
-@Api(tags = "Available Dates")
+@Tag(name = "Available Dates")
@SecurityRequirement(name = "holidayPlannerSecurity")
public class AvailableDatesController {
@@ -28,7 +28,7 @@ public AvailableDatesController(AvailableDatesService availableDatesService) {
@PostMapping(path = "/findmultiplebyid")
- @ApiOperation(value = "Find multiple available dates by id")
+ @Operation(summary = "Find multiple available dates by id")
public ResponseEntity findMultipleById(@RequestBody List availableDatesIds) throws JsonProcessingException {
return availableDatesService.findMultipleById(availableDatesIds);
}
diff --git a/src/main/java/com/example/holidayplanner/budget/BudgetController.java b/src/main/java/com/example/holidayplanner/budget/BudgetController.java
index 445ed9f..5f05eba 100644
--- a/src/main/java/com/example/holidayplanner/budget/BudgetController.java
+++ b/src/main/java/com/example/holidayplanner/budget/BudgetController.java
@@ -1,9 +1,9 @@
package com.example.holidayplanner.budget;
import com.fasterxml.jackson.core.JsonProcessingException;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
+import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
@@ -15,7 +15,7 @@
@RestController
@RequestMapping("/api/v1.0/budgets")
-@Api(tags = "Budgets")
+@Tag(name = "Budgets")
@SecurityRequirement(name = "holidayPlannerSecurity")
public class BudgetController {
@@ -27,7 +27,7 @@ public BudgetController(BudgetService budgetService) {
}
@PostMapping(path = "/findmultiplebyid")
- @ApiOperation(value = "Find multiple budgets by id")
+ @Operation(summary = "Find multiple budgets by id")
public ResponseEntity findMultipleById(@RequestBody List budgetIds) throws JsonProcessingException {
return budgetService.findMultipleById(budgetIds);
}
diff --git a/src/main/java/com/example/holidayplanner/config/SecurityConfigurer.java b/src/main/java/com/example/holidayplanner/config/SecurityConfigurer.java
index 9044d19..a45ed72 100644
--- a/src/main/java/com/example/holidayplanner/config/SecurityConfigurer.java
+++ b/src/main/java/com/example/holidayplanner/config/SecurityConfigurer.java
@@ -1,7 +1,7 @@
package com.example.holidayplanner.config;
import com.example.holidayplanner.config.jwt.JwtRequestFilter;
-import org.apache.catalina.filters.CorsFilter;
+import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -15,6 +15,9 @@
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.session.SessionManagementFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
import static org.springframework.security.config.Customizer.withDefaults;
@@ -27,35 +30,21 @@ public class SecurityConfigurer {
@Autowired
private JwtRequestFilter jwtRequestFilter;
-// @Override
-// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-// auth.userDetailsService(myUserDetailsService);
-// }
-
@Bean
public SecurityFilterChain configure (HttpSecurity http) throws Exception {
return http.cors(withDefaults())
.csrf((csrf) -> csrf.disable())
.authorizeHttpRequests((authorize) -> authorize
- .requestMatchers("/api/v1.0/users/newuser", "/api/v1.0/users/login", "/", "/csrf", "/v3/api-docs",
+ .requestMatchers("/api/v1.0/users/newuser", "/api/v1.0/users/login", "/", "/csrf",
"/swagger-resources/configuration/ui", "/configuration/ui",
- "/swagger-resources", "/swagger-resources/configuration/security",
- "/configuration/security", "/swagger-ui/**", "/v2/api-docs/**", "/webjars/**", "/swagger-ui.html").permitAll())
+ "/swagger-resources/**", "/swagger-resources/configuration/security",
+ "/configuration/security", "configuration/**", "/swagger-ui/**", "/v3/api-docs/**", "/webjars/**", "/swagger-ui.html").permitAll()
+ .anyRequest().authenticated())
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore( jwtRequestFilter, UsernamePasswordAuthenticationFilter.class )
.addFilterBefore(corsFilter(), SessionManagementFilter.class).build();
}
-// @Override
-// public void configure(WebSecurity web) throws Exception {
-// web.ignoring().antMatchers("/api/v1.0/users/newuser");
-// web.ignoring().antMatchers("/api/v1.0/users/login");
-// web.ignoring().antMatchers("/", "/csrf", "/v3/api-docs",
-// "/swagger-resources/configuration/ui", "/configuration/ui",
-// "/swagger-resources", "/swagger-resources/configuration/security",
-// "/configuration/security", "/swagger-ui/**", "/v2/api-docs/**", "/webjars/**", "/swagger-ui.html");
-// }
-
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
@@ -68,7 +57,12 @@ public AuthenticationManager authenticationManagerBean(final AuthenticationConfi
@Bean
CorsFilter corsFilter() {
- CorsFilter corsFilter = new CorsFilter();
+ CorsFilter corsFilter = new CorsFilter(new CorsConfigurationSource() {
+ @Override
+ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
+ return null;
+ }
+ });
return corsFilter;
}
}
diff --git a/src/main/java/com/example/holidayplanner/config/SwaggerConfig.java b/src/main/java/com/example/holidayplanner/config/SwaggerConfig.java
index 88996ad..85f7bb9 100644
--- a/src/main/java/com/example/holidayplanner/config/SwaggerConfig.java
+++ b/src/main/java/com/example/holidayplanner/config/SwaggerConfig.java
@@ -1,71 +1,31 @@
package com.example.holidayplanner.config;
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-import springfox.documentation.builders.ApiInfoBuilder;
-import springfox.documentation.builders.PathSelectors;
-import springfox.documentation.builders.RequestHandlerSelectors;
-import springfox.documentation.service.*;
-import springfox.documentation.spi.DocumentationType;
-import springfox.documentation.spi.service.contexts.SecurityContext;
-import springfox.documentation.spring.web.plugins.Docket;
-import springfox.documentation.swagger2.annotations.EnableSwagger2;
-
-import java.time.LocalDate;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
@Configuration
-@EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer {
@Bean
- public Docket api() {
- return new Docket(DocumentationType.SWAGGER_2)
- .apiInfo(apiInfo())
- .securityContexts(Arrays.asList(securityContext()))
- .securitySchemes(Arrays.asList(apiKey()))
- .pathMapping("/")
- .select()
- .apis(RequestHandlerSelectors.any())
- .paths(PathSelectors.any())
- .build()
- .tags(new Tag("User", "All User Endpoints"), new Tag("Group", "All Group Endpoints"), new Tag("Holiday", "All Holiday Endpoints"))
-
- .directModelSubstitute(LocalDate.class, String.class)
- .genericModelSubstitutes(ResponseEntity.class)
- ;
- }
-
- ApiInfo apiInfo() {
- return new ApiInfoBuilder()
- .title("Holiday Planner")
- .version("1.0.0")
- .description("Swagger page for the Holiday Planner Monolith")
- .build()
- ;
- }
-
- @Override
- public void addViewControllers(final ViewControllerRegistry registry) {
- registry.addRedirectViewController("/", "/swagger-ui/#/");
- }
-
- private ApiKey apiKey () {
- return new ApiKey("Access Token", "Authorization", SecurityScheme.In.HEADER.name());
- }
-
- private SecurityContext securityContext() {
- return SecurityContext.builder().securityReferences(securityReference()).build();
- }
-
- private List securityReference() {
- AuthorizationScope[] authorizationScope = { new AuthorizationScope("global", "Empty Description")};
- return Collections.singletonList(new SecurityReference("Access Token", authorizationScope));
+ public OpenAPI springShopOpenAPI() {
+ return new OpenAPI().info(new Info().title("GetAway")
+ .description("Api documentation for GetAway")
+ .version("v0.01")
+ .license(new License()
+ .name("Apache 2.0").url("http://springdoc.org")
+ ))
+ .components(new Components().addSecuritySchemes("Api Key", new SecurityScheme()
+ .type(SecurityScheme.Type.APIKEY)
+ .scheme("bearer")
+ .bearerFormat("jwt")
+ .in(SecurityScheme.In.HEADER)
+ .name("Authorization")
+ )).addSecurityItem(new SecurityRequirement().addList("Api Key"));
}
}
diff --git a/src/main/java/com/example/holidayplanner/config/jwt/JwtRequestFilter.java b/src/main/java/com/example/holidayplanner/config/jwt/JwtRequestFilter.java
index ac2bf74..e50bfa9 100644
--- a/src/main/java/com/example/holidayplanner/config/jwt/JwtRequestFilter.java
+++ b/src/main/java/com/example/holidayplanner/config/jwt/JwtRequestFilter.java
@@ -16,6 +16,7 @@
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
+import java.util.Date;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@@ -23,12 +24,13 @@ public class JwtRequestFilter extends OncePerRequestFilter {
private MyUserDetailsService myUserDetailsService;
@Autowired
- private JwtUtil jwtUtil;
+ private final JwtUtil jwtUtil;
@Autowired
private final TokenService tokenService;
- public JwtRequestFilter(TokenService tokenService) {
+ public JwtRequestFilter(JwtUtil jwtUtil, TokenService tokenService) {
+ this.jwtUtil = jwtUtil;
this.tokenService = tokenService;
}
@@ -38,31 +40,33 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
final String authorizationHeader = request.getHeader("Authorization");
String jwt = null;
- String email = null;
+// String email = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
- email = jwtUtil.extractEmail(jwt);
+// email = jwtUtil.extractEmail(jwt);
}
- if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
- UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(email);
-
+ if (jwt != null && SecurityContextHolder.getContext().getAuthentication() == null) {
Token token = tokenService.findByAccessToken(jwt);
- if (!jwtUtil.validateAccessToken(token.getAccessToken(), userDetails) && jwtUtil.validateRefreshToken(token.getRefreshToken(), userDetails)) {
+ UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(token.getOwner().getEmail());
+
+ if (!jwtUtil.validateAccessToken(token.getAccessToken()) && token.getRefreshTokenExpiration().after(new Date())) {
String newAccessToken = jwtUtil.generateToken(userDetails);
String newRefreshToken = jwtUtil.generateRefreshToken(userDetails);
token.setAccessToken(newAccessToken);
+ token.setAccessTokenExpiration(jwtUtil.extractExpiration(newAccessToken));
token.setRefreshToken(newRefreshToken);
+ token.setRefreshTokenExpiration(jwtUtil.extractExpiration(newRefreshToken));
tokenService.saveToken(token);
response.setHeader("NewAccessToken", newAccessToken);
}
- if (jwtUtil.validateAccessToken(token.getAccessToken(), userDetails)) {
+ if (jwtUtil.validateAccessToken(token.getAccessToken())) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
diff --git a/src/main/java/com/example/holidayplanner/config/jwt/JwtUtil.java b/src/main/java/com/example/holidayplanner/config/jwt/JwtUtil.java
index eaae0b6..a076a58 100644
--- a/src/main/java/com/example/holidayplanner/config/jwt/JwtUtil.java
+++ b/src/main/java/com/example/holidayplanner/config/jwt/JwtUtil.java
@@ -1,9 +1,8 @@
package com.example.holidayplanner.config.jwt;
-import io.jsonwebtoken.Claims;
-import io.jsonwebtoken.Jwts;
-import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
+import io.jsonwebtoken.security.SignatureException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
@@ -37,14 +36,12 @@ public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
- public Boolean validateAccessToken(String token, UserDetails userDetails) {
- final String userName = extractEmail(token);
- return (userName.equals(userDetails.getUsername()) && !isTokenExpired(token));
+ public Boolean validateAccessToken(String accessToken) {
+ return (isTokenValid(accessToken) && !isTokenExpired(accessToken));
}
- public Boolean validateRefreshToken(String token, UserDetails userDetails) {
- final String userName = extractEmail(token);
- return (userName.equals(userDetails.getUsername()) && !isTokenExpired(token));
+ public Boolean validateRefreshToken(String refeshToken) {
+ return (isTokenValid(refeshToken) && !isTokenExpired(refeshToken));
}
public String generateRefreshToken(UserDetails userDetails) {
@@ -68,7 +65,7 @@ private String createToken(Map claims, UserDetails userDetails)
return Jwts.builder().setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt( new Date(System.currentTimeMillis()) )
- .setExpiration( new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(12)) )
+ .setExpiration( new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(60)) )
.signWith(secretKey).compact();
}
@@ -76,5 +73,16 @@ private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
+ private Boolean isTokenValid(String token) {
+ try {
+ Jwts.parserBuilder()
+ .setSigningKey(secretKey)
+ .build()
+ .parseClaimsJws(token);
+ return true;
+ } catch (ExpiredJwtException | MalformedJwtException | SignatureException exception) {
+ return false;
+ }
+ }
}
diff --git a/src/main/java/com/example/holidayplanner/config/jwt/token/Token.java b/src/main/java/com/example/holidayplanner/config/jwt/token/Token.java
index 8ba260f..8f25570 100644
--- a/src/main/java/com/example/holidayplanner/config/jwt/token/Token.java
+++ b/src/main/java/com/example/holidayplanner/config/jwt/token/Token.java
@@ -1,10 +1,15 @@
package com.example.holidayplanner.config.jwt.token;
+import com.example.holidayplanner.config.cascadeSaveMongoEventListener.CascadeSave;
+import com.example.holidayplanner.user.User;
import lombok.Data;
+import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.FieldType;
import org.springframework.data.mongodb.core.mapping.MongoId;
+import java.util.Date;
+
@Document(collection = "Tokens")
@Data
public class Token {
@@ -12,9 +17,15 @@ public class Token {
@MongoId(value = FieldType.OBJECT_ID)
private String id;
- private String userId;
+ @DBRef
+ @CascadeSave
+ private User owner;
private String accessToken;
private String refreshToken;
+
+ private Date accessTokenExpiration;
+
+ private Date refreshTokenExpiration;
}
diff --git a/src/main/java/com/example/holidayplanner/config/jwt/token/TokenRepository.java b/src/main/java/com/example/holidayplanner/config/jwt/token/TokenRepository.java
index e5060ec..04365d1 100644
--- a/src/main/java/com/example/holidayplanner/config/jwt/token/TokenRepository.java
+++ b/src/main/java/com/example/holidayplanner/config/jwt/token/TokenRepository.java
@@ -1,9 +1,10 @@
package com.example.holidayplanner.config.jwt.token;
+import com.example.holidayplanner.user.User;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface TokenRepository extends MongoRepository {
- Token findByUserId(String userId);
+ Token findByOwner(User user);
Token findByAccessToken(String accessToken);
diff --git a/src/main/java/com/example/holidayplanner/config/jwt/token/TokenService.java b/src/main/java/com/example/holidayplanner/config/jwt/token/TokenService.java
index 30b7dc9..72413fd 100644
--- a/src/main/java/com/example/holidayplanner/config/jwt/token/TokenService.java
+++ b/src/main/java/com/example/holidayplanner/config/jwt/token/TokenService.java
@@ -1,5 +1,6 @@
package com.example.holidayplanner.config.jwt.token;
+import com.example.holidayplanner.user.User;
import org.springframework.stereotype.Service;
@Service
public class TokenService {
@@ -13,8 +14,8 @@ public Token saveToken(Token token) {
return tokenRepository.save(token);
}
- public Token findByUserId(String userId) {
- return tokenRepository.findByUserId(userId);
+ public Token findByUserId(User user) {
+ return tokenRepository.findByOwner(user);
}
public Token findByAccessToken(String accessToken) {
diff --git a/src/main/java/com/example/holidayplanner/group/GroupController.java b/src/main/java/com/example/holidayplanner/group/GroupController.java
index 735ad10..2b828eb 100644
--- a/src/main/java/com/example/holidayplanner/group/GroupController.java
+++ b/src/main/java/com/example/holidayplanner/group/GroupController.java
@@ -2,9 +2,9 @@
import com.example.holidayplanner.interfaces.ControllerInterface;
import com.fasterxml.jackson.core.JsonProcessingException;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
+import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
@@ -15,7 +15,7 @@
@RestController
@RequestMapping(path = "/api/v1.0/groups")
-@Api(tags = "Group")
+@Tag(name = "Group")
@SecurityRequirement(name = "holidayPlannerSecurity")
public class GroupController implements ControllerInterface {
@@ -57,37 +57,37 @@ public String removeGroupMember(@PathVariable("groupId") String groupId, @Reques
@PostMapping(path = "/findmultiplebyid")
- @ApiOperation(value = "Find multiple groups by their ids")
+ @Operation(summary = "Find multiple groups by their ids")
public ResponseEntity