17 Haziran 2019 Pazartesi

SpringWebFlux WebClient.ResponseSpec Arayüzü

bodyToFlux metodu
Şöyle yaparız.
get()
.uri(uri)
.accept(MediaType.APPLICATION_OCTET_STREAM)
.retrieve()
.bodyToFlux(byte[].class)

SpringCloud

Giriş
Sürüm isimlerinin açıklaması şöyle.
The names Celsius, Darwin, Einstein etc are release names of the Spring Cloud Stream App Starters project. ... They are in alphabetical order, ie Einstein is the newer than Darwin (Similar to Android's release naming as Lollipop, Marshmallow, Nougat, Oreo etc).
SpringCloud Abstraction Sağlar
Açıklaması şöyle
... Spring cloud just gives you abstractions over some set of tools (eureka, zuul, feign, ribbon etc.) making it easy for you to integrate with spring applications.

However, you can also achieve microservice architecture without using spring cloud. You can take advantage of tools like Kubernetes, docker swarm, haproxy, Kong, nginx etc to achieve the same. The advantage of not using spring cloud has its own pros and cons and vice versa.
Spring Cloud ile Netflix kullanıyorsak 5 tane önemli anotasyon var. Bunlar şöyle
@EnableConfigServer

@EnableEurekaServer
@EnableEurekaClient

@EnableDiscoveryClient

@EnableCircuitBreaker

@EnableHystrix 
@EnableHystrixDashboard
@HyStrixCommand(fallbackmethod=”MethodName”)

@LoadBalanced

Bunlar aslında şu Netflix bileşenlerine denk geliyor.
Service Discovery (Eureka), Circuit Breaker (Hystrix), Intelligent Routing (Zuul) and Client-Side Load Balancing (Ribbon).
Zuul - API Gateway
Zuul yazısına taşıdım.

16 Haziran 2019 Pazar

SpringSecurity JWT Authentication

Giriş
OAuth ve JWT'nin birlikte kullanımı dışında sadece JWT kullanarak authentication da yapılabilir.

Angular İle Kullanmak
Kullandığımız front end kütüphanesinde (örneğin Angular) bir HttpInterceptor kullanarak tüm dışarı giden (outgoing) Http istekleri için Json Web Token bilgisini eklemek gerekir.

Adımlar
Adım sırası kabaca şöyle.
Hemen hemen aynı adımları izleyen bir başka uygulama burada.

Her iki örnek te token üretmek için io.jsonwebtoken.Jwts kütüphanesini kullanıyor.

Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>
JwtAuthenticationRestController Sınıfı
- Uygulama JwtAuthenticationRestController vasıtasıyla iki tane Rest noktası sunar. Bunlar şöyledir

1. http://localhost:8080/authenticate
Açıklaması şöyle.  JwtTokenRequest okur ve JwtTokenResponse döner.
Expose a POST API with mapping /authenticate. On passing the correct username and password, it will generate a JSON Web Token (JWT).

2. http://localhost:8080/refresh
JwtTokenRequest okur ve JwtTokenResponse döner. JwtTokenResponse yerine direkt Response nenesi de dönüleblir.

JwtTokenRequest Sınıfı
Sadece username ve password alanlarından oluşur.

JwtTokenResponse Sınıfı
Sadece token isimli String tipinden bir alandan oluşur.

Jwt Authentication İçin Rest Endpoint
Örnek ver

Jwt Authorization İçin Filter Sınıfı - En Önemli Sınıf
JWT Authorization İçin Filter Sınıfı yazısına taşıdım

JwtUserDetails Sınıfı
UserDetails arayüzünden kalıtan bir JwtUserDetails sınıfı kodlanır. Bu sınıf kullanıcı hakkında bilgileri içerir.

JwUserDetailsService Sınıfı
UserDetailsService arayüzünden  kalıtan bir JwUserDetailsService sınıfı kodlanır. Bu sınıf belirtilen isme ve şifreye sahip JwtUserDetails nesnesini döner veya exception fırlatır.

JwtTokenUtil Sınfı
Token içindeki çeşitli alanları erişimi kolaylaştırır.

WebSecurityConfigurerAdapter  Sınıfı
Şöyle yaparız
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

  @Autowired
  private JwtRequestFilter jwtRequestFilter;

  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {

    // dont authenticate this particular request
    httpSecurity.authorizeRequests()
      .antMatchers("/authenticate").permitAll()
      .antMatchers(HttpMethod.OPTIONS, "/**").permitAll().
      // all other requests need to be authenticated
      anyRequest().authenticated()
      .and()
// make sure we use stateless session; session won't be used to
// store user's state.
      .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
      .and()
      .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    // Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter,
UsernamePasswordAuthenticationFilter.class);
  }
}
JwtAuthenticationEntryPoint Sınıfı
Şöyle yaparız
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response,
                       AuthenticationException authException) throws IOException {

    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
  }
}


12 Haziran 2019 Çarşamba

SpringContext Internationalization ResourceBundleMessageSource Sınıfı - Locale'i Belirtmek Gerekir

Giriş
Bu sınıf org.springframework.context.MessageSource arayüzünü gerçekleştirir ve 

messages_en.properties, 
messages_kn.properties, 
messages_fr.properties 
gibi dosyaları okur

Bu sınıfı kullanırken Locale nesnesini belirtmek gerekir.

getMessage metodu - key + parameters + Locale
Açıklaması şöyle
The getMessage() takes the property name as the first parameter. The second parameter is null, because the messsage takes no parameters. The third parameter is the locale.
Örnek
Şöyle yaparız
@Component public class ApplicationConfiguration { @Autowired ResourceBundleMessageSource messageSource; @PostConstruct public void init(){ String welcome = messageSource.getMessage("welcome", null, Locale.FRANCE)); } }
Örnek
İngilizce labels.properties şöyle olsun
l1=Earth
l2=Hello {0}, how are you?
Almanca labels.properties şöyle olsun
l1=Erde
l2=Hallo {0}, wie geht's?
Şöyle yaparız
logger.info("{}", messageSource.getMessage("l2", new Object[] {"Paul Smith"},
  Locale.GERMAN));
logger.info("{}", messageSource.getMessage("l2", new Object[] {"Paul Smith"},
  Locale.ENGLISH));
Çıktı olarak şunu alırız
22:08:27.984 INFO  com.zetcode.Application - Hallo Paul Smith, wie gehts?
22:08:27.984 INFO  com.zetcode.Application - Hello Paul Smith, how are you?
Örnek
Şöyle yaparız.
for (Object object : bindingResult.getAllErrors()) {
  if(object instanceof FieldError) {
    FieldError fieldError = (FieldError) object;

    // Use null for second parameter if you do not use i18n
    String message = messageSource.getMessage(fieldError, null);
  }
}
setBasename metodu
Örnek
Elimizde şöyle bir application.properties olsun
# Whether to always apply the MessageFormat rules, parsing even messages without arguments.
spring.messages.always-use-message-format=false
 
# Whether to fall back to the system default Locale, if no files for a specific Locale have
# been found.
spring.messages.fallback-to-system-locale=true
 
# Whether to use the message code as the default message instead of throwing a
#"NoSuchMessageException". Recommended during development only.
spring.messages.use-code-as-default-message=false
Şöyle yaparız
@Bean
public ResourceBundleMessageSource messageSource() {
  ResourceBundleMessageSource source = new ResourceBundleMessageSource();
  source.setDefaultEncoding("UTF-8");
  source.setBasename("messages");
  source.setCacheSeconds(600);
  return source;
}
Örnek
Şöyle yaparız.
@Bean
public ResourceBundleMessageSource messageSource() {
  ResourceBundleMessageSource resource = new ResourceBundleMessageSource();
  resource.setBasename("message");
  return resource;
}
Örnek
XML ile şöyle yaparız.
<bean id="messageSource"
  class="org.springframework.context.support.ResourceBundleMessageSource">
  <property name="basenames" value="ValidationMessages"/>
</bean>

9 Haziran 2019 Pazar

SpringCache GuavaCacheManager Sınıfı - Kullanmayın

Giriş
Guava desteği artık yok. Caffeine kullanmak gerekiyor.
Şöyle yaparız
// From:
GuavaCacheManager guavaCacheManager = new GuavaCacheManager();
guavaCacheManager.setCacheBuilder(CacheBuilder.newBuilder()
  .expireAfterWrite(1, CACHE_TIME_UNIT));

// To:
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
  .expireAfterWrite(1, CACHE_TIME_UNIT));
constructor
Örnek
Şöyle yaparız.
@Bean
public CacheManager cacheManager() {
  GuavaCacheManager guavaCacheManager = new GuavaCacheManager();
  guavaCacheManager.setCacheBuilder(CacheBuilder.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES));
  return guavaCacheManager;
}
constructor - String
Örnek
Şöyle yaparız.
@Bean
public CacheManager cacheManager() {
  return new GuavaCacheManager("CustomerCache");
}
Örnek
Şöyle yaparız.
@Bean
public CacheManager cacheManager() {
  GuavaCacheManager cacheManager = new GuavaCacheManager("ceepCache");
  CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder().maximumSize(100)
    .expireAfterWrite(10,TimeUnit.MINUTES);
  cacheManager.setCacheBuilder(cacheBuilder);
  return cacheManager;
}