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çerisindehvl.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
vehvl.public.pem
dosyaları bulunmaktadır. Bu dosyalarhvl.core.security.jwt.key-provider.file.public-file-path
vehvl.core.security.jwt.key-provider.file.private-file-path
alanları ile değiştirilebilmektedir.
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
veheader
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ınaHVL_SESSION_ID
vehvl.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ı zamandaheaderType
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ümderesource
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ğerfalse
olursahvl.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.
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.
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.
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
veyakeysis:user:create
yetkilerinden birine sahipse metod güvenliğini geçebilir. Aksi durumda 403 nolu hata kodu alacaktır.
password:encoder:strength
: OluşturulacakpasswordEncoder
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 uygulamaapplication.yml
dosyasınahvl.core.security.permit-categories
değeri eklenmelidir. Örnek:
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.
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.
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.