Ana içeriğe geç

1.4. Security#

Uygulama içerisinde istek yaşam döngüsünde güvenlik ile ilgili bilgilerin taşınmasını ve kontrolünün yapılmasını sağlayan altyapı bileşenidir. Bu altyapı bileşeni ihtiyaçlar doğrultusunda genişletilebilmektedir.

ÖNERİ: @PreAuthorize gibi annotationlar'ın RestController üzerine konulması tavsiye edilmektedir. Servise yetkisi olan kişi RestController'dan geçtikten sonra güvenliğe takılmamalıdır. Karmaşık yetki havuzlarının oluşmasını önlemektedir.

@RestController
@RequestMapping("${hvl.system.service.menu-item.controller-path:/menu}")
public class HvlMenuItemRestController implements HvlMenuItemOperationalRestService, HvlMenuItemRestService {

    private final HvlMenuItemService menuItemService;

    /**
     * Instantiates a new HvlMenuItemRestController.
     *
     * @param menuItemService the menu item service
     */
    public HvlMenuItemRestController(HvlMenuItemService menuItemService) {
        this.menuItemService = menuItemService;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @PreAuthorize("hasAuthority('keysis')")
    public HvlResponse<Void> save(@NotNull @Valid @RequestBody HvlMenuItemModel menuItemModel) {
        menuItemService.save(menuItemModel);
        return new HvlResponse<>();
    }
}

JWT#

CSRF (Cross Site Request Forgery) saldırılarını önlemek amacı ile kullanılmaktadır. Yani dışarıdan gelen isteklerin authentication (kimlik doğrulama) kısmından geçmiş olduğunu teyit etmek içindir. EKSEN, authentication ile ilgili verileri HVL_SESSION_ID ve HVL_TOKEN alanları ile taşımaktadır. (Daha detaylı bilgi için: https://jwt.io/)

NOT: HVL_TOKEN değişkeni application-hvl-security.yml alanı içerisinde hvl.core.security.jwt.header.token alanından değiştirilebilir. Değiştirilmesi durumunda alana bağlı durumlar da düzenlenmelidir.

JWT üretmek için HvlJwtGeneratorBuilder sınıfı kullanılmaktadır. JWT üretirken şifreleme imzası (signature) önem taşımaktadır. HvlJwtAlgorithm sınıfı ile şifreleme algoritması seçilmektedir.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tr.com.havelsan.javarch.jwt.generator.builder.HvlJwtGeneratorBuilder;
import tr.com.havelsan.javarch.jwt.parser.algorithm.HvlRS256Algorithm;
import tr.com.havelsan.javarch.jwt.parser.model.HvlRSKeyPair;

/**
 * @author javarch
 */
@SpringBootApplication
public class HvlJpaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(new Class[]{HvlJpaServerApplication.class}, args);

        HvlJwtGeneratorBuilder.create()
                .setAlgorithm(new HvlRS256Algorithm(new HvlRSKeyPair()))
                .build();
    }

}

JWT içerisine konulan bilgiyi ayrıştırmak (parse) için HvlJwtParserBuilder sınıfı kullanılmaktadır. Burada önemli olan üretim yapılan algoritma ile ayrıştırma yapılan algoritmanın türünün aynı olması gerekmektedir. (HvlJwtAlgorithm aynı olmalıdır.)

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tr.com.havelsan.javarch.jwt.parser.algorithm.HvlRS256Algorithm;
import tr.com.havelsan.javarch.jwt.parser.builder.HvlJwtParserBuilder;
import tr.com.havelsan.javarch.jwt.parser.model.HvlRSKeyPair;

/**
 * @author javarch
 */
@SpringBootApplication
public class HvlJpaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(new Class[]{HvlJpaServerApplication.class}, args);

        HvlJwtParserBuilder.create()
                .setAlgorithm(new HvlRS256Algorithm(new HvlRSKeyPair()))
                .build();
    }

}

NOT: EKSEN, JWT üretimi için varsayılan olarak HvlRS256Algorithm (hvl.core.security.jwt.key-provider.algorithm) algoritması kullanmaktadır. RSA algoritmasının kullanılabilmesi için private ve public key olması gerekmektedir.

EKSEN'de varsayılan olarak hvl.pem ve hvl.public.pem dosyaları bulunmaktadır. Bu dosyalar hvl.core.security.jwt.key-provider.file.public-file-path ve hvl.core.security.jwt.key-provider.file.private-file-path alanları ile değiştirilebilmektedir.


drawing Nasıl konfigüre edilir?

hvl-infra altında bulunan 'application-hvl-security.yml' dosyasıyla konfigure edilebilmektedir.

hvl:
  core:
    security:
      jwt:
        headerType: ${JWT_HEADER_TYPE:cookie}
        cookie:
          domain: ${JWT_COOKIE_DOMAIN:hvlnet.net}
          path: ${JWT_COOKIE_PATH:/}
          max-age: ${JWT_COOKIE_MAX_AGE:315360000}
        header:
          token: ${JWT_HEADER_TOKEN:HVL_TOKEN}
        key-provider:
          type: ${JWT_SECURITY_KEY_PROVIDER_TYPE:resource}
          algorithm: ${JWT_SECURITY_KEY_PROVIDER_ALGORITHM:RS256}
          default-config-enabled: ${JWT_SECURITY_DEFAULT_CONFIG_ENABLED:true}
          secret-key: ${JWT_SECURITY_KEY_PROVIDER_SECRET_KEY:}
          file:
            public-file-path: ${JWT_SECURITY_KEY_PROVIDER_PUBLIC_FILE_PATH:}
            private-file-path: ${JWT_SECURITY_KEY_PROVIDER_PRIVATE_FILE_PATH:}
  • headerType: Güvenlik ile ilgili bilgilerin hangi yöntem ile uygulanacağını belirler. cookie ve header değerlerini alabilir.
    • cookie değeri seçildiğinde login olan kullanıcının bilgileri tarayıcının cookielerinde tutulmaktadır. hvl.core.security.jwt.cookie altındaki özellikler aktif olmaktadır.
    • header değeri seçildiğinde ise backend tarafına gelen isteklerin header alanına HVL_SESSION_ID ve hvl.core.security.jwt.header.token bilgileri basılmalıdır.
  • cookie
    • domain: headerType cookie yapıldığında, tarayıcıya yazılacak cookie bilgisinin domain değerini ayarlar. Bu domain dışından sistemin kullanılmasını engeller.
    • path: Cookie path bilgisidir.
    • max-age: Cookie max-age bilgisidir. Cookie'nin geçerli olarak kullanılabildiği süreyi ifade eder.
  • header
    • token: Cookie'de tutulacak token'ın key bilgisidir. Bu değer aynı zamanda headerType header seçildiğinde header'da taşınacak token'ın key bilgisi olarak kullanılır.
  • key-provider
    • type: JWT key sağlayıcısının çalışma türü bilgisidir. Mevcut sürümde resource değeri ile çalışmaktadır.
    • algorithm: JWT şifreleme algoritması bilgisidir. Alabileceği değerler: HS256,RS256
    • default-config-enabled: RS256 algoritması ile JWT oluşturulurken EKSEN tarafından oluşturulmuş varsayılan sertifikaların kullanılmasını sağlar. Bu değer false olursa hvl.core.security.jwt.key-provider.file altındaki değerler ile çalışacaktır.
    • secret-key: HS256 algoritması ile JWT oluşturulurken kullanılacak anahtar bilgisidir.
    • public-file-path: RS256 algoritması ile JWT oluşturulurken kullanılacak public sertifika dosya yolu bilgisidir.
    • private-file-path: RS256 algoritması ile JWT oluşturulurken kullanılacak private sertifika dosya yolu bilgisidir.

drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.security', name: 'hvl-jwt-generator'
group: 'tr.com.havelsan.framework.security', name: 'hvl-jwt-parser'
group: 'tr.com.havelsan.framework.security', name: 'hvl-jwt-security'

Session Provider#

Sisteme giriş yapmış (authenticated) bir istemcinin her bir isteğinde herhangi bir uygulama (Örneğin session-server) ile kimlik doğrulaması yapabileceği arayüz sınıfı sunulmaktadır. HvlSecuritySessionProvider sınıfı implement edilerek oturum veya istek bilgileri güvenlik katmanına özelleştirilerek aktarılabilir.

NOT: Mikro servis mimarilerde uygulamalar stateless olduğu için sisteme giriş yapan kullanıcıların oturum bilgileri bir sistemde tutulması gerekmektedir. HvlSecuritySessionProvider sınıfı implement edilerek herhangi bir uygulama ile entegre olunabilir.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.security', name: 'hvl-security-session-provider'

Context#

Uygulama güvenlik ile ilgili verilere ulaşmamızı sağlamaktadır.

İki şekilde kullanılabilmektedir:

  • DI (Dependency Injection) ile kullanılabilir.
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import tr.com.havelsan.javarch.security.common.model.HvlAuthenticationToken;
import tr.com.havelsan.javarch.security.context.HvlSecurityContext;

import java.util.Collection;

@Service
public class SampleService {

    private final HvlSecurityContext securityContext;

    public SampleService(HvlSecurityContext securityContext) {
        this.securityContext = securityContext;
    }

    public void test() {
        final HvlAuthenticationToken authentication = securityContext.getAuthentication();
        final Collection<GrantedAuthority> authorities = authentication.getAuthorities();
    }

}
  • Bean olmayan sınıflar statik olarak 'Holder' üzerinden kullanılabilir.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tr.com.havelsan.javarch.security.common.model.HvlAuthenticationToken;
import tr.com.havelsan.javarch.security.context.HvlSecurityContextHolder;

/**
 * @author javarch
 */
@SpringBootApplication
public class HvlJpaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(new Class[]{HvlJpaServerApplication.class}, args);

        final HvlAuthenticationToken authentication = HvlSecurityContextHolder.getAuthentication();
    }

}

NOT: Hiyerarşik yetki desteği sağlanmaktadır. Yetki Ekleme sayfasından detaylı bilgi edinilebilir.


drawing Nasıl konfigüre edilir?

hvl-infra altında bulunan 'application-hvl-security.yml' dosyasıyla konfigure edilebilmektedir.

hvl:
  core:
    security:
      enabled: ${SECURITY_ENABLED:true}
      method:
        enabled: ${METHOD_SECURITY_ENABLED:true}
        authorityParser: ":"
      password:
        encoder:
          strength: 11
      permit-url:
        eksen:
          - /actuator/**
          - /swagger-ui/**
          - /swagger-resources/**
          - /webjars/**
          - /eureka/peerreplication/**
          - /v2/**
          - /v3/**
          - /favicon.ico
        menu:
          - /menuItem/menuItems/getMenuListHierarchical
          - /menuItem/getMenuListInsecure
        bpmn:
          - /process/model/**
          - /display/**
        authentication:
          - /auth/login
          - /auth/complete-login-with-code
          - /auth/validate
          - /auth/generate-code
          - /auth/captcha-image-string
          - /verification/**
          - /auth/setting/**
          - /tenant/list
        setting:
          - /settings/list/by-type-and-module-name/**
          - /settings/number-setting/by-code/**
          - /settings/string-setting/by-code/**
          - /settings/boolean-setting/by-code/**
          - /settings/number-setting-with-tenant/by-code/**
          - /settings/string-setting-with-tenant/by-code/**
          - /settings/boolean-setting-with-tenant/by-code/**
        report:
          - /report/jasper/authenticate
        authorization:
          - /users/password/**
          - /users/password-with-verification
          - /user-password-policies/active
          - /users/password-with-verification
          - /user-details/user-type/list
          - /registration/register
          - /registration/verify-registered-user
          - /registration/complete-registration
  • enabled: Güvenliğin devrede olup olmadığı bilgisidir.
  • method
    • enabled: Method güvenliğinin devrede olup olmadığı bilgisidir.
    • authorityParser: Method güvenliği için kullanılan yetkilerin ayraç bilgisidir. Örnek: keysis:user:create yetkisi ile işaretlenmiş bir metod için; sistemdeki kullanıcı, keysis, keysis:user veya keysis:user:create yetkilerinden birine sahipse metod güvenliğini geçebilir. Aksi durumda 403 nolu hata kodu alacaktır.
  • password:encoder:strength: Oluşturulacak passwordEncoder bean'i içerisinde kullanılır. Şifrelenecek verinin şifrelenme gücünü ifade eder.
  • permit-url: Map şeklinde değer alır. Bu alana verilen değerler güvenliğe takılmadan ve herhangi bir yetki kontrolü yapılmadan geçilebilecek url path'lerini ifade eder. Key bilgilerine göre konfigürasyon yapılmak isteniyorsa uygulama application.yml dosyasına hvl.core.security.permit-categories değeri eklenmelidir. Örnek:
    hvl:
      core:
        security:
          permit-categories: ${JWT_PERMIT_CATEGORIES:eksen,menu}
    

Yukarıdaki gibi konfigüre edildiğinde permit-url bilgileri arasından sadece eksen ve menu altındaki değerlere göre işlem yapılacaktır. Bu değer boş bırakılırsa tüm değerler dahil edilecektir.

Bu ayarlara ek olarak uygulama içerisinden HvlSecurityContextPermitUrlConfigurer bean tanımlaması yapılarak permit edilmek istenen pathler verilebilmektedir. Bunun için aşağıdaki gibi bir tanımlama yapılabilir.

@Bean
public HvlSecurityContextPermitUrlConfigurer contextPermitUrlConfigurer() {
    return new HvlSecurityContextPermitUrlConfigurer(Set.of(
            "/my/path/1",
            "/my/path/2",
            "/all/paths/**"
    ));
}

Burada verilen pathler ortak konfigürasyondaki permit-url pathlerine eklenmektedir.


drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.security', name: 'hvl-security-context'

Security Filter Chain Özelleştirici (Customizer)#

EKSEN, temelinde spring boot kullandığı için güvenlik katmanlarında da spring'in sağladığı SecurityFilterChain gibi teknolojiler özelleştirilerek kullanılmaktadır. EKSEN, mevcutta kendi mimarisine göre özelleştirdiği bir güvenlik katmanı sunmaktadır. Ancak farklı ihtiyaçlar doğrultusunda bu katman genişletilmek istenebilir. Bu durumlar için EKSEN tarafından sağlanan HvlHttpSecurityCustomizer sınıfı bulunmaktadır. Mevcutta sağlanan SecurityFilterChain bean'i bu sınıf yardımıyla esnetilebilir.

@Bean
public HvlHttpSecurityCustomizer httpSecurityCustomizer() {
    return httpSecurity -> {
        //some codes
    };
}

Mevcutta EKSEN tarafından özelleştirilmiş SecurityFilterChain aşağıdaki gibidir.

EKSEN SecurityFilterChain

@Bean
@Order(SecurityProperties.DEFAULT_FILTER_ORDER)
SecurityFilterChain jwtSecurityFilterChain(
        HttpSecurity httpSecurity,
        HvlSecurityContext frameworkSecurityContext,
        HvlJwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,
        ObjectProvider<HvlHttpSecurityCustomizer> httpSecurityCustomizers,
        HvlJwtSessionFilter jwtSessionFilter) throws Exception {

    httpSecurity
            .securityContext(securityContextConfigurer -> securityContextConfigurer
                    .securityContextRepository(new NullSecurityContextRepository())
            )
            .cors(Customizer.withDefaults())
            .csrf(CsrfConfigurer::disable)
            .authorizeHttpRequests(authorizeHttpRequestsConfigurer -> authorizeHttpRequestsConfigurer
                    .requestMatchers(
                            frameworkSecurityContext.getPermittedRequestMatchers()
                                    .toArray(new AntPathRequestMatcher[0])
                    )
                    .permitAll()
                    .anyRequest()
                    .authenticated()
            )
            .formLogin(FormLoginConfigurer::disable)
            .sessionManagement(sessionManagementConfigurer -> sessionManagementConfigurer
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .exceptionHandling(exceptionHandlingConfigurer -> exceptionHandlingConfigurer
                    .authenticationEntryPoint(jwtAuthenticationEntryPoint)
            )
            .addFilterBefore(jwtSessionFilter, UsernamePasswordAuthenticationFilter.class)
            .headers(headersConfigurer -> headersConfigurer
                    .frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
                    .cacheControl(Customizer.withDefaults())
            );
    httpSecurityCustomizers.orderedStream()
            .forEach(hvlSecurityContextCustomizer ->
                    hvlSecurityContextCustomizer.customize(httpSecurity)
            );
    return httpSecurity.build();
}

Web Starter#

EKSEN thymeleaf ile geliştirilen giriş ekranını sağlamaktadır. Uygulamalarda kullanılarak login işlemi gerçekleştirilebilir.

@Bean
@ConditionalOnMissingBean
AuthenticationProvider authenticationProvider(HvlWebSecurityProperties webSecurityProperties){
        return new HvlWebSecurityInmemoryAuthenticationProvider(webSecurityProperties);
}

Sağlanan altyapı ekranı ile herhangi bir sisteme entegre olmak istenirse yukarıdaki Bean ezilmesi yeterli olacaktır. Varsayılan olarak inmemory çalışmaktadır. Kullanıcı bilgileri 'application-hvl-web-security.yml' içerisinden tanımlanmaktadır.


drawing Nasıl konfigüre edilir?

hvl-infra altında bulunan 'application-hvl-web-security.yml' dosyasıyla konfigure edilebilmektedir.

hvl:
  core:
    security:
      web:
        csrf:
          ignore-path:
            - /eureka/**
        login-form:
          enabled: ${SECURITY_WEB_LOGIN_FORM_ENABLED:true}
          title: ${SECURITY_WEB_LOGIN_FORM_TITLE:Javalt Eureka Server}
        credentials: admin#123456
  • csrf.ignore-path: CSRF kontrolü dışında tutulan path bilgisidir. Liste olarak birden fazla değer alabilir.
  • login-form
    • enabled: Login formun görünüp görünmeyeceği bilgisidir.
    • title: Login formunda görünecek başlık bilgisidir.
  • credentials: Burada tanımlanan kullanıcı adı ve şifre bilgileri ile forma giriş yapılabilir. Ayraç olarak # kullanılır. admin#123456 değeri; kullanıcı adı: admin, şifre: 123456 olarak ayrıştırılır.

drawing Uygulamaya nasıl eklenir?
group: 'tr.com.havelsan.framework.security', name: 'hvl-security-web-starter'