spring boot etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
spring boot etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

Spring Boot ve Redis Önbellek Kullanarak Müşteri Verilerini Hızlandırma: Adım Adım Rehber

 İşte müşteri bilgilerini Redis cache'de tutan basit bir Spring Boot uygulamasının örnek senaryosu ve kodları:



### Gereksinimler


- Spring Boot

- Spring Data JPA

- Spring Data Redis

- H2 Database (veya herhangi bir veritabanı)

- Redis Server


### 1. Proje Yapılandırması


#### `pom.xml` veya `build.gradle`


```xml

<dependencies>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-data-jpa</artifactId>

    </dependency>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-data-redis</artifactId>

    </dependency>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

    <dependency>

        <groupId>com.h2database</groupId>

        <artifactId>h2</artifactId>

        <scope>runtime</scope>

    </dependency>

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-test</artifactId>

        <scope>test</scope>

    </dependency>

</dependencies>

```


### 2. Redis ve JPA Yapılandırması


#### `application.properties`


```properties

# H2 Database Configuration

spring.datasource.url=jdbc:h2:mem:testdb

spring.datasource.driverClassName=org.h2.Driver

spring.datasource.username=sa

spring.datasource.password=password

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

spring.h2.console.enabled=true


# Redis Configuration

spring.redis.host=localhost

spring.redis.port=6379

```


### 3. Entity Sınıfı


#### `Customer.java`


```java

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.GenerationType;

import javax.persistence.Id;

import java.io.Serializable;


@Entity

public class Customer implements Serializable {


    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    private String name;

    private String email;


    // Getters and Setters

}

```


### 4. Repository


#### `CustomerRepository.java`


```java

import org.springframework.data.jpa.repository.JpaRepository;


public interface CustomerRepository extends JpaRepository<Customer, Long> {

}

```


### 5. Service Sınıfı


#### `CustomerService.java`


```java

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.cache.annotation.Cacheable;

import org.springframework.stereotype.Service;


import java.util.Optional;


@Service

public class CustomerService {


    @Autowired

    private CustomerRepository customerRepository;


    @Cacheable(value = "customerCache", key = "#id")

    public Optional<Customer> getCustomerById(Long id) {

        return customerRepository.findById(id);

    }

}

```


### 6. Controller


#### `CustomerController.java`


```java

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RestController;


import java.util.Optional;


@RestController

public class CustomerController {


    @Autowired

    private CustomerService customerService;


    @GetMapping("/customers/{id}")

    public Optional<Customer> getCustomer(@PathVariable Long id) {

        return customerService.getCustomerById(id);

    }

}

```


### 7. Redis Cache Yapılandırması


#### `RedisConfig.java`


```java

import org.springframework.cache.annotation.EnableCaching;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.cache.RedisCacheConfiguration;

import org.springframework.data.redis.cache.RedisCacheManager;

import org.springframework.data.redis.connection.RedisConnectionFactory;

import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;

import org.springframework.data.redis.serializer.StringRedisSerializer;


import java.time.Duration;


@Configuration

@EnableCaching

public class RedisConfig {


    @Bean

    public RedisConnectionFactory redisConnectionFactory() {

        return new LettuceConnectionFactory();

    }


    @Bean

    public RedisTemplate<String, Object> redisTemplate() {

        RedisTemplate<String, Object> template = new RedisTemplate<>();

        template.setConnectionFactory(redisConnectionFactory());

        template.setKeySerializer(new StringRedisSerializer());

        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        return template;

    }


    @Bean

    public RedisCacheManager cacheManager() {

        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()

                .entryTtl(Duration.ofMinutes(10))

                .disableCachingNullValues();


        return RedisCacheManager.builder(redisConnectionFactory())

                .cacheDefaults(cacheConfig)

                .build();

    }

}

```


### 8. Ana Uygulama Sınıfı


#### `Application.java`


```java

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication

public class Application {

    public static void main(String[] args) {

        SpringApplication.run(Application.class, args);

    }

}

```


Bu örnek ile müşteri bilgilerini Redis cache'de tutan basit bir Spring Boot uygulaması oluşturabilirsiniz. Uygulama çalıştığında, `/customers/{id}` endpoint'ine yapılan isteklerde müşteri bilgileri önce Redis cache'den kontrol edilecek, eğer cache'de bulunmazsa veritabanından alınacak ve cache'e kaydedilecektir.

Spring Boot ile Kafka'dan Çoklu Partition Okuma: 5 Thread Kullanarak Paralel İşleme Rehberi

Kafka’dan 5 partition’a sahip bir topic okuyarak, Spring Boot uygulaması ile 5 thread kullanarak paralel okuma gerçekleştiren bir senaryo oluşturacağız. Bu senaryoda, Java ExecutorService kullanarak 5 thread ile paralel işlem yapacağız.



Adımlar

  1. Spring Boot Projesi Oluşturma
  2. Kafka Konfigürasyonu
  3. Kafka Consumer Oluşturma
  4. Thread Pool (ExecutorService) ile Paralel Okuma

1. Spring Boot Projesi Oluşturma

Spring Initializr kullanarak yeni bir Spring Boot projesi oluşturun ve aşağıdaki bağımlılıkları ekleyin:

  • Spring Web
  • Spring for Apache Kafka

2. Kafka Konfigürasyonu

application.properties dosyasını aşağıdaki gibi konfigüre edin:

properties
spring.kafka.bootstrap-servers=localhost:9092 spring.kafka.consumer.group-id=my-group spring.kafka.consumer.auto-offset-reset=earliest spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer

3. Kafka Consumer Oluşturma

KafkaConsumerConfig sınıfını oluşturarak Kafka Consumer konfigürasyonunu yapalım:

java
import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.common.serialization.StringDeserializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.annotation.EnableKafka; import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; import org.springframework.kafka.core.ConsumerFactory; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; import org.springframework.kafka.listener.config.ContainerProperties; import java.util.HashMap; import java.util.Map; @EnableKafka @Configuration public class KafkaConsumerConfig { @Bean public ConsumerFactory<String, String> consumerFactory() { Map<String, Object> props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092"); props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group"); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); return new DefaultKafkaConsumerFactory<>(props); } @Bean public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(consumerFactory()); return factory; } }

4. Kafka Listener ve Thread Pool (ExecutorService) ile Paralel Okuma

KafkaConsumerService sınıfını oluşturarak Kafka mesajlarını paralel olarak işleyelim:

java
import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Service; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @Service public class KafkaConsumerService { private final ExecutorService executorService; public KafkaConsumerService() { this.executorService = Executors.newFixedThreadPool(5); } @KafkaListener(topics = "my-topic", groupId = "my-group") public void listen(String message) { executorService.submit(() -> processMessage(message)); } public void processMessage(String message) { System.out.println("Processing message: " + message); // Mesajı işlemek için gereken kodları buraya ekleyin } public void shutdown() { executorService.shutdown(); try { if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { executorService.shutdownNow(); } } }

Spring Boot Uygulamasını Başlatma

Uygulamayı başlatarak Kafka topic’inden mesajları paralel olarak işlemeye başlayabilirsiniz.

Tüm Proje Dosya Yapısı:

css
src └── main └── java └── com └── example └── kafka ├── KafkaConsumerConfig.java ├── KafkaConsumerService.java └── KafkaApplication.java └── resources └── application.properties

KafkaApplication.java

java
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class KafkaApplication { public static void main(String[] args) { SpringApplication.run(KafkaApplication.class, args); } }

Bu yapılandırma ile Kafka’dan 5 partition’a sahip bir topic’i okuyarak, 5 thread ile paralel okuma gerçekleştirebilirsiniz. Mesajlar processMessage metodu içerisinde işlenir ve ExecutorService ile paralel işlenir.

Spring Boot DevTools Nedir? Geliştirme Modunda Özel Özellikler ve application-dev.yml Kullanımı



Spring Boot DevTools, Spring Boot uygulamaları geliştirmeyi ve test etmeyi kolaylaştırmak için tasarlanmış bir geliştirme zamanında yardımcı araçlar setidir. DevTools, geliştiricilere daha verimli bir çalışma ortamı sunarak geliştirme sürecini hızlandırır. Aşağıda Spring Boot DevTools'un bazı önemli özelliklerini bulabilirsiniz:

  1. Otomatik Yeniden Başlatma (Automatic Restart): Uygulama kodunda yapılan değişikliklerin otomatik olarak algılanıp uygulamanın yeniden başlatılmasını sağlar. Bu, her kod değişikliğinden sonra uygulamayı manuel olarak yeniden başlatma ihtiyacını ortadan kaldırır.

  2. LiveReload Entegrasyonu: LiveReload kullanarak tarayıcıyı otomatik olarak yeniler, böylece UI değişikliklerini anında görebilirsiniz.

  3. Hızlı Uygulama Yükleme (Fast Application Restarts): Uygulama yeniden başlatma süresini kısaltmak için sınıf yüklemeyi optimize eder.

  4. Özel Özellikler (Custom Properties): Sadece geliştirme modunda aktif olacak özel özellikler ekleyebilirsiniz. Örneğin, geliştirme ortamına özgü veritabanı yapılandırmaları veya hata ayıklama özellikleri eklemek.

  5. H2 Konsolu (H2 Console): Embedded H2 veritabanını kullandığınızda, H2 konsolunu otomatik olarak etkinleştirir, böylece veritabanı üzerinde doğrudan çalışabilirsiniz.

  6. Cache Devre Dışı Bırakma (Disabling Caching): Şablon motoru (Thymeleaf, Freemarker vb.) önbelleklerini devre dışı bırakarak, şablon değişikliklerini anında görebilmenizi sağlar.

Spring Boot DevTools kullanarak uygulamanızı geliştirirken zamandan tasarruf edebilir ve geliştirme sürecini daha verimli hale getirebilirsiniz. DevTools'u kullanmak için spring-boot-devtools bağımlılığını projenize eklemeniz yeterlidir:

xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>

Bu bağımlılığı ekledikten sonra, uygulamanızın geliştirme sürecinde DevTools özelliklerinden otomatik olarak faydalanabilirsiniz.


Spring Boot DevTools ile sadece geliştirme modunda aktif olacak özel özellikler ekleyebilirsiniz. Bu, uygulamanızın farklı ortamlar (geliştirme, test, üretim) için farklı yapılandırmalar kullanmasına olanak tanır. Geliştirme modunda özel özellikler eklemek için application.properties veya application.yml dosyalarına ortam bazlı yapılandırmalar ekleyebilirsiniz.

Spring Boot, profillere dayalı yapılandırmalar sunar. Örneğin, geliştirme ortamına özgü yapılandırmaları application-dev.properties veya application-dev.yml dosyasına ekleyebilirsiniz.

Örnek: Geliştirme Ortamı için Özel Veritabanı Yapılandırması

application.properties

properties
spring.profiles.active=dev

application-dev.properties

properties
# Geliştirme veritabanı yapılandırması spring.datasource.url=jdbc:h2:mem:devdb spring.datasource.username=sa spring.datasource.password=password spring.datasource.driver-class-name=org.h2.Driver # H2 konsolunu etkinleştir spring.h2.console.enabled=true # Şablon önbelleğini devre dışı bırakma spring.thymeleaf.cache=false

Örnek: Hata Ayıklama Özellikleri

application-dev.properties

properties
# Hata ayıklama için özel yapılandırmalar logging.level.org.springframework.web=DEBUG logging.level.com.yourapp.package=TRACE # Geliştirme ortamına özgü başka özellikler spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true

Bu yapılandırmalar sadece dev profili aktif olduğunda geçerli olacaktır. Üretim ortamında ise farklı bir profil (örneğin prod profili) kullanarak uygun yapılandırmaları yükleyebilirsiniz.

application-prod.properties

properties
# Üretim veritabanı yapılandırması spring.datasource.url=jdbc:mysql://prod-db-server/mydb spring.datasource.username=produser spring.datasource.password=prodpassword spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # H2 konsolunu devre dışı bırak spring.h2.console.enabled=false # Şablon önbelleğini etkinleştirme spring.thymeleaf.cache=true

Bu şekilde, geliştirme ve üretim ortamları için farklı yapılandırmalar kullanarak uygulamanızın farklı gereksinimlerini karşılayabilirsiniz. DevTools, geliştirme sırasında bu tür yapılandırmaların kolayca yönetilmesine yardımcı olur.

Spring Boot ile Kullanıcı Mikro Servisi Oluşturma: Java 21, Spring Security ve JWT Kullanarak Adım Adım Rehber



User Microservice - Baştan Kodlama

Aşağıda, tüm ayrıntılarıyla User microservice'i yeniden baştan kodlayacağız.

1. Proje Yapılandırması

1.1. pom.xml

xml
<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>com.example</groupId> <artifactId>user-service</artifactId> <version>1.0.0</version> <properties> <java.version>21</java.version> <spring.boot.version>3.1.0</spring.boot.version> <mapstruct.version>1.5.2.Final</mapstruct.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-data-jpa</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.springframework.security</groupId> <artifactId>spring-security-oauth2-jose</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>21</source> <target>21</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> </project>

2. application.yml

server: port: 8081 spring: datasource: url: jdbc:postgresql://localhost:5432/usersdb username: postgres password: password driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update show-sql: true security: oauth2: resourceserver: jwt: issuer-uri: http://localhost:8080/oauth2/default jwt: secret: myJwtSecret expiration: 86400000 # 1 day in milliseconds

3. Model, Repository, Service ve Controller Katmanları

3.1. User Entity

package com.example.userservice.model; import jakarta.persistence.*; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import lombok.Data; @Entity @Data public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank private String username; @NotBlank private String password; @Email @NotBlank private String email; @Enumerated(EnumType.STRING) private Role role; public enum Role { USER, ADMIN } }

3.2. UserRepository


package com.example.userservice.repository; import com.example.userservice.model.User; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByUsername(String username); }

3.3. DTO'lar ve Mapper

3.3.1. UserRequest


package com.example.userservice.dto; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import lombok.Data; @Data public class UserRequest { @NotBlank private String username; @NotBlank private String password; @Email @NotBlank private String email; @NotBlank private String role; }

3.3.2. UserResponse


package com.example.userservice.dto; import lombok.Data; @Data public class UserResponse { private Long id; private String username; private String email; private String role; }

3.3.3. UserMapper


package com.example.userservice.mapper; import com.example.userservice.dto.UserRequest; import com.example.userservice.dto.UserResponse; import com.example.userservice.model.User; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @Mapper(componentModel = "spring") public interface UserMapper { @Mapping(target = "role", expression =
"java(User.Role.valueOf(userRequest.getRole().toUpperCase()))") User toEntity(UserRequest userRequest); @Mapping(target = "role", source = "role") UserResponse toResponse(User user); }

3.4. UserService


package com.example.userservice.service; import com.example.userservice.model.User; import com.example.userservice.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; public User registerUser(User user) { user.setPassword(passwordEncoder.encode(user.getPassword())); return userRepository.save(user); } public User findByUsername(String username) { return userRepository.findByUsername(username).orElse(null); } }

3.5. UserController


package com.example.userservice.controller; import com.example.userservice.dto.UserRequest; import com.example.userservice.dto.UserResponse; import com.example.userservice.mapper.UserMapper; import com.example.userservice.model.User; import com.example.userservice.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") @RequiredArgsConstructor public class UserController { private final UserService userService; private final UserMapper userMapper; @PostMapping("/register") public ResponseEntity<UserResponse> registerUser(@Validated
@RequestBody UserRequest userRequest) { User user = userService.registerUser(userMapper.toEntity(userRequest)); return ResponseEntity.ok(userMapper.toResponse(user)); } @GetMapping("/{username}") public ResponseEntity<UserResponse> getUser(@PathVariable String username) { User user = userService.findByUsername(username); if (user == null) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok(userMapper.toResponse(user)); } }

4. Güvenlik Yapılandırması

4.1. SecurityConfig


package com.example.userservice.config; import com.example.userservice.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.
configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.
EnableWebSecurity; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig { private final UserService userService; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception { http .csrf().disable() .authorizeHttpRequests(authz -> authz .requestMatchers("/users/register").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2.jwt()); return http.build(); } @Bean public UserDetailsService userDetailsService() { return username -> userService.findByUsername(username); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager
(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } }

Sonuç

Bu yapılandırma ile User microservice'i baştan sona tamamladık. MapStruct kullanarak DTO'lar arasında dönüşümleri kolayca gerçekleştirdik ve güvenlik yapılandırmasıyla kullanıcı doğrulama işlemlerini sağladık. Bu yapılandırmayı diğer microservisler için de benzer şekilde uygulayabilirsiniz.


CustomUserDetails,JwtTokenProvider,JwtAuthenticationFilter,JwtAuthenticationToken

1. CustomUserDetails

1.1. CustomUserDetails


package com.example.userservice.security; import com.example.userservice.model.User; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.stream.Collectors; import java.util.stream.Stream; public class CustomUserDetails implements UserDetails { private final User user; public CustomUserDetails(User user) { this.user = user; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return Stream.of(new SimpleGrantedAuthority("ROLE_" +
user.getRole().name())) .collect(Collectors.toList()); } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getUsername(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }

2. JwtTokenProvider

2.1. JwtTokenProvider


package com.example.userservice.security; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; import java.util.Date; @Component public class JwtTokenProvider { @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expiration}") private Long jwtExpiration; public String generateToken(Authentication authentication) { CustomUserDetails userPrincipal = (CustomUserDetails)
authentication.getPrincipal(); Date now = new Date(); Date expiryDate = new Date(now.getTime() + jwtExpiration); return Jwts.builder() .setSubject(userPrincipal.getUsername()) .setIssuedAt(new Date()) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, jwtSecret) .compact(); } public String getUserUsernameFromJWT(String token) { Claims claims = Jwts.parser() .setSigningKey(jwtSecret) .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public boolean validateToken(String authToken) { try { Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); return true; } catch (Exception e) { return false; } } }

3. JwtAuthenticationFilter

3.1. JwtAuthenticationFilter


package com.example.userservice.security; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.
WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @Component @RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenProvider tokenProvider; private final UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { String jwt = getJwtFromRequest(request); if (jwt != null && tokenProvider.validateToken(jwt)) { String username = tokenProvider.getUserUsernameFromJWT(jwt); CustomUserDetails userDetails =
(CustomUserDetails) userDetailsService.loadUserByUsername(username); JwtAuthenticationToken authentication =
new JwtAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource()
.buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } filterChain.doFilter(request, response); } private String getJwtFromRequest(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (bearerToken != null && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } }

4. JwtAuthenticationToken

4.1. JwtAuthenticationToken


package com.example.userservice.security; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import java.util.Collection; public class JwtAuthenticationToken extends AbstractAuthenticationToken { private final Object principal; public JwtAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) { super(authorities); this.principal = principal; setAuthenticated(true); } @Override public Object getCredentials() { return null; } @Override public Object getPrincipal() { return this.principal; } }

Tam Proje Kodu

Projenin tamamı aşağıda verilmiştir. Her bir bileşen yukarıda detaylandırılmıştır ve aşağıda proje dizin yapısında belirtilmiştir.


user-service │ ├── pom.xml │ ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── example │ │ │ └── userservice │ │ │ ├── UserServiceApplication.java │ │ │ ├── controller │ │ │ │ └── UserController.java │ │ │ ├── dto │ │ │ │ ├── UserRequest.java │ │ │ │ └── UserResponse.java │ │ │ ├── mapper │ │ │ │ └── UserMapper.java │ │ │ ├── model │ │ │ │ └── User.java │ │ │ ├── repository │ │ │ │ └── UserRepository.java │ │ │ ├── security │ │ │ │ ├── CustomUserDetails.java │ │ │ │ ├── JwtAuthenticationFilter.java │ │ │ │ ├── JwtAuthenticationToken.java │ │ │ │ └── JwtTokenProvider.java │ │ │ ├── config │ │ │ │ └── SecurityConfig.java │ │ │ └── service │ │ │ └── UserService.java │ │ └── resources │ │ ├── application.yml │ │ └── data.sql └── src ├── test └── java └── com └── example └── userservice └── UserServiceApplicationTests.java

UserServiceApplication.java


package com.example.userservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } }

Bu yapılandırma ile User microservice'ini tamamlamış olduk. Her bileşen bağımsız olarak çalışır ve JWT ile güvenli bir kimlik doğrulama sağlar. MapStruct kullanarak DTO'lar arasındaki dönüşümleri de sağladık. Bu yapılandırmayı diğer microservisler için de benzer şekilde uygulayabilirsiniz.


Junit5 ve Mocikto ile birim ve entegrasyon testlerini de yaz

Unit ve Entegrasyon Testleri

JUnit 5 ve Mockito kullanarak User microservice'i için birim ve entegrasyon testleri yazalım.

1. Birim Testleri

1.1. UserServiceTest


package com.example.userservice.service; import com.example.userservice.model.User; import com.example.userservice.repository.UserRepository; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.security.crypto.password.PasswordEncoder; import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class UserServiceTest { @Mock private UserRepository userRepository; @Mock private PasswordEncoder passwordEncoder; @InjectMocks private UserService userService; @Test void testRegisterUser() { User user = new User(); user.setUsername("testuser"); user.setPassword("password"); when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword"); when(userRepository.save(any(User.class))).thenReturn(user); User savedUser = userService.registerUser(user); assertNotNull(savedUser); assertEquals("testuser", savedUser.getUsername()); verify(passwordEncoder, times(1)).encode("password"); verify(userRepository, times(1)).save(user); } @Test void testFindByUsername() { User user = new User(); user.setUsername("testuser"); when(userRepository.findByUsername("testuser")).
thenReturn(Optional.of(user)); User foundUser = userService.findByUsername("testuser"); assertNotNull(foundUser); assertEquals("testuser", foundUser.getUsername()); verify(userRepository, times(1)).findByUsername("testuser"); } }

2. Entegrasyon Testleri

2.1. UserControllerIntegrationTest


package com.example.userservice.controller; import com.example.userservice.dto.UserRequest; import com.example.userservice.dto.UserResponse; import com.example.userservice.model.User; import com.example.userservice.repository.UserRepository; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.
AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import java.util.Optional; import static org.springframework.test.web.servlet.request.
MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.
MockMvcResultMatchers.*; @ExtendWith(SpringExtension.class) @SpringBootTest @AutoConfigureMockMvc public class UserControllerIntegrationTest { @Autowired private MockMvc mockMvc; @Autowired private UserRepository userRepository; @Autowired private ObjectMapper objectMapper; @BeforeEach void setUp() { userRepository.deleteAll(); } @Test void testRegisterUser() throws Exception { UserRequest userRequest = new UserRequest(); userRequest.setUsername("testuser"); userRequest.setPassword("password"); userRequest.setEmail("testuser@example.com"); userRequest.setRole("USER"); mockMvc.perform(post("/users/register") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(userRequest))) .andExpect(status().isOk()) .andExpect(jsonPath("$.username").value("testuser")) .andExpect(jsonPath("$.email").value("testuser@example.com")); } @Test void testGetUser() throws Exception { User user = new User(); user.setUsername("testuser"); user.setPassword("password"); user.setEmail("testuser@example.com"); user.setRole(User.Role.USER); userRepository.save(user); mockMvc.perform(get("/users/testuser") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.username").value("testuser")) .andExpect(jsonPath("$.email").value("testuser@example.com")); } @Test void testGetUserNotFound() throws Exception { mockMvc.perform(get("/users/nonexistentuser") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); } }

3. pom.xml Güncellemesi

Test kütüphanelerini eklemek için pom.xml dosyasına gerekli bağımlılıkları ekleyin.


<dependencies> <!-- Other dependencies --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies>

4. Uygulama Testleri İçin application.yml

Test ortamı için gerekli yapılandırmaları da ekleyin.


spring: datasource: url: jdbc:postgresql://localhost:5432/usersdb_test username: postgres password: password driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: create-drop show-sql: true

Sonuç

Yukarıdaki birim ve entegrasyon testleri, User microservice'in farklı katmanlarını kapsar. Bu testler, servis ve kontrolör katmanlarının beklenen şekilde çalıştığını doğrular. JUnit 5 ve Mockito kullanarak bu testleri yazmak, kodun doğruluğunu ve güvenilirliğini artırır. Entegrasyon testleri ise uygulamanın çeşitli bileşenlerinin birlikte nasıl çalıştığını test eder. Bu yapı, microservice'in sağlam ve hatasız olmasını sağlar.


Bu servise authontication service desek daha doğru olmaz mıydı? Veya başka ne isim verebiliriz?

Evet, bu servis, kullanıcı kaydı, kimlik doğrulama ve yetkilendirme işlemlerini yönettiği için "Authentication Service" (Kimlik Doğrulama Servisi) olarak adlandırmak daha uygun olabilir. Alternatif olarak aşağıdaki isimler de uygun olabilir:
  1. Identity Service (Kimlik Servisi): Kullanıcı kimlik bilgileri ve doğrulama süreçlerini yönetir.
  2. Auth Service (Kısaltılmış Kimlik Doğrulama Servisi): Kimlik doğrulama ve yetkilendirme işlemlerini kapsar.
  3. User Management Service (Kullanıcı Yönetim Servisi): Kullanıcı bilgilerini ve kimlik doğrulama işlemlerini kapsar.

Bu isimlendirmelerden herhangi biri, servisin amacını daha iyi yansıtabilir.

Rastgele İçerik

© tüm hakları saklıdır
made with by templateszoo