27 Ekim 2020 Salı

SpringSecurit SecurityContextPersistenceFilter Sınıfı

Giriş
Açıklaması şöyle
This filter is used to store the security context of a user across multiple requests.
Açıklaması şöyle
From the API documentation: “Populates the SecurityContextHolder with information obtained from the configured SecurityContextRepository prior to the request and stores it back in the repository once the request has completed and clearing the context holder.”

The SecurityContext mainly represents the persisted session. It contains an Authentication which in the context of a web application encapsulates the information of the authenticated user. The default implementation of the SecurityContextRepository stores the SecurityContext in the HttpSession.

SpringSecurity ExceptionTranslationFilter Sınıfı

Giriş
Açıklaması şöyle
This filter is used to handle exceptions thrown during the security process and convert them into HTTP responses.
constructor - AuthenticationEntryPoint
Açıklaması şöyle. Eğer doğrulama işlemi başarısız olursa bu filtreye verilen AuthenticationEntryPoint çalıştırılır
From the API documentation: “Handles any AccessDeniedException and AuthenticationException thrown within the filter chain.”

Especially the very first, unauthorized request of a user triggers an AccessDeniedException (somewhere out of the FilterSecurityInterceptor). This one is catched and handled by the ExceptionTranslationFilter. If the user is not yet authenticated, the filter forwards him/her to the configured AuthenticationEntryPoint.

In the default configuration, the original request is temporarily stored in a RequestCache to be replayed after a successful login (see previous section). 
Açıklaması şöyle
But what if our authentication fails? How Spring Security handles the negative scenario? Well, there’s this guy in Spring Security filters, called ExceptionTranslationFilter.java, handles this part. It tries to continue the chain of filters and if it catches any Exception, this filter has something called AuthenticationEntrypoint.java which we configure it in our security configuration. This entrypoint is the place for commencing our response.

SpringSecurity UsernamePasswordAuthenticationFilter Sınıfı

Giriş
Açıklaması şöyle
This filter is used to authenticate a user using a username and password.
Şeklen şöyle. POST isteğinin içindeki "user name" ve "password" alanlarını alır. Bir tane UsernamePasswordAuthenticationToken nesnesi yaratır ve bunu AuthenticationManager nesnesine geçerek kullanıcıyı doğrular


Açıklaması şöyle
From the API documentation: “Processes an authentication form submission.”

See also Chapter 10 of the Spring Security Reference for a detailed description of the Spring Security authentication process.

The UsernamePasswordAuthenticationFilter triggers the authentication, if necessary and possible. It reads username and password from a login form request, wraps them into a UsernamePasswordAuthenticationToken and calls the configured AuthenticationManager to perform the authentication.

In the default configuration, the AuthenticationManager is a ProviderManager which holds a list of AuthenticationProviders to which it delegates the authentication request. In our sample project we use a very basic InMemoryAuthenticationProvider which knows only one static user. In a real world project we would instead use a database or LDAP provider (from the Spring Security LDAP module).

After a successful login the configured AuthenticationSuccessHandler is called. Usually, this handler decides about where to forward the user to after the successful login. In the default configuration a SavedRequestAwareAuthenticationSuccessHandler is used. It loads and replays the original request (which was cached before by the ExceptionTranslationFilter, see next section) to show the page to the user which he/she originally requested.

26 Ekim 2020 Pazartesi

Server Sent Events Kullanımı

Giriş
SSE kullanmak için 3 tane seçenek var

1. Flux<String> döndürmek
Örnek
Şöyle yaparız
@GetMapping(path = "/stream-flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamFlux() {
   return Flux.interval(Duration.ofSeconds(1))
           .map(sequence -> "Flux - " + LocalTime.now().toString());
}
2. Flux<ServerSentEvent> döndürmek
ServerSentEvent bir spring sınıfı. Açıklaması şöyle
Spring introduced support for SSE specification in version 4.2 together with a ServerSentEvent class. The benefit here is that we can skip the text/event-stream media type explicit specification, as well as we can add metadata such as id or event type.
Örnek
Şöyle yaparız
@GetMapping("/sse-flux-2")
public Flux<ServerSentEvent> sseFlux2() {
   return Flux.interval(Duration.ofSeconds(1))
           .map(sequence -> ServerSentEvent.builder()
                   .id(String.valueOf(sequence))
                   .event("EVENT_TYPE")
                   .data("SSE - " + LocalTime.now().toString())
                   .build());
}
3. SseEmitter Sınıfını Kullanmak
SseEmitter yazısına taşıdım.

20 Ekim 2020 Salı

SpringSecurity JWT Authorization İçin Filter Sınıfı

Giriş
Bu sınıfın amacı eğer mümkünse SecurityContextHolder.getContext nesnesini doldurmak.

İskeleti
Bu sınıfın iskeleti şöyle
public class JwtAuthenticationFilter extends OncePerRequestFilter {

  @Override
  protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, FilterChain filterChain)
throws ServletException, IOException
{ ... } }
Bazı kodlarda OncePerRequestFilter yerine BasicAuthenticationFilter'dan kalıtıyor ancak bu sınıfın amacı Http Basic Authentication yapmak. Amacının açıklaması şöyle. Dolayısıyla OncePerRequestFilter'dan kalıtmak daha iyi.
In summary, this filter is responsible for processing any request that has a HTTP request header of Authorization with an authentication scheme of Basic and a Base64-encoded username:password token. For example, to authenticate user "Aladdin" with password "open sesame" the following header would be presented:

 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Filtreyi Tanıtmak
Şöyle yaparız
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http...;
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
http.addFilterBefore(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class); }
JWT Token'ı Elde Etmek
Filtre sınıfında şöyley yaparız
private String getJwtFromRequest(HttpServletRequest request) {
  String bearerToken = request.getHeader("Authorization");
  if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
    return bearerToken.substring(7, bearerToken.length());
  }
  return null;
}
SecurityContextHolder Nesnesini Doldurmak
Şöyle yaparız
if(validToken(jwtToken)){
  String username = ...;//JWT token'dan username alınır
  //Eğer username varsa ve SecurityContextHolder.getContext boş ise
SecurityContextHolder securityContextHolder = SecurityContextHolder.getContext();
  if (username != null && securityContextHolder.getAuthentication() == null) {
    UserDetails userDetails = userDetailsService.loadUserByUsername(username);
    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
      new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
    usernamePasswordAuthenticationToken.setDetails(new
WebAuthenticationDetailsSource().buildDetails(request));
    securityContextHolder.setAuthentication(usernamePasswordAuthenticationToken);
  }
}
filterChain.doFilter(request, response);
validToken() metodu bir sürü exception fırlatabilir. Bunlar şöyle
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

SpringTest @DirtiesContext Anotasyonu

Giriş
Açıklaması şöyle. Eğer bir test ApplicationContext'i kirli bırakıyorsa kullanılabilir
There might be good reasons to use this annotation, e.g. when you modify the global application state in a test.

SpringBoot Test @SpringBootTest Anotasyonu classes Alanı

Giriş 
Bu alan en önemli ve kullanımı karışık alan bu yüzden ayrı bir yazıya taşımak istedim. Açıklaması şöyle
The component classes to use for loading an ApplicationContext. Can also be specified using @ContextConfiguration(classes=...). If no explicit classes are defined the test will look for nested @Configuration classes, before falling back to a @SpringBootConfiguration search.
1. Bu Alan Tanımlı Değilse

1.1 Bu Anotasyonla Beraber @ContextConfiguration Varsa
O zaman sanki bu alana değer tanımlanmış gibi @ContextConfiguration ile tanımlı bean'leri yükler.  
@ContextConfiguration SpringBoot gelmeden önceki standart spring test anotasyonudur. 

Örnek
Hem bu alana değer atamazsak ve tek bir bean ile ApplicationContext yaratmak istersek şöyle yaparız.
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.profiles.active=test")
@ContextConfiguration(classes = MyTestBean.class)
1.2 Test Sınıfı İçinde @Configuration Anotasyonu Varsa
@Configuration ile tanımlı bean'leri yükler
Örnek
Tüm uygulama yerine sadece belli bean'leri ayağa kaldırmak isteyelim. Test içinde @Configuration olarak işaretli static sınıf yaratırız. Şöyle yaparız.
@RunWith(SpringRunner.class)
@SpringBootTest
public class SomeTest {

    @Configuration
    static class ContextConfiguration {
        @Bean
        @Primary //may omit this if this is the only SomeBean defined/visible
        public SomeBean someBean () {
            return new SomeBean();
        }
    }

    @Autowired
    private SomeBean someBean;

    @Test
    public void testMethod() {
        // test
    }
}
1.3 Test Sınıfı İçinde @TestConfiguration Anotasyonu Varsa
Örnek
Tüm uygulamadaki bean'lere ilave olarak bazı bean'leri daha ayağa kaldırmak isteyelim. Test içinde @TestConfiguration olarak işaretli bir static sınıf yaratırız.

1.4 Test Sınıfı İçinde @Configuration Anotasyonu Yoksa
@SpringBootConfiguration anotasyonlu sınıfı arar ve onu yükler. Bu sınıf ta tüm Spring uygulamasıdır
Örnek
@SpringBootApplication olarak işaretli sınıfın paket olarak test sınıfının paketinde olması gerekir. Eğer @SpringBootApplication olarak işaretli sınıf bulunamazsa şu hatayı alırız.
Unable to find a @SpringBootConfiguration, you need to use 
@ContextConfiguration or @SpringBootTest(classes=...) ...

2. Bu Alan Tanımlıysa

2.1 Sadece Belirtilen Sınıfları Yükler
Yüklenen sınıf yine @SpringBootApplication uygulaması olabilir

Örnek
Tüm uygulama yerine sadece belli bean'leri ayağa kaldırmak isteyelim. Elimizde şöyle bir kod olsun.
@Configuration
public class ApplicationConfig {

  @Bean
  public MapperFactory mapperFactory() {
    return new DefaultMapperFactory.Builder().build();
  }
}
Şöyle yaparız.
@SpringBootTest(classes = ApplicationConfig.class)
@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
public class ProductSupplierIntegrationTests {
  // tests
}
Örnek
Test sınıfımızın paketi şöyle olsun com.example.test.JpaTest
@SpringBoot olarak işaretli sınıfı com.example paketinde arar. Eğer  sınıfımız com.example.Application ise sorun yoktur. Ancak sınıfımız com.example.application.Application ise hata alırız. Bu durumda en doğrusu @SpringBoot olarak işaretli sınıfı açıkça belirtmek. Şöyle yaparız
@SpringBootTest(classes = Application.class)
Örnek
Yine aynı şekilde farklı bir paketteki tüm uygulamayı ayağa kaldırmak isteyelim. Elimizde şöyle bir kod olsun.
@SpringBootApplication
public class Application {

  public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
  }
}
Şöyle yaparız.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class ApplicationTest{

  @Autowire
  Foo foo //whatever you are testing

  @Test
  public void FooTest() throws Exception{
    Foo f = foo.getFooById("22");
    assertEquals("9B". f.getCode); 
  }
}