diff --git a/03.microservices/currency-conversion-service/pom.xml b/03.microservices/currency-conversion-service/pom.xml index a3202e3..0e87c2b 100644 --- a/03.microservices/currency-conversion-service/pom.xml +++ b/03.microservices/currency-conversion-service/pom.xml @@ -28,6 +28,21 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-security + + + + com.google.guava + guava + 19.0 + + org.springframework.cloud spring-cloud-starter-config @@ -56,7 +71,11 @@ org.springframework.amqp spring-rabbit - + + + org.springframework.kafka + spring-kafka + org.springframework.boot @@ -69,6 +88,30 @@ spring-boot-starter-test test + + + org.springframework.kafka + spring-kafka-test + test + + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + @@ -89,6 +132,14 @@ org.springframework.boot spring-boot-maven-plugin + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java index ee56f4b..7c51534 100644 --- a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionController.java @@ -5,18 +5,22 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController +@RequestMapping("/api") public class CurrencyConversionController { @Autowired private CurrencyExchangeProxy proxy; @GetMapping("/currency-conversion/from/{from}/to/{to}/quantity/{quantity}") + @PreAuthorize("hasAnyRole('ROLE_STUDENT')") public CurrencyConversion calculateCurrencyConversion( @PathVariable String from, @PathVariable String to, @@ -42,7 +46,8 @@ public CurrencyConversion calculateCurrencyConversion( } @GetMapping("/currency-conversion-feign/from/{from}/to/{to}/quantity/{quantity}") - public CurrencyConversion calculateCurrencyConversionFeign( + @PreAuthorize("hasAuthority('course:write')") + public CurrencyConversion calculateCurrencyConversionFeign( @PathVariable String from, @PathVariable String to, @PathVariable BigDecimal quantity diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java index e2d7f6a..7aa54f5 100644 --- a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/CurrencyConversionServiceApplication.java @@ -1,8 +1,11 @@ package com.in28minutes.microservices.currencyconversionservice; +import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.kafka.core.KafkaTemplate; @SpringBootApplication @EnableFeignClients @@ -12,4 +15,8 @@ public static void main(String[] args) { SpringApplication.run(CurrencyConversionServiceApplication.class, args); } + @Bean + CommandLineRunner commandLineRunner(KafkaTemplate kafkaTemplate){ + return args -> {kafkaTemplate.send("xecTest","hello xec from kafka");}; + } } diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaConsumerConfig.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaConsumerConfig.java new file mode 100644 index 0000000..1d64143 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaConsumerConfig.java @@ -0,0 +1,58 @@ +package com.in28minutes.microservices.currencyconversionservice.Message; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.config.KafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaConsumerConfig { + + @Value("localhost:9092") + private String bootstrapServers; + + public Map consumerConfig() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.GROUP_ID_CONFIG, "hello"); + return props; + } + + + //creating consumer instances + @Bean + public ConsumerFactory consumerFactory() { + return new DefaultKafkaConsumerFactory<>((Map) consumerConfig()); + } + + //listerner container + @Bean + public KafkaListenerContainerFactory> factory( + ConsumerFactory consumerFactory + ) { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory); + return factory; + } + + public KafkaConsumer getConsumer() { + KafkaConsumer consumer = new KafkaConsumer<>(consumerConfig()); + consumer.subscribe(Collections.singletonList("currencyConversion")); + return consumer; + } +} \ No newline at end of file diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaProducerConfig.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaProducerConfig.java new file mode 100644 index 0000000..a455b8a --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaProducerConfig.java @@ -0,0 +1,42 @@ +package com.in28minutes.microservices.currencyconversionservice.Message; + +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.config.ConfigDef; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaProducerConfig { + + @Value("${spring.kafka.bootstrap-servers}") + private String bootstrapServers; + + public Map producerConfig(){ + Map props=new HashMap<>(); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,bootstrapServers); + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + return props; + } + + + //creating producer instances + @Bean + public ProducerFactory producerFactory(){ + return new DefaultKafkaProducerFactory<>(producerConfig()); + } + + //send messages + @Bean + public KafkaTemplate kafkaTemplate(ProducerFactory producerFactory){ + return new KafkaTemplate<>(producerFactory); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaTopicConfig.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaTopicConfig.java new file mode 100644 index 0000000..d658088 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/KafkaTopicConfig.java @@ -0,0 +1,20 @@ +package com.in28minutes.microservices.currencyconversionservice.Message; + +import org.apache.kafka.clients.admin.NewTopic; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.TopicBuilder; + +@Configuration +public class KafkaTopicConfig { + + @Bean + public NewTopic xecTopic(){ + return TopicBuilder.name("xecTest").build(); + } + + @Bean + public NewTopic currencyConversion(){ + return TopicBuilder.name("currencyConversion").build(); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/ConsumerSuperThread.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/ConsumerSuperThread.java new file mode 100644 index 0000000..27b7ea0 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/ConsumerSuperThread.java @@ -0,0 +1,68 @@ +package com.in28minutes.microservices.currencyconversionservice.Message.api; + +import com.in28minutes.microservices.currencyconversionservice.CurrencyConversion; +import com.in28minutes.microservices.currencyconversionservice.Message.KafkaConsumerConfig; +import io.jsonwebtoken.lang.Collections; +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.apache.kafka.clients.consumer.ConsumerRecords; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +@Component +public class ConsumerSuperThread extends Thread { + + public ConsumerSuperThread() { + } + + Logger logger= LoggerFactory.getLogger(this.getClass()); + + @Autowired + private KafkaConsumerConfig kafkaConsumerConfig; + + public Map MY_MAP=new HashMap<>(); + + public void triggerConsumerData(String from,String to,BigDecimal quantity){ + if(Collections.isEmpty(MY_MAP)) { + this.start(); + }else { + BigDecimal multiple = BigDecimal.valueOf(Long.parseLong(MY_MAP.get("multiply"))); + + CurrencyConversion hello_from_messaging_kafka = new CurrencyConversion(1L, + from, to, quantity, + multiple, + quantity.multiply(multiple), + "Hello from Messaging kafka"); + System.out.println(hello_from_messaging_kafka); + MY_MAP.clear(); + } + } + @Override + public void run() { + BigDecimal multiple = BigDecimal.ZERO; + KafkaConsumer consumer = kafkaConsumerConfig.getConsumer(); + try { + while (MY_MAP.isEmpty()) { + ConsumerRecords records = consumer.poll(Duration.ofSeconds(5)); + for (ConsumerRecord record : records) + { + logger.info("topic = %s, partition = %d, offset = %d, multipleKey = %s multipleValue = %s", + record.topic(), record.partition(), record.offset(), + record.key(), record.value()); + + MY_MAP.put("multiply", record.value().split("-")[0]); + } + } + } finally { + consumer.close(); + } + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/CurrencyConversionKafkaController.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/CurrencyConversionKafkaController.java new file mode 100644 index 0000000..0b11ac8 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/CurrencyConversionKafkaController.java @@ -0,0 +1,46 @@ +package com.in28minutes.microservices.currencyconversionservice.Message.api; + +import com.in28minutes.microservices.currencyconversionservice.CurrencyExchangeProxy; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.concurrent.ExecutionException; + +@RestController + @RequestMapping("/app") + public class CurrencyConversionKafkaController { + @Autowired + private CurrencyExchangeProxy proxy; + @Autowired + private KafkaTemplate kafkaTemplate; + + @Autowired + private ConsumerSuperThread consumerSuperThread; + + @GetMapping("/currency-conversion-feign/from/{from}/to/{to}/quantity/{quantity}") + @PreAuthorize("hasAuthority('course:write')") + public String calculateCurrencyConversionKafka( + @PathVariable String from, + @PathVariable String to, + @PathVariable BigDecimal quantity + ) throws ExecutionException, InterruptedException { + kafkaTemplate.send("currencyConversion", from + "-" + to + "-" + new Date()); + consumerSuperThread.triggerConsumerData(from, to, quantity); + return "Success"; + } + + } + + + + + + + diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/CurrencyConversionMessageRequest.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/CurrencyConversionMessageRequest.java new file mode 100644 index 0000000..ffcfe7a --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/CurrencyConversionMessageRequest.java @@ -0,0 +1,37 @@ +package com.in28minutes.microservices.currencyconversionservice.Message.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class CurrencyConversionMessageRequest { + + + @JsonProperty("requestFrom") + private String requestFrom; + + @JsonProperty("requestTo") + private String requestTo; + + public CurrencyConversionMessageRequest(String requestFrom,String requestTo) { + this.requestFrom = requestFrom; + this.requestTo = requestTo; + } + + public CurrencyConversionMessageRequest() { + } + + public String getRequestFrom() { + return requestFrom; + } + + public void setRequestFrom(String requestFrom) { + this.requestFrom = requestFrom; + } + + public String getRequestTo() { + return requestTo; + } + + public void setRequestTo(String requestTo) { + this.requestTo = requestTo; + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/MessageController.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/MessageController.java new file mode 100644 index 0000000..621cb54 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/MessageController.java @@ -0,0 +1,28 @@ +package com.in28minutes.microservices.currencyconversionservice.Message.api; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("api/v1/messages") +public class MessageController { + + private KafkaTemplate kafkaTemplate; + + @Autowired + public MessageController(KafkaTemplate kafkaTemplate){ + this.kafkaTemplate=kafkaTemplate; + } + + public MessageController(){ + } + + @PostMapping + public void publish(@RequestBody MessageRequest request){ + kafkaTemplate.send("xecTest",request.getMessage()); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/MessageRequest.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/MessageRequest.java new file mode 100644 index 0000000..823bb4d --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/api/MessageRequest.java @@ -0,0 +1,24 @@ +package com.in28minutes.microservices.currencyconversionservice.Message.api; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class MessageRequest{ + + @JsonProperty("message") + private String message; + + public MessageRequest(String message) { + this.message = message; + } + + public MessageRequest() { + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/listner/kafkaListeners.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/listner/kafkaListeners.java new file mode 100644 index 0000000..1d45f72 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/Message/listner/kafkaListeners.java @@ -0,0 +1,13 @@ +package com.in28minutes.microservices.currencyconversionservice.Message.listner; + +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +public class kafkaListeners { + + @KafkaListener(topics = "currencyExchange", groupId = "hello") + void listener(String data) { + System.out.println("Listener received of currencyExchange: " + data + " :)"); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUser.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUser.java new file mode 100644 index 0000000..6af7cb2 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUser.java @@ -0,0 +1,83 @@ +package com.in28minutes.microservices.currencyconversionservice.auth; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +public class ApplicationUser implements UserDetails { + + private final String username; + + + private final String password; + + private final Set grantedAuthorities; + + private final Boolean isAccountNonExpired; + + private final Boolean isAccountNonLocked; + + private final Boolean isCredentialsNonExpired; + + private final Boolean isEnabled; + + + public ApplicationUser( String username,String password, Set grantedAuthorities, + Boolean isAccountNonExpired, Boolean isAccountNonLocked, Boolean isCredentialsNonExpired, + Boolean isEnabled) { + super(); + this.password = password; + this.username = username; + this.grantedAuthorities = grantedAuthorities; + this.isAccountNonExpired = isAccountNonExpired; + this.isAccountNonLocked = isAccountNonLocked; + this.isCredentialsNonExpired = isCredentialsNonExpired; + this.isEnabled = isEnabled; + } + + @Override + public Collection getAuthorities() { + // TODO Auto-generated method stub + return grantedAuthorities; + } + + @Override + public String getPassword() { + // TODO Auto-generated method stub + return password; + } + + @Override + public String getUsername() { + // TODO Auto-generated method stub + return username; + } + + @Override + public boolean isAccountNonExpired() { + // TODO Auto-generated method stub + return isAccountNonExpired; + } + + @Override + public boolean isAccountNonLocked() { + // TODO Auto-generated method stub + return isAccountNonLocked; + } + + @Override + public boolean isCredentialsNonExpired() { + // TODO Auto-generated method stub + return isCredentialsNonExpired; + } + + @Override + public boolean isEnabled() { + // TODO Auto-generated method stub + return isEnabled; + } + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUserDao.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUserDao.java new file mode 100644 index 0000000..3e71035 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUserDao.java @@ -0,0 +1,7 @@ +package com.in28minutes.microservices.currencyconversionservice.auth; + +import java.util.Optional; + +public interface ApplicationUserDao { +Optional selectApplicationUserName(String username); +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUserService.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUserService.java new file mode 100644 index 0000000..7718659 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/ApplicationUserService.java @@ -0,0 +1,29 @@ +package com.in28minutes.microservices.currencyconversionservice.auth; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +public class ApplicationUserService implements UserDetailsService { + + private final ApplicationUserDao applicationUserDao; + + @Autowired + public ApplicationUserService(@Qualifier("fake") ApplicationUserDao applicationUserDao) { + this.applicationUserDao=applicationUserDao; + } + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // TODO Auto-generated method stub + ApplicationUser applicationUser = applicationUserDao.selectApplicationUserName(username).orElseThrow(() -> new UsernameNotFoundException("user not found :" + username)); + if(applicationUser==null || !applicationUser.isEnabled() ){ + throw new UsernameNotFoundException(String.format("%s user not found in DB",username)); + } + return applicationUser; + } + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/FakeApplicationUserDaoService.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/FakeApplicationUserDaoService.java new file mode 100644 index 0000000..f7bc17e --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/auth/FakeApplicationUserDaoService.java @@ -0,0 +1,78 @@ +package com.in28minutes.microservices.currencyconversionservice.auth; + +import java.util.List; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Repository; + +import com.google.common.collect.Lists; +import com.in28minutes.microservices.currencyconversionservice.security.ApplicationUserRole; + +@Repository("fake") +public class FakeApplicationUserDaoService implements ApplicationUserDao { + + + private final PasswordEncoder passwordEncoder; + + @Autowired + public FakeApplicationUserDaoService(PasswordEncoder passwordEncoder) { + super(); + this.passwordEncoder = passwordEncoder; + } + + @Override + public Optional selectApplicationUserName(String username) { + // TODO Auto-generated method stub + return getApplicationUsers().stream().filter(user->user.getUsername().equals(username)).findFirst(); + } + + private List getApplicationUsers(){ + List appUsers=Lists.newArrayList( + + new ApplicationUser( + "imran", + passwordEncoder.encode("password"), + ApplicationUserRole.ADMIN.getAuthority(), + true, + true, + true + ,true), + new ApplicationUser( + "salman", + passwordEncoder.encode("password"), + ApplicationUserRole.STUDENT.getAuthority(), + true, + true, + true + ,true), + new ApplicationUser( + "imtiaz", + passwordEncoder.encode("password"), + ApplicationUserRole.STUDENT.getAuthority(), + true, + true, + true + ,true), + new ApplicationUser( + "ibrahim", + passwordEncoder.encode("password"), + ApplicationUserRole.STUDENT.getAuthority(), + true, + true, + true + ,true), + new ApplicationUser( + "heena", + passwordEncoder.encode("password"), + ApplicationUserRole.STUDENT.getAuthority(), + true, + true, + true + ,true) + ); + + return appUsers; + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomAccessDeniedHandler.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomAccessDeniedHandler.java new file mode 100644 index 0000000..682d02c --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomAccessDeniedHandler.java @@ -0,0 +1,53 @@ +package com.in28minutes.microservices.currencyconversionservice.exceptions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.HttpStatus; +import org.springframework.security.web.access.AccessDeniedHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class CustomAccessDeniedHandler implements AccessDeniedHandler { + // Jackson JSON serializer instance + private ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, org.springframework.security.access.AccessDeniedException accessDeniedException) throws IOException, ServletException { + + + HttpStatus httpStatus = HttpStatus.FORBIDDEN; // 403 + + Map data = new HashMap<>(); + data.put( + "timestamp", + new Date() + ); + data.put( + "code", + httpStatus.value() + ); + data.put( + "status", + httpStatus.name() + ); + data.put( + "message", + accessDeniedException.getMessage() + "from Custom Access denied :)" + ); + + // setting the response HTTP status code + response.setStatus(httpStatus.value()); + + // serializing the response body in JSON + response + .getOutputStream() + .println( + objectMapper.writeValueAsString(data) + ); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomAuthenticationFailureHandler.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomAuthenticationFailureHandler.java new file mode 100644 index 0000000..c1e6508 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomAuthenticationFailureHandler.java @@ -0,0 +1,56 @@ +package com.in28minutes.microservices.currencyconversionservice.exceptions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler { + // Jackson JSON serializer instance + private ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public void onAuthenticationFailure( + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException exception + ) throws IOException, ServletException { + HttpStatus httpStatus = HttpStatus.UNAUTHORIZED; // 401 + + Map data = new HashMap<>(); + data.put( + "timestamp", + new Date() + ); + data.put( + "code", + httpStatus.value() + ); + data.put( + "status", + httpStatus.name() + ); + data.put( + "message", + exception.getMessage() + ); + + // setting the response HTTP status code + response.setStatus(httpStatus.value()); + + // serializing the response body in JSON + response + .getOutputStream() + .println( + objectMapper.writeValueAsString(data) + ); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomizedResponseEntityExceptionHandler.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomizedResponseEntityExceptionHandler.java new file mode 100644 index 0000000..2aeb2e0 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/CustomizedResponseEntityExceptionHandler.java @@ -0,0 +1,32 @@ +package com.in28minutes.microservices.currencyconversionservice.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import java.time.LocalDateTime; + +@ControllerAdvice +public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(Exception.class) + public final ResponseEntity handleAllExceptions(Exception ex, WebRequest request) throws Exception { + ErrorDetails errorDetails = new ErrorDetails(LocalDateTime.now(), + ex.getMessage(), request.getDescription(false)); + + return new ResponseEntity(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR); + + } + + @ExceptionHandler(UserNotFoundException.class) + public final ResponseEntity handleUserNotFoundException(Exception ex, WebRequest request) throws Exception { + ErrorDetails errorDetails = new ErrorDetails(LocalDateTime.now(), + ex.getMessage(), request.getDescription(false)); + + return new ResponseEntity(errorDetails, HttpStatus.NOT_FOUND); + + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/ErrorDetails.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/ErrorDetails.java new file mode 100644 index 0000000..f737ed8 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/ErrorDetails.java @@ -0,0 +1,31 @@ +package com.in28minutes.microservices.currencyconversionservice.exceptions; + +import java.time.LocalDateTime; + +public class ErrorDetails { + + private LocalDateTime timestamp; + private String message; + private String details; + + public ErrorDetails(LocalDateTime timestamp, String message, String details) { + super(); + this.timestamp = timestamp; + this.message = message; + this.details = details; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public String getMessage() { + return message; + } + + public String getDetails() { + return details; + } + + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/UserNotFoundException.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/UserNotFoundException.java new file mode 100644 index 0000000..d8c489f --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/exceptions/UserNotFoundException.java @@ -0,0 +1,13 @@ +package com.in28minutes.microservices.currencyconversionservice.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.NOT_FOUND) +public class UserNotFoundException extends RuntimeException { + + public UserNotFoundException(String message) { + super(message); + } + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtManualConfig.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtManualConfig.java new file mode 100644 index 0000000..ae05248 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtManualConfig.java @@ -0,0 +1,45 @@ +package com.in28minutes.microservices.currencyconversionservice.jwt; + +import com.google.common.net.HttpHeaders; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@ConfigurationProperties(prefix = "application.jwt") +@Configuration +public class JwtManualConfig { + + private String secretKey; + private String tokenPrefix; + private Integer tokenExpirationAfterDays; + + public JwtManualConfig() { + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getTokenPrefix() { + return tokenPrefix; + } + + public void setTokenPrefix(String tokenPrefix) { + this.tokenPrefix = tokenPrefix; + } + + public Integer getTokenExpirationAfterDays() { + return tokenExpirationAfterDays; + } + + public void setTokenExpirationAfterDays(Integer tokenExpirationAfterDays) { + this.tokenExpirationAfterDays = tokenExpirationAfterDays; + } + + public String getAuthorizationHeader() { + return HttpHeaders.AUTHORIZATION; + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtManualSecretKey.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtManualSecretKey.java new file mode 100644 index 0000000..15fe5d3 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtManualSecretKey.java @@ -0,0 +1,24 @@ +package com.in28minutes.microservices.currencyconversionservice.jwt; + +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.crypto.SecretKey; + +@Configuration +public class JwtManualSecretKey { + + private final JwtManualConfig jwtManualConfig; + + @Autowired + public JwtManualSecretKey(JwtManualConfig jwtManualConfig) { + this.jwtManualConfig = jwtManualConfig; + } + + @Bean + public SecretKey secretKey() { + return Keys.hmacShaKeyFor(jwtManualConfig.getSecretKey().getBytes()); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtTokenVerifier.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtTokenVerifier.java new file mode 100644 index 0000000..93238b8 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtTokenVerifier.java @@ -0,0 +1,86 @@ +package com.in28minutes.microservices.currencyconversionservice.jwt; + +import com.google.common.base.Strings; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.crypto.SecretKey; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class JwtTokenVerifier extends OncePerRequestFilter { + + private final SecretKey secretKey; + private final JwtManualConfig jwtManualConfig; + + public JwtTokenVerifier(SecretKey secretKey, + JwtManualConfig jwtManualConfig) { + this.secretKey = secretKey; + this.jwtManualConfig = jwtManualConfig; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + String authorizationHeader = request.getHeader(jwtManualConfig.getAuthorizationHeader()); + + if (request.getServletPath().equals("/login")) { + filterChain.doFilter(request, response); + } else { + + if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith(jwtManualConfig.getTokenPrefix())) { + filterChain.doFilter(request, response); + return; + } + + String token = authorizationHeader.replace(jwtManualConfig.getTokenPrefix(), ""); + + try { + + Jws claimsJws = Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(token); + + Claims body = claimsJws.getBody(); + + String username = body.getSubject(); + + var authorities = (List>) body.get("authorities"); + + Set simpleGrantedAuthorities = authorities.stream() + .map(m -> new SimpleGrantedAuthority(m.get("authority"))) + .collect(Collectors.toSet()); + + Authentication authentication = new UsernamePasswordAuthenticationToken( + username, + null, + simpleGrantedAuthorities + ); + + SecurityContextHolder.getContext().setAuthentication(authentication); + + } catch (JwtException e) { + logger.error(e.getMessage()); + throw new IllegalStateException(String.format("Token %s cannot be trusted", token)); + } + + filterChain.doFilter(request, response); + } + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtUsernameAndPasswordAuthenticationFilter.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtUsernameAndPasswordAuthenticationFilter.java new file mode 100644 index 0000000..92f54a0 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/JwtUsernameAndPasswordAuthenticationFilter.java @@ -0,0 +1,88 @@ +package com.in28minutes.microservices.currencyconversionservice.jwt; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.Jwts; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.boot.web.servlet.RegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.crypto.SecretKey; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.LocalDate; +import java.util.Date; + +public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + + private final AuthenticationManager authenticationManager; + private final JwtManualConfig jwtManualConfig; + private final SecretKey secretKey; + + public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authenticationManager, + JwtManualConfig jwtManualConfig, + SecretKey secretKey) { + this.authenticationManager = authenticationManager; + this.jwtManualConfig = jwtManualConfig; + this.secretKey = secretKey; + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, + HttpServletResponse response) throws AuthenticationException { + + try { + + UsernameAndPasswordAuthenticationRequest authenticationRequest = new ObjectMapper() + .readValue(request.getInputStream(), UsernameAndPasswordAuthenticationRequest.class); + + Authentication authentication = new UsernamePasswordAuthenticationToken( + authenticationRequest.getUsername(), + authenticationRequest.getPassword() + ); + + Authentication authenticate = authenticationManager.authenticate(authentication); + return authenticate; + + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + if (false) { + chain.doFilter(request, response); + } else { + super.doFilter(request, response, chain); + } + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, + HttpServletResponse response, + FilterChain chain, + Authentication authResult) throws IOException, ServletException { + String token = Jwts.builder() + .setSubject(authResult.getName()) + .claim("authorities", authResult.getAuthorities()) + .setIssuedAt(new Date()) + .setExpiration(java.sql.Date.valueOf(LocalDate.now().plusDays(jwtManualConfig.getTokenExpirationAfterDays()))) + .signWith(secretKey) + .compact(); + + response.addHeader(jwtManualConfig.getAuthorizationHeader(), jwtManualConfig.getTokenPrefix() + token); + } + + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/UsernameAndPasswordAuthenticationRequest.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/UsernameAndPasswordAuthenticationRequest.java new file mode 100644 index 0000000..ff82472 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/jwt/UsernameAndPasswordAuthenticationRequest.java @@ -0,0 +1,26 @@ +package com.in28minutes.microservices.currencyconversionservice.jwt; + +public class UsernameAndPasswordAuthenticationRequest { + + private String username; + private String password; + + public UsernameAndPasswordAuthenticationRequest() { + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationSecurityConfig.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationSecurityConfig.java new file mode 100644 index 0000000..e945506 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationSecurityConfig.java @@ -0,0 +1,148 @@ +package com.in28minutes.microservices.currencyconversionservice.security; + +import com.in28minutes.microservices.currencyconversionservice.auth.ApplicationUserService; +import com.in28minutes.microservices.currencyconversionservice.exceptions.CustomAccessDeniedHandler; +import com.in28minutes.microservices.currencyconversionservice.exceptions.CustomAuthenticationFailureHandler; +import com.in28minutes.microservices.currencyconversionservice.jwt.JwtManualConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; + +import javax.crypto.SecretKey; +import java.util.concurrent.TimeUnit; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled=true) +public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { + + private final PasswordEncoder passwordEncoder; + + private final ApplicationUserService appService; + + + private final SecretKey secretKey; + + private final JwtManualConfig jwtManualConfig; + + + + + @Autowired + public ApplicationSecurityConfig(PasswordEncoder passwordEncoder, ApplicationUserService appService, SecretKey secretKey, JwtManualConfig jwtManualConfig) { + this.passwordEncoder=passwordEncoder; + this.appService=appService; + this.secretKey = secretKey; + this.jwtManualConfig = jwtManualConfig; + } + + /** + * /* + * antMatcher to validate permissions + * * .antMatchers("/*").hasRole(ApplicationUserRole.ADMIN.name()) + * * .antMatchers("/*").hasAuthority( + * * ApplicationUserPermission.COURSE_WRITE.getPermission()) + * * .antMatchers("/*").hasAuthority( + * * ApplicationUserPermission.COURSE_READ.getPermission()) + * */ + /* + * @param http the {@link HttpSecurity} to modify + * @throws Exception + */ + @Override + public void configure(HttpSecurity http) throws Exception { + + + http + .csrf().disable() + //.authorizeRequests().antMatchers("/app/**").permitAll().and() + + /* .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()*/ + .authorizeRequests().antMatchers("/app/**").authenticated().and() + .authorizeRequests().antMatchers("/login").authenticated().and() + //.addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtManualConfig, secretKey)) + //.authorizeRequests().antMatchers("/app/**").permitAll().and() + + //.addFilterAfter(new JwtTokenVerifier(secretKey, jwtManualConfig), JwtUsernameAndPasswordAuthenticationFilter.class) + + .authorizeRequests() + + .anyRequest() + .authenticated() + .and() + .formLogin() + .loginPage("/login").permitAll() + .defaultSuccessUrl("/sucess") + .passwordParameter("password") + .usernameParameter("username") + .and() + .rememberMe() + .tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(21)) + .key("sha") + .and() + .logout() + .logoutUrl("/logout") + .clearAuthentication(true) + .invalidateHttpSession(true) + .deleteCookies("JSESSIONID", "remember-me") + .logoutSuccessUrl("/login") + .and() + .formLogin() + .failureHandler(authenticationFailureHandler()) + .and() + .exceptionHandling() + .accessDeniedHandler(accessDeniedHandler()); + } + + /* + * @Override + * + * @Bean protected UserDetailsService userDetailsService() { UserDetails + * imranUser=User.builder().username("imran") + * .password(passwordEncoder.encode("password123")) + * //.roles(ApplicationUserRole.STUDENT.name()) + * .authorities(ApplicationUserRole.STUDENT.getAuthority()) .build(); + * UserDetails ayeshaUser=User.builder().username("ayesha") + * .password(passwordEncoder.encode("password123")) + * //.roles(ApplicationUserRole.ADMIN.name()) + * .authorities(ApplicationUserRole.ADMIN.getAuthority()) .build(); + * + * return new InMemoryUserDetailsManager(imranUser,ayeshaUser); } + */ + + @Bean + public DaoAuthenticationProvider daoAuthProvider() { + DaoAuthenticationProvider daoAuthProvider=new DaoAuthenticationProvider(); + daoAuthProvider.setPasswordEncoder(passwordEncoder); + daoAuthProvider.setUserDetailsService(appService); + return daoAuthProvider; + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(daoAuthProvider()); + } + + @Bean + public AccessDeniedHandler accessDeniedHandler() { + return new CustomAccessDeniedHandler(); + } + + + @Bean + public AuthenticationFailureHandler authenticationFailureHandler() { + return new CustomAuthenticationFailureHandler(); + } + + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationUserPermission.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationUserPermission.java new file mode 100644 index 0000000..1fd7d6a --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationUserPermission.java @@ -0,0 +1,19 @@ +package com.in28minutes.microservices.currencyconversionservice.security; + +public enum ApplicationUserPermission { + STUDENT_READ("student:read"), + STUDENT_WRITE("student:write"), + COURSE_READ("course:read"), + COURSE_WRITE("course:write"); + + private final String permission; + + ApplicationUserPermission(String permission){ + this.permission=permission; + } + + public String getPermission() { + return permission; + } + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationUserRole.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationUserRole.java new file mode 100644 index 0000000..809efe1 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/ApplicationUserRole.java @@ -0,0 +1,40 @@ +package com.in28minutes.microservices.currencyconversionservice.security; + +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import static com.in28minutes.microservices.currencyconversionservice.security.ApplicationUserPermission.*; + +import com.google.common.collect.Sets; + + +public enum ApplicationUserRole { + STUDENT(Sets.newHashSet()), + ADMIN(Sets.newHashSet( + COURSE_READ, + COURSE_WRITE, + COURSE_READ, + COURSE_READ + )); + + private final Set permissions; + + ApplicationUserRole(Set permissions){ + this.permissions=permissions; + } + + public Set getPermission(){ + return permissions; + } + + public Set getAuthority(){ + Set permissions= + getPermission().stream().map(p-> new SimpleGrantedAuthority(p.getPermission())) + .collect(Collectors.toSet()); + permissions.add(new SimpleGrantedAuthority("ROLE_" + this.name())); + return permissions; + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/PasswordConfig.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/PasswordConfig.java new file mode 100644 index 0000000..6e56a10 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/PasswordConfig.java @@ -0,0 +1,16 @@ +package com.in28minutes.microservices.currencyconversionservice.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class PasswordConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(10); + } + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/TemplateController.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/TemplateController.java new file mode 100644 index 0000000..f32f5b3 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/security/TemplateController.java @@ -0,0 +1,25 @@ +package com.in28minutes.microservices.currencyconversionservice.security; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequestMapping("/") +public class TemplateController { + + @GetMapping("login") + public String getLoginView() { + return "login"; + } + + @GetMapping("sucess") + public String getSuccessView() { + return "sucess"; + } + + @GetMapping("logout") + public String getLogoutView() { + return "logout"; + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/Student.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/Student.java new file mode 100644 index 0000000..21f2033 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/Student.java @@ -0,0 +1,23 @@ +package com.in28minutes.microservices.currencyconversionservice.student; + +public class Student { + + private final int studentId; + private final String studentName; + + public Student(int studentId, String studentName) { + super(); + this.studentId = studentId; + this.studentName = studentName; + } + + + public int getStudentId() { + return studentId; + } + public String getStudentName() { + return studentName; + } + + +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/StudentController.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/StudentController.java new file mode 100644 index 0000000..e44df73 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/StudentController.java @@ -0,0 +1,25 @@ +package com.in28minutes.microservices.currencyconversionservice.student; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(path ="api/v1/student") +public class StudentController { + + private static List STUDENTS=Arrays.asList(new Student(1,"imran"),new Student(2,"vishakh")); + + + + @GetMapping(path="{studentId}") + public Student getStudent(@PathVariable("studentId") int studentId) { + return STUDENTS.stream().filter(student->student.getStudentId()==studentId) + .findFirst() + .orElseThrow(()->new IllegalStateException("student" +studentId)); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/StudentManagementController.java b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/StudentManagementController.java new file mode 100644 index 0000000..9ca135e --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/java/com/in28minutes/microservices/currencyconversionservice/student/StudentManagementController.java @@ -0,0 +1,45 @@ +package com.in28minutes.microservices.currencyconversionservice.student; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(path ="api/v1/student") +public class StudentManagementController { + + private static List STUDENTS=Arrays.asList(new Student(1,"imran"),new Student(2,"vishakh")); + + @GetMapping + public List getAllStudent(){ + return STUDENTS; + } + + @PostMapping + public void newStudent(@RequestBody Student student) + { + System.out.println("added student id:" + student.getStudentId()); + + } + + @DeleteMapping(path="{studentId}") + public void deleteStudent(@PathVariable("studentId") int studentId) + { + System.out.println("deleted student id:" + studentId); + + } + + @PutMapping(path="{studentId}") + public void updateStudent(@PathVariable("studentId") int studentId,@RequestBody Student student) + { + System.out.println(" update student id:" + student.getStudentId()); + } +} diff --git a/03.microservices/currency-conversion-service/src/main/resources/application.properties b/03.microservices/currency-conversion-service/src/main/resources/application.properties index 36524cd..1a1d595 100644 --- a/03.microservices/currency-conversion-service/src/main/resources/application.properties +++ b/03.microservices/currency-conversion-service/src/main/resources/application.properties @@ -2,5 +2,11 @@ spring.application.name=currency-conversion server.port=8100 eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka +eureka.client.instance.preferIpAddress = true -spring.sleuth.sampler.probability=1.0 \ No newline at end of file +spring.sleuth.sampler.probability=1.0 +application.jwt.secretKey=securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecure +application.jwt.tokenPrefix=Bearer +application.jwt.tokenExpirationAfterDays=14 + +spring.kafka.bootstrap-servers=localhost:9092 \ No newline at end of file diff --git a/03.microservices/currency-conversion-service/src/main/resources/templates/login.html b/03.microservices/currency-conversion-service/src/main/resources/templates/login.html new file mode 100644 index 0000000..0b0d4d3 --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/resources/templates/login.html @@ -0,0 +1,32 @@ + + + + + + + + + Please sign in + + + + +
+ +
+ \ No newline at end of file diff --git a/03.microservices/currency-conversion-service/src/main/resources/templates/sucess.html b/03.microservices/currency-conversion-service/src/main/resources/templates/sucess.html new file mode 100644 index 0000000..b6cc55e --- /dev/null +++ b/03.microservices/currency-conversion-service/src/main/resources/templates/sucess.html @@ -0,0 +1,61 @@ + + + + + + + + Please sign in + + + + +

URLs

+

Limits Service

+ +

Cloud Config Server

+ +

Currency Exchange Service

+ +

Currency Conversion Service

+ +

Eureka

+ +

Spring Cloud Api Gateway

+

Initial

+ +

Intermediate

+ +

Final

+ + + + \ No newline at end of file diff --git a/03.microservices/currency-exchange-service/pom.xml b/03.microservices/currency-exchange-service/pom.xml index f45c95d..0b7b924 100644 --- a/03.microservices/currency-exchange-service/pom.xml +++ b/03.microservices/currency-exchange-service/pom.xml @@ -83,6 +83,15 @@ spring-boot-starter-test test + + org.springframework.kafka + spring-kafka + + + org.springframework.kafka + spring-kafka-test + test + diff --git a/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaConsumerConfig.java b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaConsumerConfig.java new file mode 100644 index 0000000..ae36672 --- /dev/null +++ b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaConsumerConfig.java @@ -0,0 +1,50 @@ +package com.in28minutes.microservices.currencyexchangeservice.kafka; + +import java.util.HashMap; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.config.KafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; + +import io.vavr.collection.Map; + +public class KafkaConsumerConfig { + + + @Value("localhost:9092") + private String bootstrapServers; + + public Map consumerConfig() { + Map props = (Map) new HashMap(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringSerializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringSerializer.class); + return props; + } + + + //creating consumer instances + @Bean + public ConsumerFactory consumerFactory() { + //return new DefaultKafkaConsumerFactory<>() consumerConfig()); + return new DefaultKafkaConsumerFactory<>((java.util.Map) consumerConfig()); + + } + + //listerner container + @Bean + public KafkaListenerContainerFactory> factory( + ConsumerFactory consumerFactory + ) { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory); + return factory; + } +} diff --git a/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaListeners.java b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaListeners.java new file mode 100644 index 0000000..0476ea0 --- /dev/null +++ b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaListeners.java @@ -0,0 +1,32 @@ +package com.in28minutes.microservices.currencyexchangeservice.kafka; +import java.util.Date; +import java.util.HashMap; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +import com.in28minutes.microservices.currencyexchangeservice.CurrencyExchange; +import com.in28minutes.microservices.currencyexchangeservice.CurrencyExchangeController; + +import io.vavr.collection.Map; + +@Component +public class KafkaListeners { + + @Autowired + CurrencyExchangeController c; + + @Autowired + private KafkaTemplate kafkaTemplate; + + @KafkaListener(topics = "currencyConversion", groupId = "hello") + void listener(String data) { + System.out.println("-------------------------Listener received " + data + " ----------------------------------------------:)"); + HashMap myVal= new HashMap(); + CurrencyExchange currencyExchange=c.retrieveExchangeValue(data.split("-")[0],data.split("-")[1]); + kafkaTemplate.send("currencyExchange", currencyExchange.getConversionMultiple().toString() +"-"+ new Date()); + } + +} diff --git a/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaProducerConfig.java b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaProducerConfig.java new file mode 100644 index 0000000..8991e9a --- /dev/null +++ b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaProducerConfig.java @@ -0,0 +1,44 @@ +package com.in28minutes.microservices.currencyexchangeservice.kafka; + +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.config.ConfigDef; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaProducerConfig { + + @Value("${spring.kafka.bootstrap-servers}") + private String bootstrapServers; + + public Map producerConfig(){ + Map props=new HashMap<>(); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,bootstrapServers); + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + return props; + } + + + //creating producer instances + @Bean + public ProducerFactory producerFactory(){ + return new DefaultKafkaProducerFactory<>(producerConfig()); + } + + //send messages + @Bean + public KafkaTemplate kafkaTemplate(ProducerFactory producerFactory){ + return new KafkaTemplate<>(producerFactory); + } +} + + diff --git a/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaTopicConfig.java b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaTopicConfig.java new file mode 100644 index 0000000..be2ad24 --- /dev/null +++ b/03.microservices/currency-exchange-service/src/main/java/com/in28minutes/microservices/currencyexchangeservice/kafka/KafkaTopicConfig.java @@ -0,0 +1,16 @@ +package com.in28minutes.microservices.currencyexchangeservice.kafka; + + +import org.apache.kafka.clients.admin.NewTopic; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.TopicBuilder; + +@Configuration +public class KafkaTopicConfig { + + @Bean + public NewTopic currencyConversion(){ + return TopicBuilder.name("currencyExchange").build(); + } +} diff --git a/03.microservices/currency-exchange-service/src/main/resources/application.properties b/03.microservices/currency-exchange-service/src/main/resources/application.properties index 9b466f0..19805c8 100644 --- a/03.microservices/currency-exchange-service/src/main/resources/application.properties +++ b/03.microservices/currency-exchange-service/src/main/resources/application.properties @@ -6,6 +6,7 @@ spring.datasource.url=jdbc:h2:mem:testdb spring.h2.console.enabled=true eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka +eureka.client.instance.preferIpAddress = true resilience4j.retry.instances.sample-api.maxRetryAttempts=5 resilience4j.retry.instances.sample-api.waitDuration=1s @@ -20,4 +21,6 @@ resilience4j.bulkhead.instances.sample-api.maxConcurrentCalls=10 spring.sleuth.sampler.probability=1.0 -##spring.zipkin.baseUrl=http://localhost:9411/ \ No newline at end of file +##spring.zipkin.baseUrl=http://localhost:9411/ + +spring.kafka.bootstrap-servers=localhost:9092 \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..e69de29