diff --git a/.github/workflows/build-on-pull-request.yml b/.github/workflows/build-on-pull-request.yml
index 744b905..695b179 100644
--- a/.github/workflows/build-on-pull-request.yml
+++ b/.github/workflows/build-on-pull-request.yml
@@ -17,6 +17,7 @@ jobs:
uses: actions/setup-java@v2
with:
java-version: 17
- distribution: 'adopt'
+ distribution: 'zulu'
+
- name: Build with Maven
run: mvn clean install
diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index 5cc0ca0..769fce7 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -26,7 +26,7 @@ jobs:
uses: actions/setup-java@v2
with:
java-version: 17
- distribution: 'adopt'
+ distribution: 'zulu'
- name: Build with Maven
run: mvn clean install -DENV_VAR=${{ env.ENV_VAR }}
@@ -35,7 +35,7 @@ jobs:
run: mvn -B package --file pom.xml
- name: Upload WAR file as artifact
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: FHIR-API
path: target/fhirapi-v1.0.war
diff --git a/.github/workflows/sast.yml b/.github/workflows/sast.yml
index eb8d6b3..a886904 100644
--- a/.github/workflows/sast.yml
+++ b/.github/workflows/sast.yml
@@ -41,10 +41,10 @@ jobs:
uses: actions/setup-java@v2
with:
java-version: 17
- distribution: 'adopt'
+ distribution: 'zulu'
- name: Build with Maven
- run: mvn clean install -DENV_VAR=test
+ run: mvn clean install
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
diff --git a/pom.xml b/pom.xml
index a345c2c..22e8b75 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,10 +58,10 @@
org.springframework.boot
spring-boot-starter
-
- co.elastic.logging
- logback-ecs-encoder
- 1.3.2
+
+ co.elastic.logging
+ logback-ecs-encoder
+ 1.3.2
@@ -189,8 +189,7 @@
spring-boot-starter-mail
-
+
ca.uhn.hapi.fhir
hapi-fhir-structures-r4
@@ -198,8 +197,7 @@
-
+
ca.uhn.hapi.fhir
org.hl7.fhir.utilities
@@ -235,6 +233,27 @@
json-path
2.9.0
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.12.6
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.12.6
+ runtime
+
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.12.6
+ runtime
+
+
@@ -250,7 +269,7 @@
HTML
nvd
-
+
org.apache.maven.plugins
@@ -329,8 +348,7 @@
${target-properties} and
${source-properties}
-
diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties
index ed70c81..f3137e0 100644
--- a/src/main/environment/common_ci.properties
+++ b/src/main/environment/common_ci.properties
@@ -110,4 +110,5 @@ logging.level.com.iemr=DEBUG
logging.level.org.springframework=INFO
#ELK logging file name
-logging.file.name=@env.FHIR_API_LOGGING_FILE_NAME@
\ No newline at end of file
+logging.file.name=@env.FHIR_API_LOGGING_FILE_NAME@
+jwt.secret=@env.JWT_SECRET_KEY@
\ No newline at end of file
diff --git a/src/main/environment/common_dev.properties b/src/main/environment/common_dev.properties
index 3e012e9..6210f9e 100644
--- a/src/main/environment/common_dev.properties
+++ b/src/main/environment/common_dev.properties
@@ -107,3 +107,4 @@ logging.level.org.springframework.web=INFO
logging.level.org.hibernate=INFO
logging.level.com.iemr=DEBUG
logging.level.org.springframework=INFO
+jwt.secret=
diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties
index 803d225..0679bed 100644
--- a/src/main/environment/common_example.properties
+++ b/src/main/environment/common_example.properties
@@ -109,3 +109,4 @@ logging.level.org.springframework.web=INFO
logging.level.org.hibernate=INFO
logging.level.com.iemr=DEBUG
logging.level.org.springframework=INFO
+jwt.secret=
diff --git a/src/main/environment/common_test.properties b/src/main/environment/common_test.properties
index db18ad9..44e1523 100644
--- a/src/main/environment/common_test.properties
+++ b/src/main/environment/common_test.properties
@@ -108,3 +108,4 @@ logging.level.org.springframework.web=INFO
logging.level.org.hibernate=INFO
logging.level.com.iemr=DEBUG
logging.level.org.springframework=INFO
+jwt.secret=
diff --git a/src/main/java/com/wipro/fhir/FhirApiApplication.java b/src/main/java/com/wipro/fhir/FhirApiApplication.java
index 0395010..d732a3f 100644
--- a/src/main/java/com/wipro/fhir/FhirApiApplication.java
+++ b/src/main/java/com/wipro/fhir/FhirApiApplication.java
@@ -26,6 +26,12 @@
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import com.wipro.fhir.data.users.User;
@SpringBootApplication
public class FhirApiApplication {
@@ -33,4 +39,19 @@ public class FhirApiApplication {
public static void main(String[] args) {
SpringApplication.run(FhirApiApplication.class, args);
}
+
+ @Bean
+ public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
+ RedisTemplate template = new RedisTemplate<>();
+ template.setConnectionFactory(factory);
+
+ // Use StringRedisSerializer for keys (userId)
+ template.setKeySerializer(new StringRedisSerializer());
+
+ // Use Jackson2JsonRedisSerializer for values (Users objects)
+ Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(User.class);
+ template.setValueSerializer(serializer);
+
+ return template;
+ }
}
diff --git a/src/main/java/com/wipro/fhir/config/RedisConfig.java b/src/main/java/com/wipro/fhir/config/RedisConfig.java
new file mode 100644
index 0000000..d29e7c3
--- /dev/null
+++ b/src/main/java/com/wipro/fhir/config/RedisConfig.java
@@ -0,0 +1,40 @@
+package com.wipro.fhir.config;
+
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.session.data.redis.config.ConfigureRedisAction;
+
+import com.wipro.fhir.data.users.User;
+
+@Configuration
+@EnableCaching
+public class RedisConfig {
+
+ @Bean
+ public ConfigureRedisAction configureRedisAction() {
+ return ConfigureRedisAction.NO_OP;
+ }
+
+ @Bean
+ public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
+ RedisTemplate template = new RedisTemplate<>();
+ template.setConnectionFactory(factory);
+
+ // Use StringRedisSerializer for keys (userId)
+ template.setKeySerializer(new StringRedisSerializer());
+
+ // Use Jackson2JsonRedisSerializer for values (Users objects)
+ Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(User.class);
+ template.setValueSerializer(serializer);
+
+ return template;
+ }
+
+}
+
+
diff --git a/src/main/java/com/wipro/fhir/data/users/User.java b/src/main/java/com/wipro/fhir/data/users/User.java
new file mode 100644
index 0000000..c31049e
--- /dev/null
+++ b/src/main/java/com/wipro/fhir/data/users/User.java
@@ -0,0 +1,28 @@
+package com.wipro.fhir.data.users;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+@Entity
+@Table(name = "m_user")
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class User implements Serializable {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "UserID")
+ private Long userID;
+ @Column(name = "userName")
+ private String userName;
+ @Column(name = "Deleted", insertable = false, updatable = true)
+ private Boolean deleted;
+}
diff --git a/src/main/java/com/wipro/fhir/repo/user/UserLoginRepo.java b/src/main/java/com/wipro/fhir/repo/user/UserLoginRepo.java
new file mode 100644
index 0000000..1267ab6
--- /dev/null
+++ b/src/main/java/com/wipro/fhir/repo/user/UserLoginRepo.java
@@ -0,0 +1,16 @@
+package com.wipro.fhir.repo.user;
+
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.springframework.stereotype.Repository;
+
+import com.wipro.fhir.data.users.User;
+
+@Repository
+public interface UserLoginRepo extends CrudRepository {
+
+ @Query(" SELECT u FROM User u WHERE u.userID = :userID AND u.deleted = false ")
+ public User getUserByUserID(@Param("userID") Long userID);
+
+}
diff --git a/src/main/java/com/wipro/fhir/service/common/CommonServiceImpl.java b/src/main/java/com/wipro/fhir/service/common/CommonServiceImpl.java
index 0c87321..5fcf125 100644
--- a/src/main/java/com/wipro/fhir/service/common/CommonServiceImpl.java
+++ b/src/main/java/com/wipro/fhir/service/common/CommonServiceImpl.java
@@ -58,11 +58,13 @@
import com.wipro.fhir.data.mongo.care_context.NDHMResponse;
import com.wipro.fhir.data.mongo.care_context.Notification;
import com.wipro.fhir.data.mongo.care_context.PatientCareContexts;
+import com.wipro.fhir.data.mongo.care_context.PatientCareContextsStringOBJ;
import com.wipro.fhir.data.mongo.care_context.SMSNotify;
import com.wipro.fhir.data.patient.PatientDemographic;
import com.wipro.fhir.data.patient_data_handler.PatientDemographicModel_NDHM_Patient_Profile;
import com.wipro.fhir.data.request_handler.PatientEligibleForResourceCreation;
import com.wipro.fhir.data.request_handler.ResourceRequestHandler;
+import com.wipro.fhir.data.users.User;
import com.wipro.fhir.repo.common.PatientEligibleForResourceCreationRepo;
import com.wipro.fhir.repo.healthID.BenHealthIDMappingRepo;
import com.wipro.fhir.repo.mongo.amrit_resource.AMRIT_ResourceMongoRepo;
@@ -101,6 +103,12 @@ public class CommonServiceImpl implements CommonService {
private static String authKey;
private UUID uuid;
+
+ // public static String NDHM_AUTH_TOKEN;
+ // public static Long NDHM_TOKEN_EXP;
+ // public static String NDHM_OTP_TOKEN;
+
+
@Value("${clientID}")
private String clientID;
@@ -161,7 +169,8 @@ public String processResourceOperation() throws FHIRException {
String response = null;
// list of patient eligible for resource creation
List pList = getPatientListForResourceEligible();
- logger.info("No of records available to create FHIR in last 2 dagetPatientListForResourceEligibleys : " + pList.size());
+ logger.info("No of records available to create FHIR in last 2 dagetPatientListForResourceEligibleys : "
+ + pList.size());
ResourceRequestHandler resourceRequestHandler;
for (PatientEligibleForResourceCreation p : pList) {
@@ -290,6 +299,38 @@ public void addCareContextToMongo(PatientDemographic pDemo, PatientEligibleForRe
if (pDemo != null && pVisit != null) {
+
+// JsonObject jsnOBJ = new JsonObject();
+// JsonParser jsnParser = new JsonParser();
+// JsonElement jsnElmnt = jsnParser.parse(requestObj);
+// jsnOBJ = jsnElmnt.getAsJsonObject();
+
+ PatientCareContextsStringOBJ patientCareContextsStringOBJ = new PatientCareContextsStringOBJ();
+
+ // wrong variable name in request obj for benregid, need to correct in main
+ // request obj first
+// Long benID = null;
+// Long benRegID = null;
+// Long visitCode = null;
+//
+// if (jsnOBJ.has("beneficiaryID") && jsnOBJ.get("beneficiaryID") != null)
+// benRegID = jsnOBJ.get("beneficiaryID").getAsLong();
+// if (jsnOBJ.has("visitCode") && jsnOBJ.get("visitCode") != null)
+// visitCode = jsnOBJ.get("visitCode").getAsLong();
+// String healthID = jsnOBJ.get("healthID").getAsString();
+// String healthIDNumber = jsnOBJ.get("healthIdNumber").getAsString();
+// String visitCategory = jsnOBJ.get("visitCategory").getAsString();
+// String phoneNo;
+// String gender;
+// String yearOfBirth;
+// String name;
+// String email;
+
+ // get benid
+// if (benRegID != null)
+// benID = benHealthIDMappingRepo.getBenID(benRegID);
+
+
// fetch abdm facility id
logger.info("********t_benvisistData fetch request pvisit data :", pVisit);
@@ -299,6 +340,7 @@ public void addCareContextToMongo(PatientDemographic pDemo, PatientEligibleForRe
ArrayList ccList = new ArrayList<>();
CareContexts cc = new CareContexts();
+
logger.info("********t_benvisistData fetch response : {}", res);
cc.setReferenceNumber(pVisit.getVisitCode() != null ? pVisit.getVisitCode().toString() : null);
@@ -310,16 +352,22 @@ public void addCareContextToMongo(PatientDemographic pDemo, PatientEligibleForRe
cc.setCareContextLinkedDate(resData[1] != null ? resData[1].toString() : null);
}
+
logger.info("********data to be saved in mongo :", cc);
PatientCareContexts pcc;
+ PatientCareContexts resultSet = null;
+
+
+ logger.info("********data to be saved in mongo :", cc);
+ PatientCareContexts pcc1;
if (pDemo.getBeneficiaryID() != null) {
- pcc = patientCareContextsMongoRepo.findByIdentifier(pDemo.getBeneficiaryID().toString());
+ pcc1 = patientCareContextsMongoRepo.findByIdentifier(pDemo.getBeneficiaryID().toString());
- if (pcc != null && pcc.getIdentifier() != null) {
+ if (pcc1 != null && pcc1.getIdentifier() != null) {
// Get the existing careContextsList
- if (pcc.getCareContextsList() != null && pcc.getCareContextsList().size() > 0) {
- ccList = pcc.getCareContextsList();
+ if (pcc1.getCareContextsList() != null && pcc1.getCareContextsList().size() > 0) {
+ ccList = pcc1.getCareContextsList();
// Check if the visitCode is already in the careContextsList
for (CareContexts existingContext : ccList) {
@@ -330,8 +378,8 @@ public void addCareContextToMongo(PatientDemographic pDemo, PatientEligibleForRe
}
}
ccList.add(cc);
- pcc.setCareContextsList(ccList);
- patientCareContextsMongoRepo.save(pcc);
+ pcc1.setCareContextsList(ccList);
+ patientCareContextsMongoRepo.save(pcc1);
}
// }
// if (pcc != null && pcc.getIdentifier() != null) {
@@ -341,20 +389,20 @@ public void addCareContextToMongo(PatientDemographic pDemo, PatientEligibleForRe
// resultSet = patientCareContextsMongoRepo.save(pcc);
//
} else {
- pcc = new PatientCareContexts();
- pcc.setCaseReferenceNumber(pDemo.getBeneficiaryID().toString());
- pcc.setIdentifier(pDemo.getBeneficiaryID().toString());
+ pcc1 = new PatientCareContexts();
+ pcc1.setCaseReferenceNumber(pDemo.getBeneficiaryID().toString());
+ pcc1.setIdentifier(pDemo.getBeneficiaryID().toString());
if (pDemo.getGenderID() != null) {
switch (pDemo.getGenderID()) {
case 1:
- pcc.setGender("M");
+ pcc1.setGender("M");
break;
case 2:
- pcc.setGender("F");
+ pcc1.setGender("F");
break;
case 3:
- pcc.setGender("O");
+ pcc1.setGender("O");
break;
default:
@@ -362,19 +410,19 @@ public void addCareContextToMongo(PatientDemographic pDemo, PatientEligibleForRe
}
}
if (pDemo.getName() != null)
- pcc.setName(pDemo.getName());
+ pcc1.setName(pDemo.getName());
if (pDemo.getDOB() != null)
- pcc.setYearOfBirth(pDemo.getDOB().toString().split("-")[0]);
+ pcc1.setYearOfBirth(pDemo.getDOB().toString().split("-")[0]);
if (pDemo.getPreferredPhoneNo() != null)
- pcc.setPhoneNumber(pDemo.getPreferredPhoneNo());
+ pcc1.setPhoneNumber(pDemo.getPreferredPhoneNo());
if (pDemo.getHealthID() != null)
- pcc.setHealthId(pDemo.getHealthID());
+ pcc1.setHealthId(pDemo.getHealthID());
if (pDemo.getHealthIdNo() != null)
- pcc.setHealthNumber(pDemo.getHealthIdNo());
+ pcc1.setHealthNumber(pDemo.getHealthIdNo());
ccList.add(cc);
- pcc.setCareContextsList(ccList);
+ pcc1.setCareContextsList(ccList);
// save carecontext back to mongo
- patientCareContextsMongoRepo.save(pcc);
+ patientCareContextsMongoRepo.save(pcc1);
}
}
diff --git a/src/main/java/com/wipro/fhir/utils/CookieUtil.java b/src/main/java/com/wipro/fhir/utils/CookieUtil.java
new file mode 100644
index 0000000..3ccec9d
--- /dev/null
+++ b/src/main/java/com/wipro/fhir/utils/CookieUtil.java
@@ -0,0 +1,31 @@
+package com.wipro.fhir.utils;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+import org.springframework.stereotype.Service;
+
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+@Service
+public class CookieUtil {
+
+ public Optional getCookieValue(HttpServletRequest request, String cookieName) {
+ Cookie[] cookies = request.getCookies();
+ if (cookies != null) {
+ for (Cookie cookie : cookies) {
+ if (cookieName.equals(cookie.getName())) {
+ return Optional.of(cookie.getValue());
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ public String getJwtTokenFromCookie(HttpServletRequest request) {
+ return Arrays.stream(request.getCookies()).filter(cookie -> "Jwttoken".equals(cookie.getName()))
+ .map(Cookie::getValue).findFirst().orElse(null);
+ }
+}
diff --git a/src/main/java/com/wipro/fhir/utils/FilterConfig.java b/src/main/java/com/wipro/fhir/utils/FilterConfig.java
new file mode 100644
index 0000000..5a7ef36
--- /dev/null
+++ b/src/main/java/com/wipro/fhir/utils/FilterConfig.java
@@ -0,0 +1,19 @@
+package com.wipro.fhir.utils;
+
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class FilterConfig {
+
+ @Bean
+ public FilterRegistrationBean jwtUserIdValidationFilter(
+ JwtAuthenticationUtil jwtAuthenticationUtil) {
+ FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
+ registrationBean.setFilter(new JwtUserIdValidationFilter(jwtAuthenticationUtil));
+ registrationBean.addUrlPatterns("/*"); // Apply filter to all API endpoints
+ return registrationBean;
+ }
+
+}
diff --git a/src/main/java/com/wipro/fhir/utils/JwtAuthenticationUtil.java b/src/main/java/com/wipro/fhir/utils/JwtAuthenticationUtil.java
new file mode 100644
index 0000000..61cdc90
--- /dev/null
+++ b/src/main/java/com/wipro/fhir/utils/JwtAuthenticationUtil.java
@@ -0,0 +1,125 @@
+package com.wipro.fhir.utils;
+
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import com.wipro.fhir.data.users.User;
+import com.wipro.fhir.repo.user.UserLoginRepo;
+
+import io.jsonwebtoken.Claims;
+import jakarta.servlet.http.HttpServletRequest;
+
+@Component
+public class JwtAuthenticationUtil {
+
+ @Autowired
+ private CookieUtil cookieUtil;
+ @Autowired
+ private JwtUtil jwtUtil;
+ @Autowired
+ private RedisTemplate redisTemplate;
+ @Autowired
+ private UserLoginRepo userLoginRepo;
+ private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
+
+ public JwtAuthenticationUtil(CookieUtil cookieUtil, JwtUtil jwtUtil) {
+ this.cookieUtil = cookieUtil;
+ this.jwtUtil = jwtUtil;
+ }
+
+ public ResponseEntity validateJwtToken(HttpServletRequest request) {
+ Optional jwtTokenOpt = cookieUtil.getCookieValue(request, "Jwttoken");
+
+ if (jwtTokenOpt.isEmpty()) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
+ .body("Error 401: Unauthorized - JWT Token is not set!");
+ }
+
+ String jwtToken = jwtTokenOpt.get();
+
+ // Validate the token
+ Claims claims = jwtUtil.validateToken(jwtToken);
+ if (claims == null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Error 401: Unauthorized - Invalid JWT Token!");
+ }
+
+ // Extract username from token
+ String usernameFromToken = claims.getSubject();
+ if (usernameFromToken == null || usernameFromToken.isEmpty()) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
+ .body("Error 401: Unauthorized - Username is missing!");
+ }
+
+ // Return the username if valid
+ return ResponseEntity.ok(usernameFromToken);
+ }
+
+ public boolean validateUserIdAndJwtToken(String jwtToken) throws Exception {
+ try {
+ // Validate JWT token and extract claims
+ Claims claims = jwtUtil.validateToken(jwtToken);
+
+ if (claims == null) {
+ throw new Exception("Invalid JWT token.");
+ }
+
+ String userId = claims.get("userId", String.class);
+
+ // Check if user data is present in Redis
+ User user = getUserFromCache(userId);
+ if (user == null) {
+ // If not in Redis, fetch from DB and cache the result
+ user = fetchUserFromDB(userId);
+ }
+ if (user == null) {
+ throw new Exception("Invalid User ID.");
+ }
+
+ return true; // Valid userId and JWT token
+ } catch (Exception e) {
+ logger.error("Validation failed: " + e.getMessage(), e);
+ throw new Exception("Validation error: " + e.getMessage(), e);
+ }
+ }
+
+ private User getUserFromCache(String userId) {
+ String redisKey = "user_" + userId; // The Redis key format
+ User user = (User) redisTemplate.opsForValue().get(redisKey);
+
+ if (user == null) {
+ logger.warn("User not found in Redis. Will try to fetch from DB.");
+ } else {
+ logger.info("User fetched successfully from Redis.");
+ }
+
+ return user; // Returns null if not found
+ }
+
+ private User fetchUserFromDB(String userId) {
+ // This method will only be called if the user is not found in Redis.
+ String redisKey = "user_" + userId; // Redis key format
+
+ // Fetch user from DB
+ User user = userLoginRepo.getUserByUserID(Long.parseLong(userId));
+
+ if (user != null) {
+ // Cache the user in Redis for future requests (cache for 30 minutes)
+ redisTemplate.opsForValue().set(redisKey, user, 30, TimeUnit.MINUTES);
+
+ // Log that the user has been stored in Redis
+ logger.info("User stored in Redis with key: " + redisKey);
+ } else {
+ logger.warn("User not found for userId: " + userId);
+ }
+
+ return user;
+ }
+}
diff --git a/src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java b/src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java
new file mode 100644
index 0000000..09ae81f
--- /dev/null
+++ b/src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java
@@ -0,0 +1,111 @@
+package com.wipro.fhir.utils;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+@Component
+public class JwtUserIdValidationFilter implements Filter {
+
+ private final JwtAuthenticationUtil jwtAuthenticationUtil;
+ private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
+
+ public JwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil) {
+ this.jwtAuthenticationUtil = jwtAuthenticationUtil;
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+ throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+ String path = request.getRequestURI();
+ String contextPath = request.getContextPath();
+ logger.info("JwtUserIdValidationFilter invoked for path: " + path);
+
+ // Log cookies for debugging
+ Cookie[] cookies = request.getCookies();
+ if (cookies != null) {
+ for (Cookie cookie : cookies) {
+ if ("userId".equals(cookie.getName())) {
+ logger.warn("userId found in cookies! Clearing it...");
+ clearUserIdCookie(response); // Explicitly remove userId cookie
+ }
+ }
+ } else {
+ logger.info("No cookies found in the request");
+ }
+
+ // Log headers for debugging
+ String jwtTokenFromHeader = request.getHeader("Jwttoken");
+ logger.info("JWT token from header: ");
+
+ // Skip login and public endpoints
+ if (path.equals(contextPath + "/user/userAuthenticate")
+ || path.equalsIgnoreCase(contextPath + "/user/logOutUserFromConcurrentSession")
+ || path.startsWith(contextPath + "/public")) {
+ logger.info("Skipping filter for path: " + path);
+ filterChain.doFilter(servletRequest, servletResponse);
+ return;
+ }
+
+ try {
+ // Retrieve JWT token from cookies
+ String jwtTokenFromCookie = getJwtTokenFromCookies(request);
+ logger.info("JWT token from cookie: ");
+
+ // Determine which token (cookie or header) to validate
+ String jwtToken = jwtTokenFromCookie != null ? jwtTokenFromCookie : jwtTokenFromHeader;
+ if (jwtToken == null) {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "JWT token not found in cookies or headers");
+ return;
+ }
+
+ // Validate JWT token and userId
+ boolean isValid = jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtToken);
+
+ if (isValid) {
+ // If token is valid, allow the request to proceed
+ filterChain.doFilter(servletRequest, servletResponse);
+ } else {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid JWT token");
+ }
+ } catch (Exception e) {
+ logger.error("Authorization error: ", e);
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization error: " + e.getMessage());
+ }
+ }
+
+ private String getJwtTokenFromCookies(HttpServletRequest request) {
+ Cookie[] cookies = request.getCookies();
+ if (cookies != null) {
+ for (Cookie cookie : cookies) {
+ if (cookie.getName().equals("Jwttoken")) {
+ return cookie.getValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ private void clearUserIdCookie(HttpServletResponse response) {
+ Cookie cookie = new Cookie("userId", null);
+ cookie.setPath("/");
+ cookie.setHttpOnly(true);
+ cookie.setSecure(true);
+ cookie.setMaxAge(0); // Invalidate the cookie
+ response.addCookie(cookie);
+ }
+}
diff --git a/src/main/java/com/wipro/fhir/utils/JwtUtil.java b/src/main/java/com/wipro/fhir/utils/JwtUtil.java
new file mode 100644
index 0000000..6f22eb7
--- /dev/null
+++ b/src/main/java/com/wipro/fhir/utils/JwtUtil.java
@@ -0,0 +1,68 @@
+package com.wipro.fhir.utils;
+
+import java.security.Key;
+import java.util.Date;
+import java.util.function.Function;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+
+@Component
+public class JwtUtil {
+
+ @Value("${jwt.secret}")
+ private String SECRET_KEY;
+
+ private static final long EXPIRATION_TIME = 24L * 60 * 60 * 1000; // 1 day in milliseconds
+
+ // Generate a key using the secret
+ private Key getSigningKey() {
+ if (SECRET_KEY == null || SECRET_KEY.isEmpty()) {
+ throw new IllegalStateException("JWT secret key is not set in application.properties");
+ }
+ return Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
+ }
+
+ // Generate JWT Token
+ public String generateToken(String username, String userId) {
+ Date now = new Date();
+ Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);
+
+ // Include the userId in the JWT claims
+ return Jwts.builder().setSubject(username).claim("userId", userId) // Add userId as a claim
+ .setIssuedAt(now).setExpiration(expiryDate).signWith(getSigningKey(), SignatureAlgorithm.HS256)
+ .compact();
+ }
+
+ // Validate and parse JWT Token
+ public Claims validateToken(String token) {
+ try {
+ // Use the JwtParserBuilder correctly in version 0.12.6
+ return Jwts.parser() // Correct method in 0.12.6 to get JwtParserBuilder
+ .setSigningKey(getSigningKey()) // Set the signing key
+ .build() // Build the JwtParser
+ .parseClaimsJws(token) // Parse and validate the token
+ .getBody();
+ } catch (Exception e) {
+ return null; // Handle token parsing/validation errors
+ }
+ }
+
+ public String extractUsername(String token) {
+ return extractClaim(token, Claims::getSubject);
+ }
+
+ public T extractClaim(String token, Function claimsResolver) {
+ final Claims claims = extractAllClaims(token);
+ return claimsResolver.apply(claims);
+ }
+
+ private Claims extractAllClaims(String token) {
+ return Jwts.parser().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
+ }
+}