mirror of
https://github.com/OtusTeam/Spring.git
synced 2026-06-20 11:14:34 +00:00
Merge branch 'master' of https://github.com/OtusTeam/Spring
This commit is contained in:
commit
e79093e482
70 files changed
+1844
-2
No files matched your search
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ spring:
|
||||
client:
|
||||
registration:
|
||||
github:
|
||||
clientId: b08e2b651985f2440b7f
|
||||
clientSecret: 891acf5785a5af14ebe4827c0d9606b3385304cd
|
||||
clientId: Ov23lij0iEG1mzTVZWoY
|
||||
clientSecret: 65488851bae03f4661e478a26989ed30ece39245
|
||||
|
||||
logging:
|
||||
level:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-39-kafka</artifactId>
|
||||
<version>1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>consumer</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<String, StringValue> 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<String, StringValue>(props);
|
||||
kafkaConsumerFactory.setValueDeserializer(new JsonDeserializer<>(mapper));
|
||||
return kafkaConsumerFactory;
|
||||
}
|
||||
|
||||
@Bean("listenerContainerFactory")
|
||||
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, StringValue>>
|
||||
listenerContainerFactory(ConsumerFactory<String, StringValue> consumerFactory) {
|
||||
var factory = new ConcurrentKafkaListenerContainerFactory<String, StringValue>();
|
||||
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<StringValue> values) {
|
||||
log.info("values, values.size:{}", values.size());
|
||||
stringValueConsumer.accept(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package ru.demo.model;
|
||||
|
||||
public record StringValue(long id, String value) {}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package ru.demo.service;
|
||||
|
||||
import java.util.List;
|
||||
import ru.demo.model.StringValue;
|
||||
|
||||
public interface StringValueConsumer {
|
||||
|
||||
void accept(List<StringValue> value);
|
||||
}
|
||||
+17
@@ -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<StringValue> values) {
|
||||
for (var value : values) {
|
||||
log.info("log:{}", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-39-kafka</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>consumer</module>
|
||||
<module>producer</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<maven-enforcer-plugin.version>3.0.0-M3</maven-enforcer-plugin.version>
|
||||
<maven-assembly-plugin.version>3.1.1</maven-assembly-plugin.version>
|
||||
<minimal.maven.version>3.3.9</minimal.maven.version>
|
||||
<dependencyManagement.version>1.0.12.RELEASE</dependencyManagement.version>
|
||||
<springframeworkBoot.version>3.0.5</springframeworkBoot.version>
|
||||
<jsr305.version>3.0.2</jsr305.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${springframeworkBoot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>${jsr305.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>${maven-enforcer-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce-maven</id>
|
||||
<goals>
|
||||
<goal>enforce</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<rules>
|
||||
<dependencyConvergence/>
|
||||
<requireMavenVersion>
|
||||
<version>${minimal.maven.version}</version>
|
||||
</requireMavenVersion>
|
||||
</rules>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>${maven-shade-plugin.version}</version>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>${maven-assembly-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-39-kafka</artifactId>
|
||||
<version>1.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>producer</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<String, StringValue> 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<String, StringValue>(props);
|
||||
kafkaProducerFactory.setValueSerializer(new JsonSerializer<>(mapper));
|
||||
return kafkaProducerFactory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KafkaTemplate<String, StringValue> kafkaTemplate(
|
||||
ProducerFactory<String, StringValue> 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<String, StringValue> kafkaTemplate) {
|
||||
return new DataSenderKafka(
|
||||
topic.name(),
|
||||
kafkaTemplate,
|
||||
stringValue -> log.info("asked, value:{}", stringValue));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StringValueSource stringValueSource(DataSender dataSender) {
|
||||
return new StringValueSource(dataSender);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package ru.demo.model;
|
||||
|
||||
public record StringValue(long id, String value) {}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.demo.service;
|
||||
|
||||
import ru.demo.model.StringValue;
|
||||
|
||||
public interface DataSender {
|
||||
void send(StringValue value);
|
||||
}
|
||||
@@ -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<String, StringValue> template;
|
||||
|
||||
private final Consumer<StringValue> sendAsk;
|
||||
|
||||
private final String topic;
|
||||
|
||||
public DataSenderKafka(
|
||||
String topic,
|
||||
KafkaTemplate<String, StringValue> template,
|
||||
Consumer<StringValue> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.demo.service;
|
||||
|
||||
public interface ValueSource {
|
||||
void generate();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
application:
|
||||
kafka:
|
||||
topic: "demo-topic"
|
||||
|
||||
spring:
|
||||
kafka:
|
||||
producer:
|
||||
bootstrap-servers: "127.0.0.1:9092"
|
||||
client-id: "demo-producer"
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-framework-27-webflux</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.5</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.flapdoodle.embed</groupId>
|
||||
<artifactId>de.flapdoodle.embed.mongo</artifactId>
|
||||
<version>4.6.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -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<ServerResponse> staticResourceRouter() {
|
||||
return RouterFunctions.resources( "/**.html", new ClassPathResource( "static/" ) );
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+11
@@ -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<Person, String> {
|
||||
|
||||
Mono<Person> findByName( String string );
|
||||
}
|
||||
+34
@@ -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<Person> getAll() {
|
||||
return personRepository.findAll();
|
||||
}
|
||||
|
||||
@GetMapping("/person/find")
|
||||
public Mono<Person> find( @RequestParam("name") String name ) {
|
||||
return personRepository.findByName( name )
|
||||
.cache();
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/person")
|
||||
public Mono<Person> savePerson( @RequestBody Person person ) {
|
||||
return personRepository.save( person );
|
||||
}
|
||||
}
|
||||
+49
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.mongodb.embedded.version: 4.6.1
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Только для авторизованных
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<a href="public.html">/public.html</a>
|
||||
<br>
|
||||
<a href="authenticated.html">/authenticated.html</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Доступен всем
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>classwork</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+13
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -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";
|
||||
}
|
||||
}
|
||||
+16
@@ -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<String> accessError() {
|
||||
return ResponseEntity.of(Optional.of("Неудачник"));
|
||||
}
|
||||
}
|
||||
+10
@@ -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 {
|
||||
}
|
||||
+47
@@ -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<UserDetails>();
|
||||
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 );
|
||||
|
||||
}
|
||||
}
|
||||
+17
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Страница с доступом только админу
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Только для аторизованных
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Вам доступ запрещён!
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<a th:href="@{public}">/public</a>
|
||||
<br>
|
||||
<a th:href="@{authenticated}">/authenticated</a>
|
||||
<br>
|
||||
<a th:href="@{user}">/user</a>
|
||||
<br>
|
||||
<a th:href="@{admin}">/admin</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Доступ к MANAGER
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Доступен всем
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Вы успешно вошли !
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Доступ к USER
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>spring-framework-27-jwt</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.5</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<ehcache-core.version>2.6.11</ehcache-core.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<version>1.6.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>jquery</artifactId>
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
<version>4.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>webjars-locator-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>js-cookie</artifactId>
|
||||
<version>2.1.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
+97
@@ -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<SecurityContext> jwks = new ImmutableJWKSet<>( new JWKSet( jwk ) );
|
||||
return new NimbusJwtEncoder( jwks );
|
||||
}
|
||||
}
|
||||
+14
@@ -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() + "!";
|
||||
}
|
||||
}
|
||||
+39
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<br>
|
||||
<a th:href="@{swagger-ui.html}">/swagger</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.otus</groupId>
|
||||
<artifactId>solution2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.7.8</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+13
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -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";
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ru.otus.spring.security;
|
||||
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class MethodSecurityConfiguration {
|
||||
}
|
||||
+51
@@ -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<UserDetails>();
|
||||
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 );
|
||||
|
||||
}
|
||||
}
|
||||
+12
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Страница с доступом только админу
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Только для аторизованных
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Вам доступ запрещён!
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<a th:href="@{public}">/public</a>
|
||||
<br>
|
||||
<a th:href="@{authenticated}">/authenticated</a>
|
||||
<br>
|
||||
<a th:href="@{user}">/user</a>
|
||||
<br>
|
||||
<a th:href="@{manager}">/manager</a>
|
||||
<br>
|
||||
<a th:href="@{admin}">/admin</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Доступ к MANAGER
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Доступен всем
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Вы успешно вошли !
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Доступ к USER
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user