From 4ac7592bd78ae01ee00ffed0f63aadceaa8919c6 Mon Sep 17 00:00:00 2001 From: petrelevich Date: Mon, 8 Jul 2024 09:16:47 +0300 Subject: [PATCH 1/2] L-39 kafka --- 2024-01/spring-39-kafka/.gitignore | 39 +++++++ 2024-01/spring-39-kafka/consumer/pom.xml | 49 ++++++++ .../src/main/java/ru/demo/ConsumerApp.java | 11 ++ .../ru/demo/config/ApplicationConfig.java | 108 ++++++++++++++++++ .../main/java/ru/demo/model/StringValue.java | 3 + .../ru/demo/service/StringValueConsumer.java | 9 ++ .../service/StringValueConsumerLogger.java | 17 +++ .../src/main/resources/application.yml | 12 ++ .../spring-39-kafka/docker/docker-compose.yml | 24 ++++ 2024-01/spring-39-kafka/pom.xml | 92 +++++++++++++++ 2024-01/spring-39-kafka/producer/pom.xml | 45 ++++++++ .../src/main/java/ru/demo/ProducerApp.java | 11 ++ .../ru/demo/config/ApplicationConfig.java | 73 ++++++++++++ .../main/java/ru/demo/model/StringValue.java | 3 + .../main/java/ru/demo/service/DataSender.java | 7 ++ .../java/ru/demo/service/DataSenderKafka.java | 48 ++++++++ .../src/main/java/ru/demo/service/Runner.java | 18 +++ .../ru/demo/service/StringValueSource.java | 30 +++++ .../java/ru/demo/service/ValueSource.java | 5 + .../src/main/resources/application.yml | 11 ++ 20 files changed, 615 insertions(+) create mode 100644 2024-01/spring-39-kafka/.gitignore create mode 100644 2024-01/spring-39-kafka/consumer/pom.xml create mode 100644 2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/ConsumerApp.java create mode 100644 2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/config/ApplicationConfig.java create mode 100644 2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/model/StringValue.java create mode 100644 2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/service/StringValueConsumer.java create mode 100644 2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/service/StringValueConsumerLogger.java create mode 100644 2024-01/spring-39-kafka/consumer/src/main/resources/application.yml create mode 100644 2024-01/spring-39-kafka/docker/docker-compose.yml create mode 100644 2024-01/spring-39-kafka/pom.xml create mode 100644 2024-01/spring-39-kafka/producer/pom.xml create mode 100644 2024-01/spring-39-kafka/producer/src/main/java/ru/demo/ProducerApp.java create mode 100644 2024-01/spring-39-kafka/producer/src/main/java/ru/demo/config/ApplicationConfig.java create mode 100644 2024-01/spring-39-kafka/producer/src/main/java/ru/demo/model/StringValue.java create mode 100644 2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/DataSender.java create mode 100644 2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/DataSenderKafka.java create mode 100644 2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/Runner.java create mode 100644 2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/StringValueSource.java create mode 100644 2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/ValueSource.java create mode 100644 2024-01/spring-39-kafka/producer/src/main/resources/application.yml diff --git a/2024-01/spring-39-kafka/.gitignore b/2024-01/spring-39-kafka/.gitignore new file mode 100644 index 00000000..dae2cf99 --- /dev/null +++ b/2024-01/spring-39-kafka/.gitignore @@ -0,0 +1,39 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +!gradle-wrapper.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# Ignore Gradle project-specific cache directory +.gradle +/buildSrc/.gradle/ + +# Ignore Gradle build output directory +build +out + +#Idea +*.iml +*.iws +*.ipr +*.idea + diff --git a/2024-01/spring-39-kafka/consumer/pom.xml b/2024-01/spring-39-kafka/consumer/pom.xml new file mode 100644 index 00000000..5d7aa504 --- /dev/null +++ b/2024-01/spring-39-kafka/consumer/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + ru.otus + spring-39-kafka + 1.0 + + + consumer + 1.0 + + + 17 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.kafka + spring-kafka + + + + com.fasterxml.jackson.core + jackson-databind + + + com.google.code.findbugs + jsr305 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/ConsumerApp.java b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/ConsumerApp.java new file mode 100644 index 00000000..a9e6aa58 --- /dev/null +++ b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/ConsumerApp.java @@ -0,0 +1,11 @@ +package ru.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ConsumerApp { + public static void main(String[] args) { + SpringApplication.run(ConsumerApp.class, args); + } +} diff --git a/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/config/ApplicationConfig.java b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/config/ApplicationConfig.java new file mode 100644 index 00000000..dc63a5a8 --- /dev/null +++ b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/config/ApplicationConfig.java @@ -0,0 +1,108 @@ +package ru.demo.config; + +import static org.springframework.kafka.support.serializer.JsonDeserializer.TYPE_MAPPINGS; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.kafka.KafkaProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.config.KafkaListenerContainerFactory; +import org.springframework.kafka.config.TopicBuilder; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; +import org.springframework.kafka.support.JacksonUtils; +import org.springframework.kafka.support.serializer.JsonDeserializer; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor; +import ru.demo.model.StringValue; +import ru.demo.service.StringValueConsumer; +import ru.demo.service.StringValueConsumerLogger; + +@Configuration +public class ApplicationConfig { + private static final Logger log = LoggerFactory.getLogger(ApplicationConfig.class); + public final String topicName; + + public ApplicationConfig(@Value("${application.kafka.topic}") String topicName) { + this.topicName = topicName; + } + + @Bean + public ObjectMapper objectMapper() { + return JacksonUtils.enhancedObjectMapper(); + } + + @Bean + public ConsumerFactory consumerFactory( + KafkaProperties kafkaProperties, ObjectMapper mapper) { + var props = kafkaProperties.buildConsumerProperties(); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); + props.put(TYPE_MAPPINGS, "ru.demo.model.StringValue:ru.demo.model.StringValue"); + props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 3); + props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 3_000); + + var kafkaConsumerFactory = new DefaultKafkaConsumerFactory(props); + kafkaConsumerFactory.setValueDeserializer(new JsonDeserializer<>(mapper)); + return kafkaConsumerFactory; + } + + @Bean("listenerContainerFactory") + public KafkaListenerContainerFactory> + listenerContainerFactory(ConsumerFactory consumerFactory) { + var factory = new ConcurrentKafkaListenerContainerFactory(); + factory.setConsumerFactory(consumerFactory); + factory.setBatchListener(true); + factory.setConcurrency(1); + factory.getContainerProperties().setIdleBetweenPolls(1_000); + factory.getContainerProperties().setPollTimeout(1_000); + + var executor = new SimpleAsyncTaskExecutor("k-consumer-"); + executor.setConcurrencyLimit(10); + var listenerTaskExecutor = new ConcurrentTaskExecutor(executor); + factory.getContainerProperties().setListenerTaskExecutor(listenerTaskExecutor); + return factory; + } + + @Bean + public NewTopic topic() { + return TopicBuilder.name(topicName).partitions(1).replicas(1).build(); + } + + @Bean + public StringValueConsumer stringValueConsumerLogger() { + return new StringValueConsumerLogger(); + } + + @Bean + public KafkaClient stringValueConsumer(StringValueConsumer stringValueConsumer) { + return new KafkaClient(stringValueConsumer); + } + + public static class KafkaClient { + private final StringValueConsumer stringValueConsumer; + + public KafkaClient(StringValueConsumer stringValueConsumer) { + this.stringValueConsumer = stringValueConsumer; + } + + @KafkaListener( + topics = "${application.kafka.topic}", + containerFactory = "listenerContainerFactory") + public void listen(@Payload List values) { + log.info("values, values.size:{}", values.size()); + stringValueConsumer.accept(values); + } + } +} diff --git a/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/model/StringValue.java b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/model/StringValue.java new file mode 100644 index 00000000..213f1309 --- /dev/null +++ b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/model/StringValue.java @@ -0,0 +1,3 @@ +package ru.demo.model; + +public record StringValue(long id, String value) {} diff --git a/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/service/StringValueConsumer.java b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/service/StringValueConsumer.java new file mode 100644 index 00000000..ff5cd296 --- /dev/null +++ b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/service/StringValueConsumer.java @@ -0,0 +1,9 @@ +package ru.demo.service; + +import java.util.List; +import ru.demo.model.StringValue; + +public interface StringValueConsumer { + + void accept(List value); +} diff --git a/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/service/StringValueConsumerLogger.java b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/service/StringValueConsumerLogger.java new file mode 100644 index 00000000..c791cbea --- /dev/null +++ b/2024-01/spring-39-kafka/consumer/src/main/java/ru/demo/service/StringValueConsumerLogger.java @@ -0,0 +1,17 @@ +package ru.demo.service; + +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.demo.model.StringValue; + +public class StringValueConsumerLogger implements StringValueConsumer { + private static final Logger log = LoggerFactory.getLogger(StringValueConsumerLogger.class); + + @Override + public void accept(List values) { + for (var value : values) { + log.info("log:{}", value); + } + } +} diff --git a/2024-01/spring-39-kafka/consumer/src/main/resources/application.yml b/2024-01/spring-39-kafka/consumer/src/main/resources/application.yml new file mode 100644 index 00000000..32db801b --- /dev/null +++ b/2024-01/spring-39-kafka/consumer/src/main/resources/application.yml @@ -0,0 +1,12 @@ +application: + kafka: + topic: "demo-topic" + + +spring: + kafka: + consumer: + group-id: "test-group" + bootstrap-servers: "localhost:9092" + client-id: "demo-consumer" + auto-offset-reset: earliest diff --git a/2024-01/spring-39-kafka/docker/docker-compose.yml b/2024-01/spring-39-kafka/docker/docker-compose.yml new file mode 100644 index 00000000..b16128f3 --- /dev/null +++ b/2024-01/spring-39-kafka/docker/docker-compose.yml @@ -0,0 +1,24 @@ + +services: + zookeeper: + image: confluentinc/cp-zookeeper:6.2.0 + container_name: zookeeper + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + + broker: + image: confluentinc/cp-kafka:7.0.0 + container_name: broker + ports: + - "9092:9092" + depends_on: + - zookeeper + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_INTERNAL:PLAINTEXT + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092,PLAINTEXT_INTERNAL://broker:29092 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 \ No newline at end of file diff --git a/2024-01/spring-39-kafka/pom.xml b/2024-01/spring-39-kafka/pom.xml new file mode 100644 index 00000000..cab976ba --- /dev/null +++ b/2024-01/spring-39-kafka/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + ru.otus + spring-39-kafka + 1.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.1.1 + + + pom + + + consumer + producer + + + + UTF-8 + 17 + 17 + 3.0.0-M3 + 3.1.1 + 3.3.9 + 1.0.12.RELEASE + 3.0.5 + 3.0.2 + + + + + + org.springframework.boot + spring-boot-dependencies + ${springframeworkBoot.version} + pom + import + + + com.google.code.findbugs + jsr305 + ${jsr305.version} + + + + + + + + + + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-maven + + enforce + + + + + + ${minimal.maven.version} + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven-shade-plugin.version} + + + + org.apache.maven.plugins + maven-assembly-plugin + ${maven-assembly-plugin.version} + + + + + diff --git a/2024-01/spring-39-kafka/producer/pom.xml b/2024-01/spring-39-kafka/producer/pom.xml new file mode 100644 index 00000000..9485620b --- /dev/null +++ b/2024-01/spring-39-kafka/producer/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + + ru.otus + spring-39-kafka + 1.0 + + + producer + 1.0 + + + 17 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.kafka + spring-kafka + + + + com.fasterxml.jackson.core + jackson-databind + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/ProducerApp.java b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/ProducerApp.java new file mode 100644 index 00000000..e9d022bb --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/ProducerApp.java @@ -0,0 +1,11 @@ +package ru.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ProducerApp { + public static void main(String[] args) { + SpringApplication.run(ProducerApp.class, args); + } +} diff --git a/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/config/ApplicationConfig.java b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/config/ApplicationConfig.java new file mode 100644 index 00000000..e0bc3346 --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/config/ApplicationConfig.java @@ -0,0 +1,73 @@ +package ru.demo.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.clients.admin.NewTopic; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.kafka.KafkaProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.config.TopicBuilder; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.support.JacksonUtils; +import org.springframework.kafka.support.serializer.JsonSerializer; +import ru.demo.model.StringValue; +import ru.demo.service.DataSender; +import ru.demo.service.DataSenderKafka; +import ru.demo.service.StringValueSource; + +@Configuration +public class ApplicationConfig { + private static final Logger log = LoggerFactory.getLogger(ApplicationConfig.class); + public final String topicName; + + public ApplicationConfig(@Value("${application.kafka.topic}") String topicName) { + this.topicName = topicName; + } + + @Bean + public ObjectMapper objectMapper() { + return JacksonUtils.enhancedObjectMapper(); + } + + @Bean + public ProducerFactory producerFactory( + KafkaProperties kafkaProperties, ObjectMapper mapper) { + var props = kafkaProperties.buildProducerProperties(); + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); + + var kafkaProducerFactory = new DefaultKafkaProducerFactory(props); + kafkaProducerFactory.setValueSerializer(new JsonSerializer<>(mapper)); + return kafkaProducerFactory; + } + + @Bean + public KafkaTemplate kafkaTemplate( + ProducerFactory producerFactory) { + return new KafkaTemplate<>(producerFactory); + } + + @Bean + public NewTopic topic() { + return TopicBuilder.name(topicName).partitions(1).replicas(1).build(); + } + + @Bean + public DataSender dataSender(NewTopic topic, KafkaTemplate kafkaTemplate) { + return new DataSenderKafka( + topic.name(), + kafkaTemplate, + stringValue -> log.info("asked, value:{}", stringValue)); + } + + @Bean + public StringValueSource stringValueSource(DataSender dataSender) { + return new StringValueSource(dataSender); + } +} diff --git a/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/model/StringValue.java b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/model/StringValue.java new file mode 100644 index 00000000..213f1309 --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/model/StringValue.java @@ -0,0 +1,3 @@ +package ru.demo.model; + +public record StringValue(long id, String value) {} diff --git a/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/DataSender.java b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/DataSender.java new file mode 100644 index 00000000..321e3f5f --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/DataSender.java @@ -0,0 +1,7 @@ +package ru.demo.service; + +import ru.demo.model.StringValue; + +public interface DataSender { + void send(StringValue value); +} diff --git a/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/DataSenderKafka.java b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/DataSenderKafka.java new file mode 100644 index 00000000..a1b07f80 --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/DataSenderKafka.java @@ -0,0 +1,48 @@ +package ru.demo.service; + +import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import ru.demo.model.StringValue; + +public class DataSenderKafka implements DataSender { + private static final Logger log = LoggerFactory.getLogger(DataSenderKafka.class); + + private final KafkaTemplate template; + + private final Consumer sendAsk; + + private final String topic; + + public DataSenderKafka( + String topic, + KafkaTemplate template, + Consumer sendAsk) { + this.topic = topic; + this.template = template; + this.sendAsk = sendAsk; + } + + @Override + public void send(StringValue value) { + try { + log.info("value:{}", value); + template.send(topic, value) + .whenComplete( + (result, ex) -> { + if (ex == null) { + log.info( + "message id:{} was sent, offset:{}", + value.id(), + result.getRecordMetadata().offset()); + sendAsk.accept(value); + } else { + log.error("message id:{} was not sent", value.id(), ex); + } + }); + } catch (Exception ex) { + log.error("send error, value:{}", value, ex); + } + } +} diff --git a/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/Runner.java b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/Runner.java new file mode 100644 index 00000000..16c25602 --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/Runner.java @@ -0,0 +1,18 @@ +package ru.demo.service; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Service; + +@Service +public class Runner implements CommandLineRunner { + private final ValueSource valueSource; + + public Runner(ValueSource valueSource) { + this.valueSource = valueSource; + } + + @Override + public void run(String... args) { + valueSource.generate(); + } +} diff --git a/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/StringValueSource.java b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/StringValueSource.java new file mode 100644 index 00000000..6554db75 --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/StringValueSource.java @@ -0,0 +1,30 @@ +package ru.demo.service; + +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.demo.model.StringValue; + +public class StringValueSource implements ValueSource { + private static final Logger log = LoggerFactory.getLogger(StringValueSource.class); + private final AtomicLong nextValue = new AtomicLong(1); + private final DataSender valueConsumer; + + public StringValueSource(DataSender dataSender) { + this.valueConsumer = dataSender; + } + + @Override + public void generate() { + var executor = Executors.newScheduledThreadPool(1); + executor.scheduleAtFixedRate(() -> valueConsumer.send(makeValue()), 0, 1, TimeUnit.SECONDS); + log.info("generation started"); + } + + private StringValue makeValue() { + var id = nextValue.getAndIncrement(); + return new StringValue(id, "stVal:" + id); + } +} diff --git a/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/ValueSource.java b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/ValueSource.java new file mode 100644 index 00000000..1204f5e6 --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/java/ru/demo/service/ValueSource.java @@ -0,0 +1,5 @@ +package ru.demo.service; + +public interface ValueSource { + void generate(); +} diff --git a/2024-01/spring-39-kafka/producer/src/main/resources/application.yml b/2024-01/spring-39-kafka/producer/src/main/resources/application.yml new file mode 100644 index 00000000..72fa1f46 --- /dev/null +++ b/2024-01/spring-39-kafka/producer/src/main/resources/application.yml @@ -0,0 +1,11 @@ +application: + kafka: + topic: "demo-topic" + +spring: + kafka: + producer: + bootstrap-servers: "127.0.0.1:9092" + client-id: "demo-producer" + + From f69368cf297f0036d316418f99143b1a015d0fd1 Mon Sep 17 00:00:00 2001 From: vitalykutsenko Date: Mon, 8 Jul 2024 20:03:26 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=D0=9F=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D1=8B?= =?UTF-8?q?=20=D0=BA=20=D0=B7=D0=B0=D0=BD=D1=8F=D1=82=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ACL/src/main/resources/schema.json | 113 ++++++++++++++++++ .../oauth/src/main/resources/application.yml | 4 +- 2024-03/spring-25-SS-auth/WebFlux/pom.xml | 62 ++++++++++ .../java/ru/otus/spring/WebFluxStarter.java | 22 ++++ .../main/java/ru/otus/spring/data/Person.java | 41 +++++++ .../ru/otus/spring/data/PersonRepository.java | 11 ++ .../ru/otus/spring/rest/PersonController.java | 34 ++++++ .../security/SecurityConfiguration.java | 49 ++++++++ .../src/main/resources/application.yml | 1 + .../main/resources/static/authenticated.html | 9 ++ .../src/main/resources/static/index.html | 11 ++ .../src/main/resources/static/public.html | 9 ++ 2024-03/spring-25-SS-auth/classwork/pom.xml | 54 +++++++++ .../spring/SpringSecurityAuthorization.java | 13 ++ .../spring/controller/PagesController.java | 59 +++++++++ .../controller/SecurityControllerAdvice.java | 16 +++ .../security/MethodSecurityConfiguration.java | 10 ++ .../security/SecurityConfiguration.java | 47 ++++++++ .../ru/otus/spring/service/MyService.java | 17 +++ .../src/main/resources/templates/admin.html | 9 ++ .../resources/templates/authenticated.html | 9 ++ .../src/main/resources/templates/error.html | 9 ++ .../src/main/resources/templates/index.html | 15 +++ .../src/main/resources/templates/manager.html | 9 ++ .../src/main/resources/templates/public.html | 9 ++ .../src/main/resources/templates/success.html | 9 ++ .../src/main/resources/templates/user.html | 9 ++ 2024-03/spring-25-SS-auth/jwt/pom.xml | 78 ++++++++++++ .../java/ru/otus/security/jwt/JwtStarter.java | 12 ++ .../security/jwt/config/SecurityConfig.java | 97 +++++++++++++++ .../jwt/controller/HelloController.java | 14 +++ .../jwt/controller/TokenController.java | 39 ++++++ .../jwt/src/main/resources/app.key | 28 +++++ .../jwt/src/main/resources/app.pub | 9 ++ .../jwt/src/main/resources/application.yml | 10 ++ .../src/main/resources/templates/index.html | 11 ++ 2024-03/spring-25-SS-auth/solution2/pom.xml | 54 +++++++++ .../spring/SpringSecurityAuthorization.java | 13 ++ .../spring/controller/PagesController.java | 56 +++++++++ .../security/MethodSecurityConfiguration.java | 7 ++ .../security/SecurityConfiguration.java | 51 ++++++++ .../ru/otus/spring/service/MyService.java | 12 ++ .../src/main/resources/templates/admin.html | 9 ++ .../resources/templates/authenticated.html | 9 ++ .../src/main/resources/templates/error.html | 9 ++ .../src/main/resources/templates/index.html | 17 +++ .../src/main/resources/templates/manager.html | 9 ++ .../src/main/resources/templates/public.html | 9 ++ .../src/main/resources/templates/success.html | 9 ++ .../src/main/resources/templates/user.html | 9 ++ 50 files changed, 1229 insertions(+), 2 deletions(-) create mode 100644 2024-01/spring-26/ACL/src/main/resources/schema.json create mode 100644 2024-03/spring-25-SS-auth/WebFlux/pom.xml create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/WebFluxStarter.java create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/data/Person.java create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/data/PersonRepository.java create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/rest/PersonController.java create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/security/SecurityConfiguration.java create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/resources/application.yml create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/authenticated.html create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/index.html create mode 100644 2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/public.html create mode 100644 2024-03/spring-25-SS-auth/classwork/pom.xml create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/SpringSecurityAuthorization.java create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/controller/PagesController.java create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/controller/SecurityControllerAdvice.java create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/security/MethodSecurityConfiguration.java create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/security/SecurityConfiguration.java create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/service/MyService.java create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/admin.html create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/authenticated.html create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/error.html create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/index.html create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/manager.html create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/public.html create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/success.html create mode 100644 2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/user.html create mode 100644 2024-03/spring-25-SS-auth/jwt/pom.xml create mode 100644 2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/JwtStarter.java create mode 100644 2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/config/SecurityConfig.java create mode 100644 2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/controller/HelloController.java create mode 100644 2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/controller/TokenController.java create mode 100644 2024-03/spring-25-SS-auth/jwt/src/main/resources/app.key create mode 100644 2024-03/spring-25-SS-auth/jwt/src/main/resources/app.pub create mode 100644 2024-03/spring-25-SS-auth/jwt/src/main/resources/application.yml create mode 100644 2024-03/spring-25-SS-auth/jwt/src/main/resources/templates/index.html create mode 100644 2024-03/spring-25-SS-auth/solution2/pom.xml create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/SpringSecurityAuthorization.java create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/controller/PagesController.java create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/security/MethodSecurityConfiguration.java create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/security/SecurityConfiguration.java create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/service/MyService.java create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/admin.html create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/authenticated.html create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/error.html create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/index.html create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/manager.html create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/public.html create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/success.html create mode 100644 2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/user.html diff --git a/2024-01/spring-26/ACL/src/main/resources/schema.json b/2024-01/spring-26/ACL/src/main/resources/schema.json new file mode 100644 index 00000000..f3c13aa2 --- /dev/null +++ b/2024-01/spring-26/ACL/src/main/resources/schema.json @@ -0,0 +1,113 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost:8080", + "description": "Generated server url" + } + ], + "paths": { + "/message": { + "get": { + "tags": [ + "notice-message-controller" + ], + "operationId": "getAll", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NoticeMessage" + } + } + } + } + } + } + }, + "put": { + "tags": [ + "notice-message-controller" + ], + "operationId": "getById", + "parameters": [ + { + "name": "message", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/NoticeMessage" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/NoticeMessage" + } + } + } + } + } + } + }, + "/message/{id}": { + "get": { + "tags": [ + "notice-message-controller" + ], + "operationId": "getById_1", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/NoticeMessage" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "NoticeMessage": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "content": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/2024-01/spring-26/oauth/src/main/resources/application.yml b/2024-01/spring-26/oauth/src/main/resources/application.yml index 4e767b01..9534ebc4 100644 --- a/2024-01/spring-26/oauth/src/main/resources/application.yml +++ b/2024-01/spring-26/oauth/src/main/resources/application.yml @@ -4,8 +4,8 @@ spring: client: registration: github: - clientId: b08e2b651985f2440b7f - clientSecret: 891acf5785a5af14ebe4827c0d9606b3385304cd + clientId: Ov23lij0iEG1mzTVZWoY + clientSecret: 65488851bae03f4661e478a26989ed30ece39245 logging: level: diff --git a/2024-03/spring-25-SS-auth/WebFlux/pom.xml b/2024-03/spring-25-SS-auth/WebFlux/pom.xml new file mode 100644 index 00000000..3eda6d55 --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + ru.otus + spring-framework-27-webflux + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 3.2.5 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.security + spring-security-core + + + org.springframework.security + spring-security-config + + + org.springframework.security + spring-security-web + + + + org.springframework.boot + spring-boot-starter-data-mongodb-reactive + + + + de.flapdoodle.embed + de.flapdoodle.embed.mongo + 4.6.1 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/WebFluxStarter.java b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/WebFluxStarter.java new file mode 100644 index 00000000..7a06898e --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/WebFluxStarter.java @@ -0,0 +1,22 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerResponse; + +@SpringBootApplication +public class WebFluxStarter { + + public static void main( String[] args ) { + SpringApplication.run( WebFluxStarter.class ); + } + + @Bean + RouterFunction staticResourceRouter() { + return RouterFunctions.resources( "/**.html", new ClassPathResource( "static/" ) ); + } +} diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/data/Person.java b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/data/Person.java new file mode 100644 index 00000000..bedf08b0 --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/data/Person.java @@ -0,0 +1,41 @@ +package ru.otus.spring.data; + +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document +public class Person { + + @Id + private String id; + + private String name; + + public Person() { + } + + public Person( String name ) { + this.name = name; + } + + public Person( String id, String name ) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId( String id ) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName( String name ) { + this.name = name; + } +} diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/data/PersonRepository.java b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/data/PersonRepository.java new file mode 100644 index 00000000..8f5f3d67 --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/data/PersonRepository.java @@ -0,0 +1,11 @@ +package ru.otus.spring.data; + +import org.springframework.data.mongodb.repository.ReactiveMongoRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Mono; + +@Repository +public interface PersonRepository extends ReactiveMongoRepository { + + Mono findByName( String string ); +} diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/rest/PersonController.java b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/rest/PersonController.java new file mode 100644 index 00000000..7af4b0a9 --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/rest/PersonController.java @@ -0,0 +1,34 @@ +package ru.otus.spring.rest; + +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import ru.otus.spring.data.Person; +import ru.otus.spring.data.PersonRepository; + +@RestController +public class PersonController { + + private final PersonRepository personRepository; + + public PersonController( PersonRepository personRepository ) { + this.personRepository = personRepository; + } + + @GetMapping("/person") + public Flux getAll() { + return personRepository.findAll(); + } + + @GetMapping("/person/find") + public Mono find( @RequestParam("name") String name ) { + return personRepository.findByName( name ) + .cache(); + } + + + @PostMapping("/person") + public Mono savePerson( @RequestBody Person person ) { + return personRepository.save( person ); + } +} diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/security/SecurityConfiguration.java b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/security/SecurityConfiguration.java new file mode 100644 index 00000000..6dee3e75 --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/java/ru/otus/spring/security/SecurityConfiguration.java @@ -0,0 +1,49 @@ +package ru.otus.spring.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; +import org.springframework.security.core.userdetails.ReactiveUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.server.SecurityWebFilterChain; + +@EnableWebFluxSecurity +@Configuration +public class SecurityConfiguration { + + @Bean + public SecurityWebFilterChain springWebFilterChain( ServerHttpSecurity http ) { + return http + .authorizeExchange((exchanges)->exchanges + .pathMatchers( HttpMethod.GET, "/authenticated.html" ).authenticated() + .pathMatchers( "/person" ).hasAnyRole( "USER" ) + .anyExchange().denyAll() + ) + .httpBasic( Customizer.withDefaults()) + .build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return NoOpPasswordEncoder.getInstance(); + } + + @Bean + public ReactiveUserDetailsService userDetailsService() { + UserDetails user = User + .withUsername( "user" ) + .password( "password" ) + .roles( "USER" ) + .build(); + return new MapReactiveUserDetailsService( user ); + } +} diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/application.yml b/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/application.yml new file mode 100644 index 00000000..add8d9b3 --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/application.yml @@ -0,0 +1 @@ +spring.mongodb.embedded.version: 4.6.1 \ No newline at end of file diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/authenticated.html b/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/authenticated.html new file mode 100644 index 00000000..9f8b0d7e --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/authenticated.html @@ -0,0 +1,9 @@ + + + + + + +Только для авторизованных + + diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/index.html b/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/index.html new file mode 100644 index 00000000..a89ba331 --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/index.html @@ -0,0 +1,11 @@ + + + + + + +/public.html +
+/authenticated.html + + diff --git a/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/public.html b/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/public.html new file mode 100644 index 00000000..77188469 --- /dev/null +++ b/2024-03/spring-25-SS-auth/WebFlux/src/main/resources/static/public.html @@ -0,0 +1,9 @@ + + + + + + +Доступен всем + + diff --git a/2024-03/spring-25-SS-auth/classwork/pom.xml b/2024-03/spring-25-SS-auth/classwork/pom.xml new file mode 100644 index 00000000..747bcc45 --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + ru.otus + classwork + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 3.2.5 + + + + + UTF-8 + UTF-8 + 17 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-security + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/SpringSecurityAuthorization.java b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/SpringSecurityAuthorization.java new file mode 100644 index 00000000..6b3bf909 --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/SpringSecurityAuthorization.java @@ -0,0 +1,13 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringSecurityAuthorization { + + public static void main(String[] args) { + SpringApplication.run(SpringSecurityAuthorization.class); + } + +} diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/controller/PagesController.java b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/controller/PagesController.java new file mode 100644 index 00000000..c7e8419c --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/controller/PagesController.java @@ -0,0 +1,59 @@ +package ru.otus.spring.controller; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import ru.otus.spring.service.MyService; + +@Controller +public class PagesController { + + private final MyService myService; + + public PagesController(MyService myService) { + this.myService = myService; + } + + @GetMapping("/") + public String indexPage() { + return "index"; + } + + @GetMapping("/public") + public String publicPage() { + return "public"; + } + + @GetMapping("/user") + public String userPage() { + myService.onlyUser(); + return "user"; + } + + @GetMapping("/manager") + public String managerPage() { + return "manager"; + } + + @GetMapping("/admin") + //@Secured( "ADMIN" ) + public String adminPage() { + myService.onlyUser(); + //myService.onlyAdmin(); + return "admin"; + } + + @GetMapping("/authenticated") + public String authenticatedPage() { + UserDetails userDetails = (UserDetails) SecurityContextHolder + .getContext().getAuthentication().getPrincipal(); + System.out.println(userDetails.getUsername()); + return "authenticated"; + } + + @GetMapping("/success") + public String successPage() { + return "success"; + } +} diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/controller/SecurityControllerAdvice.java b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/controller/SecurityControllerAdvice.java new file mode 100644 index 00000000..bdbb5ea3 --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/controller/SecurityControllerAdvice.java @@ -0,0 +1,16 @@ +package ru.otus.spring.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import java.util.Optional; + +@ControllerAdvice +public class SecurityControllerAdvice { + @ExceptionHandler(AccessDeniedException.class) + public ResponseEntity accessError() { + return ResponseEntity.of(Optional.of("Неудачник")); + } +} diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/security/MethodSecurityConfiguration.java b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/security/MethodSecurityConfiguration.java new file mode 100644 index 00000000..cacbe63f --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/security/MethodSecurityConfiguration.java @@ -0,0 +1,10 @@ +package ru.otus.spring.security; + +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; + +@EnableGlobalMethodSecurity( + securedEnabled = true, + prePostEnabled = true +) +public class MethodSecurityConfiguration { +} diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/security/SecurityConfiguration.java b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/security/SecurityConfiguration.java new file mode 100644 index 00000000..3aa36297 --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/security/SecurityConfiguration.java @@ -0,0 +1,47 @@ +package ru.otus.spring.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.Customizer; +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.configurers.AbstractHttpConfigurer; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import java.util.ArrayList; + +@EnableWebSecurity +public class SecurityConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain( HttpSecurity http ) throws Exception { + http + .csrf( AbstractHttpConfigurer::disable ) + .authorizeHttpRequests( ( authorize ) -> authorize + .requestMatchers( "/public", "/" ).permitAll() + .requestMatchers( "/authenticated", "/success" ).authenticated() + .requestMatchers( "/manager" ).hasAnyRole( "MANAGER" ) + .requestMatchers( "/user" ).hasAnyRole( "ADMIN", "USER" ) + .anyRequest().denyAll() + ) + .formLogin( Customizer.withDefaults() ) + + ; + return http.build(); + } + + @Bean + public InMemoryUserDetailsManager userDetailsService() { + var users = new ArrayList(); + users.add( User + .withDefaultPasswordEncoder().username( "admin" ).password( "password" ).roles( "ADMIN" ) + .build() ); + users.add( User + .withDefaultPasswordEncoder().username( "user" ).password( "password" ).roles( "USER" ) + .build() ); + return new InMemoryUserDetailsManager( users ); + + } +} diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/service/MyService.java b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/service/MyService.java new file mode 100644 index 00000000..9c108f59 --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/java/ru/otus/spring/service/MyService.java @@ -0,0 +1,17 @@ +package ru.otus.spring.service; + +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; + +@Service +public class MyService { + @PreAuthorize("hasRole('ROLE_USER') && {new java.util.Random().nextInt()%2 == 0}") + public String onlyUser() { + return "My love"; + } + + @Secured("ADMIN") + public void onlyAdmin() { + } +} diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/admin.html b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/admin.html new file mode 100644 index 00000000..aa1c9563 --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/admin.html @@ -0,0 +1,9 @@ + + + + + + +Страница с доступом только админу + + diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/authenticated.html b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/authenticated.html new file mode 100644 index 00000000..e4756c01 --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/authenticated.html @@ -0,0 +1,9 @@ + + + + + + +Только для аторизованных + + diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/error.html b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/error.html new file mode 100644 index 00000000..f28b51df --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/error.html @@ -0,0 +1,9 @@ + + + + + + +Вам доступ запрещён! + + diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/index.html b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/index.html new file mode 100644 index 00000000..d4c54ecd --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/index.html @@ -0,0 +1,15 @@ + + + + + + +/public +
+/authenticated +
+/user +
+/admin + + diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/manager.html b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/manager.html new file mode 100644 index 00000000..dd4a77af --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/manager.html @@ -0,0 +1,9 @@ + + + + + + +Доступ к MANAGER + + diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/public.html b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/public.html new file mode 100644 index 00000000..77188469 --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/public.html @@ -0,0 +1,9 @@ + + + + + + +Доступен всем + + diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/success.html b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/success.html new file mode 100644 index 00000000..4e2a37cd --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/success.html @@ -0,0 +1,9 @@ + + + + + + +Вы успешно вошли ! + + diff --git a/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/user.html b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/user.html new file mode 100644 index 00000000..a794bc2d --- /dev/null +++ b/2024-03/spring-25-SS-auth/classwork/src/main/resources/templates/user.html @@ -0,0 +1,9 @@ + + + + + + +Доступ к USER + + diff --git a/2024-03/spring-25-SS-auth/jwt/pom.xml b/2024-03/spring-25-SS-auth/jwt/pom.xml new file mode 100644 index 00000000..87fbb4a2 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + ru.otus + spring-framework-27-jwt + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 3.2.5 + + + + 2.6.11 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + + org.springdoc + springdoc-openapi-ui + 1.6.9 + + + + org.webjars + jquery + 3.4.1 + + + org.webjars + bootstrap + 4.3.1 + + + org.webjars + webjars-locator-core + + + org.webjars + js-cookie + 2.1.0 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/JwtStarter.java b/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/JwtStarter.java new file mode 100644 index 00000000..79f664b0 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/JwtStarter.java @@ -0,0 +1,12 @@ +package ru.otus.security.jwt; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class JwtStarter { + public static void main(String[] args){ + SpringApplication.run( JwtStarter.class, args ); + } +} diff --git a/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/config/SecurityConfig.java b/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/config/SecurityConfig.java new file mode 100644 index 00000000..043fc137 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/config/SecurityConfig.java @@ -0,0 +1,97 @@ +package ru.otus.security.jwt.config; + +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jose.jwk.source.ImmutableJWKSet; +import com.nimbusds.jose.jwk.source.JWKSource; +import com.nimbusds.jose.proc.SecurityContext; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; +import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; +import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +@Configuration +public class SecurityConfig { + + @Value("${jwt.public.key}") + RSAPublicKey key; + + @Value("${jwt.private.key}") + RSAPrivateKey priv; + + @Bean + public SecurityFilterChain securityFilterChain( HttpSecurity http ) throws Exception { + + http + .authorizeHttpRequests( ( authorize ) -> authorize + .anyRequest().authenticated() + ) + .csrf( AbstractHttpConfigurer::disable ) + .httpBasic( Customizer.withDefaults() ) + .oauth2ResourceServer( OAuth2ResourceServerConfigurer::jwt ) + .sessionManagement( ( session ) -> session.sessionCreationPolicy( SessionCreationPolicy.STATELESS ) ) + .exceptionHandling( ( exceptions ) -> exceptions + .authenticationEntryPoint( new BearerTokenAuthenticationEntryPoint() ) + .accessDeniedHandler( new BearerTokenAccessDeniedHandler() ) + ); + + return http.build(); + } + + @Bean + UserDetailsService users() { + + return new InMemoryUserDetailsManager( + User.withUsername( "user" ) + .password( "password" ) + .authorities( "app" ) + .build() + ); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new PasswordEncoder() { + @Override + public String encode( CharSequence charSequence ) { + return charSequence.toString(); + } + + @Override + public boolean matches( CharSequence charSequence, String s ) { + return charSequence.toString().equals( s ); + } + }; + } + + @Bean + JwtDecoder jwtDecoder() { + return NimbusJwtDecoder.withPublicKey( this.key ).build(); + } + + @Bean + JwtEncoder jwtEncoder() { + JWK jwk = new RSAKey.Builder( this.key ).privateKey( this.priv ).build(); + JWKSource jwks = new ImmutableJWKSet<>( new JWKSet( jwk ) ); + return new NimbusJwtEncoder( jwks ); + } +} diff --git a/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/controller/HelloController.java b/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/controller/HelloController.java new file mode 100644 index 00000000..4ff42a49 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/controller/HelloController.java @@ -0,0 +1,14 @@ +package ru.otus.security.jwt.controller; + +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HelloController { + + @GetMapping("/hello") + public String hello( Authentication authentication ) { + return "Hello, " + authentication.getName() + "!"; + } +} diff --git a/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/controller/TokenController.java b/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/controller/TokenController.java new file mode 100644 index 00000000..c5420966 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/src/main/java/ru/otus/security/jwt/controller/TokenController.java @@ -0,0 +1,39 @@ +package ru.otus.security.jwt.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.jwt.JwtClaimsSet; +import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.jwt.JwtEncoderParameters; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.Instant; +import java.util.stream.Collectors; + +@RestController +public class TokenController { + + @Autowired + JwtEncoder encoder; + + @PostMapping("/token") + public String token( Authentication authentication) { + Instant now = Instant.now(); + long expiry = 36000L; + // @formatter:off + String scope = authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect( Collectors.joining(" ")); + JwtClaimsSet claims = JwtClaimsSet.builder() + .issuer("self") + .issuedAt(now) + .expiresAt(now.plusSeconds(expiry)) + .subject(authentication.getName()) + .claim("scope", scope) + .build(); + // @formatter:on + return this.encoder.encode( JwtEncoderParameters.from(claims)).getTokenValue(); + } +} diff --git a/2024-03/spring-25-SS-auth/jwt/src/main/resources/app.key b/2024-03/spring-25-SS-auth/jwt/src/main/resources/app.key new file mode 100644 index 00000000..53510079 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/src/main/resources/app.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcWWomvlNGyQhA +iB0TcN3sP2VuhZ1xNRPxr58lHswC9Cbtdc2hiSbe/sxAvU1i0O8vaXwICdzRZ1JM +g1TohG9zkqqjZDhyw1f1Ic6YR/OhE6NCpqERy97WMFeW6gJd1i5inHj/W19GAbqK +LhSHGHqIjyo0wlBf58t+qFt9h/EFBVE/LAGQBsg/jHUQCxsLoVI2aSELGIw2oSDF +oiljwLaQl0n9khX5ZbiegN3OkqodzCYHwWyu6aVVj8M1W9RIMiKmKr09s/gf31Nc +3WjvjqhFo1rTuurWGgKAxJLL7zlJqAKjGWbIT4P6h/1Kwxjw6X23St3OmhsG6HIn ++jl1++MrAgMBAAECggEBAMf820wop3pyUOwI3aLcaH7YFx5VZMzvqJdNlvpg1jbE +E2Sn66b1zPLNfOIxLcBG8x8r9Ody1Bi2Vsqc0/5o3KKfdgHvnxAB3Z3dPh2WCDek +lCOVClEVoLzziTuuTdGO5/CWJXdWHcVzIjPxmK34eJXioiLaTYqN3XKqKMdpD0ZG +mtNTGvGf+9fQ4i94t0WqIxpMpGt7NM4RHy3+Onggev0zLiDANC23mWrTsUgect/7 +62TYg8g1bKwLAb9wCBT+BiOuCc2wrArRLOJgUkj/F4/gtrR9ima34SvWUyoUaKA0 +bi4YBX9l8oJwFGHbU9uFGEMnH0T/V0KtIB7qetReywkCgYEA9cFyfBIQrYISV/OA ++Z0bo3vh2aL0QgKrSXZ924cLt7itQAHNZ2ya+e3JRlTczi5mnWfjPWZ6eJB/8MlH +Gpn12o/POEkU+XjZZSPe1RWGt5g0S3lWqyx9toCS9ACXcN9tGbaqcFSVI73zVTRA +8J9grR0fbGn7jaTlTX2tnlOTQ60CgYEA5YjYpEq4L8UUMFkuj+BsS3u0oEBnzuHd +I9LEHmN+CMPosvabQu5wkJXLuqo2TxRnAznsA8R3pCLkdPGoWMCiWRAsCn979TdY +QbqO2qvBAD2Q19GtY7lIu6C35/enQWzJUMQE3WW0OvjLzZ0l/9mA2FBRR+3F9A1d +rBdnmv0c3TcCgYEAi2i+ggVZcqPbtgrLOk5WVGo9F1GqUBvlgNn30WWNTx4zIaEk +HSxtyaOLTxtq2odV7Kr3LGiKxwPpn/T+Ief+oIp92YcTn+VfJVGw4Z3BezqbR8lA +Uf/+HF5ZfpMrVXtZD4Igs3I33Duv4sCuqhEvLWTc44pHifVloozNxYfRfU0CgYBN +HXa7a6cJ1Yp829l62QlJKtx6Ymj95oAnQu5Ez2ROiZMqXRO4nucOjGUP55Orac1a +FiGm+mC/skFS0MWgW8evaHGDbWU180wheQ35hW6oKAb7myRHtr4q20ouEtQMdQIF +snV39G1iyqeeAsf7dxWElydXpRi2b68i3BIgzhzebQKBgQCdUQuTsqV9y/JFpu6H +c5TVvhG/ubfBspI5DhQqIGijnVBzFT//UfIYMSKJo75qqBEyP2EJSmCsunWsAFsM +TszuiGTkrKcZy9G0wJqPztZZl2F2+bJgnA6nBEV7g5PA4Af+QSmaIhRwqGDAuROR +47jndeyIaMTNETEmOnms+as17g== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/2024-03/spring-25-SS-auth/jwt/src/main/resources/app.pub b/2024-03/spring-25-SS-auth/jwt/src/main/resources/app.pub new file mode 100644 index 00000000..0b2ee7b3 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/src/main/resources/app.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd +7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv +c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6 +iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2 +kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o +RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj +KwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/2024-03/spring-25-SS-auth/jwt/src/main/resources/application.yml b/2024-03/spring-25-SS-auth/jwt/src/main/resources/application.yml new file mode 100644 index 00000000..ad5d3850 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/src/main/resources/application.yml @@ -0,0 +1,10 @@ +logging: + level: + root: INFO + org.springframework.web: INFO + org.springframework.security: INFO +# org.springframework.boot.autoconfigure: DEBUG + +jwt: + private.key: classpath:app.key + public.key: classpath:app.pub \ No newline at end of file diff --git a/2024-03/spring-25-SS-auth/jwt/src/main/resources/templates/index.html b/2024-03/spring-25-SS-auth/jwt/src/main/resources/templates/index.html new file mode 100644 index 00000000..fa9cb9f6 --- /dev/null +++ b/2024-03/spring-25-SS-auth/jwt/src/main/resources/templates/index.html @@ -0,0 +1,11 @@ + + + + + + + +
+/swagger + + diff --git a/2024-03/spring-25-SS-auth/solution2/pom.xml b/2024-03/spring-25-SS-auth/solution2/pom.xml new file mode 100644 index 00000000..a2c07373 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + ru.otus + solution2 + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.7.8 + + + + + UTF-8 + UTF-8 + 17 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-security + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/SpringSecurityAuthorization.java b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/SpringSecurityAuthorization.java new file mode 100644 index 00000000..6b3bf909 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/SpringSecurityAuthorization.java @@ -0,0 +1,13 @@ +package ru.otus.spring; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringSecurityAuthorization { + + public static void main(String[] args) { + SpringApplication.run(SpringSecurityAuthorization.class); + } + +} diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/controller/PagesController.java b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/controller/PagesController.java new file mode 100644 index 00000000..c2350307 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/controller/PagesController.java @@ -0,0 +1,56 @@ +package ru.otus.spring.controller; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import ru.otus.spring.service.MyService; + +@Controller +public class PagesController { + + private final MyService myService; + + public PagesController(MyService myService) { + this.myService = myService; + } + + @GetMapping("/") + public String indexPage() { + return "index"; + } + + @GetMapping("/public") + public String publicPage() { + return "public"; + } + + @GetMapping("/user") + public String userPage() { + return "user"; + } + + @GetMapping("/manager") + public String managerPage() { + myService.onlyAdmin(); + return "manager"; + } + + @GetMapping("/admin") + public String adminPage() { + return "admin"; + } + + @GetMapping("/authenticated") + public String authenticatedPage() { + UserDetails userDetails = (UserDetails) SecurityContextHolder + .getContext().getAuthentication().getPrincipal(); + System.out.println(userDetails.getUsername()); + return "authenticated"; + } + + @GetMapping("/success") + public String successPage() { + return "success"; + } +} diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/security/MethodSecurityConfiguration.java b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/security/MethodSecurityConfiguration.java new file mode 100644 index 00000000..d7fe2d0d --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/security/MethodSecurityConfiguration.java @@ -0,0 +1,7 @@ +package ru.otus.spring.security; + +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; + +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class MethodSecurityConfiguration { +} diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/security/SecurityConfiguration.java b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/security/SecurityConfiguration.java new file mode 100644 index 00000000..b0dadc85 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/security/SecurityConfiguration.java @@ -0,0 +1,51 @@ +package ru.otus.spring.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +import java.util.ArrayList; + +@EnableWebSecurity +public class SecurityConfiguration { + + + @Bean + public SecurityFilterChain securityFilterChain( HttpSecurity http ) throws Exception { + http + .csrf().disable() +// .sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS) +// .and() + .authorizeHttpRequests( ( authorize ) -> authorize + .antMatchers( "/public", "/" ).permitAll() + .antMatchers( "/authenticated", "/success" ).authenticated() + .antMatchers( "/manager" ).hasAnyRole( "MANAGER" ) + .antMatchers( "/user" ).hasAnyRole( "ADMIN", "USER" ) + .anyRequest().denyAll() + ) + .formLogin() + + ; + return http.build(); + } + + @Bean + public InMemoryUserDetailsManager userDetailsService() { + var users = new ArrayList(); + users.add( User + .withDefaultPasswordEncoder().username( "admin" ).password( "password" ).roles( "ADMIN" ) + .build() ); + users.add( User + .withDefaultPasswordEncoder().username( "user" ).password( "password" ).roles( "USER" ) + .build() ); + users.add( User + .withDefaultPasswordEncoder().username( "manager" ).password( "password" ).roles( "MANAGER", "USER" ) + .build() ); + return new InMemoryUserDetailsManager( users ); + + } +} diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/service/MyService.java b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/service/MyService.java new file mode 100644 index 00000000..3b316f45 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/java/ru/otus/spring/service/MyService.java @@ -0,0 +1,12 @@ +package ru.otus.spring.service; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; + +@Service +public class MyService { + + @PreAuthorize("hasRole('ADMIN')") + public void onlyAdmin() { + } +} diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/admin.html b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/admin.html new file mode 100644 index 00000000..aa1c9563 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/admin.html @@ -0,0 +1,9 @@ + + + + + + +Страница с доступом только админу + + diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/authenticated.html b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/authenticated.html new file mode 100644 index 00000000..e4756c01 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/authenticated.html @@ -0,0 +1,9 @@ + + + + + + +Только для аторизованных + + diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/error.html b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/error.html new file mode 100644 index 00000000..f28b51df --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/error.html @@ -0,0 +1,9 @@ + + + + + + +Вам доступ запрещён! + + diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/index.html b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/index.html new file mode 100644 index 00000000..9a8b9382 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/index.html @@ -0,0 +1,17 @@ + + + + + + +/public +
+/authenticated +
+/user +
+/manager +
+/admin + + diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/manager.html b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/manager.html new file mode 100644 index 00000000..dd4a77af --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/manager.html @@ -0,0 +1,9 @@ + + + + + + +Доступ к MANAGER + + diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/public.html b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/public.html new file mode 100644 index 00000000..77188469 --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/public.html @@ -0,0 +1,9 @@ + + + + + + +Доступен всем + + diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/success.html b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/success.html new file mode 100644 index 00000000..4e2a37cd --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/success.html @@ -0,0 +1,9 @@ + + + + + + +Вы успешно вошли ! + + diff --git a/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/user.html b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/user.html new file mode 100644 index 00000000..a794bc2d --- /dev/null +++ b/2024-03/spring-25-SS-auth/solution2/src/main/resources/templates/user.html @@ -0,0 +1,9 @@ + + + + + + +Доступ к USER + +