Initial commit
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
= Appendix
|
||||
|
||||
:leveloffset: +1
|
||||
|
||||
include::QuartzClustering.adoc[leveloffset]
|
||||
|
||||
include::Properties암호화툴사용방법.adoc[leveloffset]
|
||||
|
||||
include::uiSDL.adoc[leveloffset]
|
||||
|
||||
include::RedisConfig.adoc[leveloffset]
|
||||
|
||||
include::D-KMS 암호화 적용.adoc[leveloffset]
|
||||
@@ -0,0 +1,90 @@
|
||||
= D-KMS 암호화 적용
|
||||
|
||||
SDL(표준개발라이브러리)에 D-KMS 암호화를 적용하는 방법에 대해 설명한다.
|
||||
|
||||
== 사전 준비
|
||||
|
||||
=== KMS 포털에서 암호화 권한 신청
|
||||
|
||||
. KMS 포털에서 계정을 인증하고 암호화 권한 신청, 결재
|
||||
. 암호화 권한 결재 완료되면 D-KMS SDK 및 가이드 전송됨
|
||||
. 전달 받은 SDK 설치 및 코드 적용
|
||||
|
||||
== 코드 적용
|
||||
|
||||
=== Credential 생성 및 환경변수 추가
|
||||
|
||||
. D-KMS 가이드를 참고하여 Credential을 생성하고 시스템 환경변수에 추가한다.
|
||||
|
||||
=== SDL 수정
|
||||
|
||||
. D-KMS 암호화 라이브러리 추가
|
||||
- https://code.sec.samsung.net/confluence/pages/viewpage.action?pageId=343332410[D-KMS 가이드]를 참고하여 라이브러리를 추가한다.
|
||||
|
||||
. src/main/resources-{local|dev|prod} 에 dkms.properties 파일을 추가한다. (배포된 sdl-appendix 내 참조)
|
||||
|
||||
IMPORTANT: - dkms.task-id 프로퍼티 값을 D-KMS 신청시 부여받은 값으로 수정해야한다. +
|
||||
- 기타 다른 프로퍼티 값도 제대로 설정되었는지 확인한다.
|
||||
|
||||
[start=3]
|
||||
. mybatis typehandler 추가
|
||||
- 암호화 대상이 되는 컬럼에 대해 typehandler 를 적용한다.
|
||||
- com.samsung.dkms.handler 패키지 생성
|
||||
- EncryptionTypeHandler.java, NameEncryptionTypeHandler.java, EmailEncryptionTypeHandler.java, PhoneEncryptionTypeHandler.java, AddressEncryptionTypeHandler.java, BirthdayEncryptionTypeHandler.java 파일을 복사/붙여넣기 한다. (배포된 sdl-appendix 내 참조)
|
||||
|
||||
. mybatis-config.xml 수정
|
||||
- mapper xml 에 적용이 용이하도록 typehandler의 type alias 를 추가한다.
|
||||
+
|
||||
image::dkms_typehandler_alias.png[]
|
||||
|
||||
[start=5]
|
||||
. mapper xml 에 mybatis typehandler 적용
|
||||
- 암호화가 필요한 컬럼에 해당하는 핸들러를 선택 적용한다.
|
||||
- mapper xml에 typehandler 적용 방법은 샘플파일(mapper-mybatis-user.xml, 배포된 sdl-appendix 내 참조)을 참고한다. (`*""유무에 유의*`)
|
||||
* select resultMap 적용 예
|
||||
+
|
||||
image::dkms_typehandler_select_resultmap.png[]
|
||||
|
||||
* insert, update 적용 예
|
||||
+
|
||||
image::dkms_typehandler_insert.png[]
|
||||
image::dkms_typehandler_update.png[]
|
||||
|
||||
* 수정대상 mapper xml (배포판 기준)
|
||||
+
|
||||
----
|
||||
mapper-mybatis-common.xml
|
||||
mapper-mybatis-sample-approval-internal.xml
|
||||
mapper-mybatis-sample-approval-knox.xml
|
||||
mapper-mybatis-sample.xml
|
||||
mapper-mybatis-sys-resource.xml
|
||||
mapper-mybatis-user-sync.xml
|
||||
mapper-mybatis-approval.xml
|
||||
mapper-mybatis-user-role.xml
|
||||
mapper-mybatis-user.xml
|
||||
mapper-mybatis-workgroup-role.xml
|
||||
mapper-mybatis-comment.xml
|
||||
mapper-mybatis-post.xml
|
||||
mapper-mybatis-email.xml
|
||||
mapper-mybatis-mail-group.xml
|
||||
mapper-mybatis-history.xml
|
||||
mapper-mybatis-access-log.xml
|
||||
mapper-mybatis-admin-address.xml
|
||||
mapper-mybatis-knox-department-sync.xml
|
||||
mapper-mybatis-knox-department.xml
|
||||
mapper-mybatis-terms-use.xml
|
||||
----
|
||||
|
||||
=== 테이블 수정 및 데이터 마이그레이션
|
||||
. 테이블 컬럼 사이즈 변경
|
||||
- 암호화 대상 컬럼의 사이즈를 최소 255byte 가 되도록 변경한다.
|
||||
. 기존 데이터 암호화를 위한 마이그레이션은 D-KMS 가이드를 참고한다.
|
||||
|
||||
CAUTION: *SQL기반의 Database의 경우, 개인정보가 암호화됨으로써 아래와 같은 SQL문에 영향이 발생* (D-KMS 소개자료 중 발췌) +
|
||||
Nondeterministic Encryption으로 인해 Equality 비교 불가 +
|
||||
: WHERE, GROUP BY, JOIN (ON), DISTINCT, HAVING, ORDER BY +
|
||||
-> 일반적으로 Application Level에서 Filtering, Grouping, Joining 구현 필요 +
|
||||
+
|
||||
*대안: 별도 Column에 Hash 데이터를 추가 저장* +
|
||||
Hash로 인해 영향 받는 SQL문: WHERE (비교에 사용하는 데이터 역시 Hash 필요. Equality 비교는 가능하나 LIKE 또는 대소비교 불가), ORDER BY +
|
||||
-> 단, 모든 데이터가 Hash되어있지 않은 경우 GROUP BY에 영향을 줄 수 있음
|
||||
@@ -0,0 +1,170 @@
|
||||
= Properties 암호화 툴 사용 방법
|
||||
|
||||
sdl-encrypt는 Spring Framework에서 사용하는 properties를 http://www.jasypt.org/[Jasypt]를 이용해 쉽게 암호화 하기 위한 모듈이다.
|
||||
|
||||
== 사용 방법
|
||||
|
||||
=== 파일다운로드
|
||||
|
||||
sdl-encrypt-1.0.0.jar 파일을 다운로드 받아서 특정 디렉토리에 복사한다. (배포된 sdl-appendix 내 참조)
|
||||
|
||||
sdl-encypt-1.0.0.jar 파일을 실행하기 위해서는 JDK 8 이상의 버전이 설치 되어 있어야 한다.
|
||||
|
||||
----
|
||||
C:\encrypt>dir
|
||||
C 드라이브의 볼륨에는 이름이 없습니다.
|
||||
볼륨 일련 번호: 1841-973A
|
||||
|
||||
C:\encrypt 디렉터리
|
||||
|
||||
2022-08-09 오후 02:17 <DIR> .
|
||||
2022-08-09 오후 02:17 <DIR> ..
|
||||
2022-08-09 오후 02:15 9,114,144 sdl-encrypt-1.0.0.jar
|
||||
|
||||
----
|
||||
|
||||
'''
|
||||
|
||||
=== 암호화 key 파일 생성
|
||||
|
||||
같은 폴더에 암호화를 위한 key 파일을 sdl-encrypt-1.0.0.jar 파일을 있는 폴더에 생성한다.
|
||||
|
||||
key파일의 이름은 *jasypt.key* 로 한다. jasypt.key 파일은 어플리케이션 실행 시 서버에서 읽어 복호화 할때 필요하다.
|
||||
|
||||
*jasypt.key 파일의 내용이 유출 될 경우 문제가 생길 수 있으니 형상관리에 저장하지 않도록 한다.*
|
||||
|
||||
*jasypt.key 샘플*
|
||||
|
||||
----
|
||||
fhasiuk23dfjhasoijcnaoijfoase0342ruq0
|
||||
|
||||
----
|
||||
|
||||
jasypt.key 파일의 내용은 프로젝트에서 랜덤한 문자열로 생성한다.
|
||||
|
||||
'''
|
||||
|
||||
=== Properties 파일 생성
|
||||
|
||||
config.properties 파일에 암호화가 필요한 properties를 작성해 sdl-encrypt-1.0.0.jar 파일이 있는 폴더에 생성한다.
|
||||
|
||||
*config.properties 파일 샘플*
|
||||
|
||||
----
|
||||
# Datasource(Oracle)
|
||||
datasource.driver=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||
datasource.url=jdbc:log4jdbc:oracle:thin:@10.127.72.226:1521:XE
|
||||
datasource.username=sdl5
|
||||
datasource.password=dlatl#00
|
||||
|
||||
----
|
||||
|
||||
'''
|
||||
|
||||
=== 암호화 하기
|
||||
|
||||
실행 전에 위의 모든 파일들이 같은 폴더에 위치하는지 확인한다.
|
||||
|
||||
----
|
||||
C:\encrypt>dir
|
||||
C 드라이브의 볼륨에는 이름이 없습니다.
|
||||
볼륨 일련 번호: 1841-973A
|
||||
|
||||
C:\encrypt 디렉터리
|
||||
|
||||
2022-08-09 오후 02:29 <DIR> .
|
||||
2022-08-09 오후 02:29 <DIR> ..
|
||||
2022-08-08 오후 05:14 407 config.properties
|
||||
2022-08-09 오후 02:29 37 jasypt.key
|
||||
2022-08-09 오후 02:15 9,114,144 sdl-encrypt-1.0.0.jar
|
||||
3개 파일 9,114,588 바이트
|
||||
|
||||
----
|
||||
|
||||
*java -jar sdl-encrypt-1.0.0.jar* 실행
|
||||
|
||||
----
|
||||
C:\encrypt>java -jar sdl-encrypt-1.0.0.jar
|
||||
|
||||
. ____ _ __ _ _
|
||||
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
|
||||
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
|
||||
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||
' |____| .__|_| |_|_| |_\__, | / / / /
|
||||
=========|_|==============|___/=/_/_/_/
|
||||
:: Spring Boot :: (v2.7.2)
|
||||
|
||||
2022-08-09 14:30:33.152 INFO 47544 --- [ main] com.samsung.EncryptApplication : Starting EncryptApplication v1.0.0 using Java 1.8.0_341 on DESKTOP-BBNYDORY with PID 47544 (C:\encrypt\sdl-encrypt-1.0.0.jar started by bbnydory in C:\encrypt)
|
||||
2022-08-09 14:30:33.157 INFO 47544 --- [ main] com.samsung.EncryptApplication : No active profile set, falling back to 1 default profile: "default"
|
||||
2022-08-09 14:30:33.456 INFO 47544 --- [ main] ptablePropertiesBeanFactoryPostProcessor : Post-processing PropertySource instances
|
||||
2022-08-09 14:30:33.458 INFO 47544 --- [ main] c.u.j.EncryptablePropertySourceConverter : Skipping PropertySource configurationProperties [class org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource
|
||||
2022-08-09 14:30:33.461 INFO 47544 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource systemProperties [org.springframework.core.env.PropertiesPropertySource] to EncryptableMapPropertySourceWrapper
|
||||
2022-08-09 14:30:33.461 INFO 47544 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource systemEnvironment [org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource] to EncryptableSystemEnvironmentPropertySourceWrapper
|
||||
2022-08-09 14:30:33.463 INFO 47544 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource random [org.springframework.boot.env.RandomValuePropertySource] to EncryptablePropertySourceWrapper
|
||||
2022-08-09 14:30:33.464 INFO 47544 --- [ main] c.u.j.EncryptablePropertySourceConverter : Converting PropertySource Config resource 'class path resource [application.properties]' via location 'optional:classpath:/' [org.springframework.boot.env.OriginTrackedMapPropertySource] to EncryptableMapPropertySourceWrapper
|
||||
2022-08-09 14:30:33.500 INFO 47544 --- [ main] c.u.j.filter.DefaultLazyPropertyFilter : Property Filter custom Bean not found with name 'encryptablePropertyFilter'. Initializing Default Property Filter
|
||||
2022-08-09 14:30:33.551 INFO 47544 --- [ main] c.u.j.r.DefaultLazyPropertyResolver : Property Resolver custom Bean not found with name 'encryptablePropertyResolver'. Initializing Default Property Resolver
|
||||
2022-08-09 14:30:33.553 INFO 47544 --- [ main] c.u.j.d.DefaultLazyPropertyDetector : Property Detector custom Bean not found with name 'encryptablePropertyDetector'. Initializing Default Property Detector
|
||||
2022-08-09 14:30:33.611 INFO 47544 --- [ main] com.samsung.EncryptApplication : Started EncryptApplication in 0.765 seconds (JVM running for 1.669)
|
||||
2022-08-09 14:30:34.114 INFO 47544 --- [ main] com.samsung.EncryptService : datasource.username=ENC(fOdjJKVr6fkmdS46JlsCRw==)
|
||||
2022-08-09 14:30:34.115 INFO 47544 --- [ main] com.samsung.EncryptService : datasource.password=ENC(xfWl/qeA7tIMQ10UyU7IInz+SyY6ovX9)
|
||||
2022-08-09 14:30:34.117 INFO 47544 --- [ main] com.samsung.EncryptService : datasource.url=ENC(Ocp24DAK1cq0f8TezLyMpcedSu6nLdTACSIYyMoCnuanSJT5x76lw8yM0reAeu4qBrBF+yvItVyCBmtPlIv70A==)
|
||||
2022-08-09 14:30:34.119 INFO 47544 --- [ main] com.samsung.EncryptService : datasource.driver=ENC(GJ527jBO/7Xjilg8WbQHk55GwVUAvc6fIa1Yai1o68WeimiE5BpHNj3e7YnjhtQR)
|
||||
|
||||
----
|
||||
|
||||
'''
|
||||
|
||||
=== 확인하기
|
||||
|
||||
암호화가 정상적으로 실행 됐다면 같은 폴더에 config-enc.properties 파일이 생성된 것을 확인 할 수 있다.
|
||||
|
||||
----
|
||||
C:\encrypt>dir
|
||||
C 드라이브의 볼륨에는 이름이 없습니다.
|
||||
볼륨 일련 번호: 1841-973A
|
||||
|
||||
C:\encrypt 디렉터리
|
||||
|
||||
2022-08-09 오후 02:30 <DIR> .
|
||||
2022-08-09 오후 02:30 <DIR> ..
|
||||
2022-08-09 오후 02:30 305 config-enc.properties
|
||||
2022-08-08 오후 05:14 407 config.properties
|
||||
2022-08-09 오후 02:29 37 jasypt.key
|
||||
2022-08-09 오후 02:15 9,114,144 sdl-encrypt-1.0.0.jar
|
||||
4개 파일 9,114,893 바이트
|
||||
|
||||
----
|
||||
|
||||
config-enc.properties 파일내용
|
||||
|
||||
----
|
||||
datasource.username=ENC(fOdjJKVr6fkmdS46JlsCRw==)
|
||||
datasource.password=ENC(xfWl/qeA7tIMQ10UyU7IInz+SyY6ovX9)
|
||||
datasource.url=ENC(Ocp24DAK1cq0f8TezLyMpcedSu6nLdTACSIYyMoCnuanSJT5x76lw8yM0reAeu4qBrBF+yvItVyCBmtPlIv70A==)
|
||||
datasource.driver=ENC(GJ527jBO/7Xjilg8WbQHk55GwVUAvc6fIa1Yai1o68WeimiE5BpHNj3e7YnjhtQR)
|
||||
|
||||
----
|
||||
|
||||
'''
|
||||
|
||||
== 고급 사용
|
||||
|
||||
sdl-encrypt 모듈을 Spring Boot 프로젝트로 되어 있고 application.properties 파일에 암호화 key파일, Properties 파일, 출력파일에 대한 경로가 작성되어 있다.
|
||||
|
||||
application.properties 내용
|
||||
|
||||
[source,properties]
|
||||
----
|
||||
algorithm=PBEWithMD5AndDES
|
||||
keyObtentionIterations=1000
|
||||
pollSize=1
|
||||
stringOutputType=base64
|
||||
keyFilePath=./jasypt.key
|
||||
propertiesFilePath=./config.properties
|
||||
outputPropertiesFilePath=./config-enc.properties
|
||||
----
|
||||
|
||||
위 내용의 변경이 필요하면
|
||||
|
||||
sdl-encrypt-1.0.0.jar 파일이 있는 위치에 application.properties 파일을 새로 작성하고 위의 값들을 오버라이딩해서 사용할 수 있다.
|
||||
@@ -0,0 +1,221 @@
|
||||
= Quartz Clustering with JDBC-JobStore
|
||||
|
||||
== JDBC-JobStore DB 초기화
|
||||
quartz db schema (배포된 sdl-appendix 내 참조) 를 사용하는 DBMS에 맞는 SQL을 실행해 JDBC-JobStore를 사용하기 위한 Table을 생성한다.
|
||||
|
||||
CAUTION: Tibero DB는 미지원
|
||||
|
||||
== application.properties
|
||||
properties 파일 내용 중 Quartz JDBC-JobStore 관련 설정이다.
|
||||
[source, properties]
|
||||
----
|
||||
# Spring Quartz 설정
|
||||
spring.quartz.job-store-type=jdbc
|
||||
spring.quartz.jdbc.initialize-schema=always
|
||||
|
||||
spring.quartz.datasource.driver-class-name=org.postgresql.Driver
|
||||
spring.quartz.datasource.jdbcUrl=jdbc:postgresql://10.40.87.189:5445/SDL
|
||||
spring.quartz.datasource.username=sdl5
|
||||
spring.quartz.datasource.password=dlatl#123
|
||||
|
||||
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate <1>
|
||||
spring.quartz.properties.org.quartz.jobStore.isClustered=true
|
||||
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=60000
|
||||
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=15000
|
||||
spring.quartz.properties.org.quartz.scheduler.instanceName=sdl-prod <2>
|
||||
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO <3>
|
||||
spring.quartz.properties.org.quartz.scheduler.rmi.export=false
|
||||
spring.quartz.properties.org.quartz.scheduler.rmi.proxy=false
|
||||
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
|
||||
spring.quartz.properties.org.quartz.threadPool.threadCount=10 <4>
|
||||
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
|
||||
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
|
||||
spring.quartz.properties.org.quartz.scheduler-name=QuartzScheduler
|
||||
----
|
||||
|
||||
<1> DB 에 따라 Delegate 를 변경한다.
|
||||
+
|
||||
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate, +
|
||||
org.quartz.impl.jdbcjobstore.MSSQLDelegate, +
|
||||
org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
|
||||
<2> Quartz Scheduler 를 구별하는 Property 값으로 Clustering 할 서버에서는 같은 값을 세팅한다.
|
||||
<3> Unique 한 값이어야 하며 AUTO 일 경우 자동 생성된다.
|
||||
<4> Job 을 실행하기 위한 Thread 수
|
||||
|
||||
기타 자세한 Property 값은 http://www.quartz-scheduler.org/documentation/quartz-2.3.0/configuration/ConfigJDBCJobStoreClustering.html[Quartz 메뉴얼, window=_blank]을 참고 한다.
|
||||
|
||||
== QuartzClusteringConfig
|
||||
|
||||
QuartzClusteringConfig 파일은 Spring 에 등록된 CronTriggerFactoryBean 을 실행 하도록 SchedulerFactoryBean이 정의되어 있다.
|
||||
|
||||
[source, java]
|
||||
----
|
||||
@Bean
|
||||
public SchedulerFactoryBean setSchedulerFactoryBean(ApplicationContext applicationContext) {
|
||||
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
|
||||
|
||||
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
|
||||
jobFactory.setApplicationContext(applicationContext);
|
||||
|
||||
schedulerFactoryBean.setJobFactory(jobFactory); <1>
|
||||
schedulerFactoryBean.setDataSource(quartzDataSource()); <2>
|
||||
schedulerFactoryBean.setApplicationContext(applicationContext);
|
||||
schedulerFactoryBean.setTransactionManager(quartzTransactionManager()); <3>
|
||||
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
|
||||
schedulerFactoryBean.setStartupDelay(60);
|
||||
schedulerFactoryBean.setOverwriteExistingJobs(true);
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(quartzProperties.getProperties()); <4>
|
||||
schedulerFactoryBean.setQuartzProperties(properties);
|
||||
|
||||
schedulerFactoryBean.setTriggers(
|
||||
knoxSyncBatchConfig.knoxApprovalSyncTrigger().getObject(),
|
||||
userBatchConfig.batchUserLongTermCheckTrigger().getObject(),
|
||||
userBatchConfig.batchUserAuthExpiredTrigger().getObject(),
|
||||
sysUseLogBatchConfig.batchSysUseLogTrigger().getObject(),
|
||||
sysUseLogBatchConfig.batchMenuUseHistoryTrigger().getObject()
|
||||
); <5>
|
||||
|
||||
return schedulerFactoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.quartz.datasource")
|
||||
@QuartzDataSource <2>
|
||||
public DataSource quartzDataSource() {
|
||||
return DataSourceBuilder.create().type(HikariDataSource.class).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@QuartzTransactionManager <3>
|
||||
public DataSourceTransactionManager quartzTransactionManager() {
|
||||
return new DataSourceTransactionManager(quartzDataSource());
|
||||
}
|
||||
----
|
||||
|
||||
<1> Spring Bean을 AutoWiring
|
||||
<2> Job 정보를 조회, 저회 저장할 때 사용하는 Datasource (resources-dev, resources-prod/application.properties 참조)
|
||||
<3> Quartz 전용 TransactionManager
|
||||
<4> Quartz 설정 파일 세팅
|
||||
<5> SchedulerFactoryBean 에서 실행 할 Trigger
|
||||
|
||||
== JobDetail 및 Trigger Bean 등록
|
||||
아래는 KnoxSyncBatchConfig에 수행할 Job 클래스를 설정한 JobDetail Bean과 Trigger Bean을 등록한 예이다.
|
||||
|
||||
.KnoxSyncBatchConfig.java
|
||||
[source, java]
|
||||
----
|
||||
@Value("${knox.approval.sync.cron}")
|
||||
private String knoxApprovalSyncCron;
|
||||
|
||||
@Bean
|
||||
public JobDetail knoxApprovalSyncJobDetail() {
|
||||
return JobBuilder
|
||||
.newJob(KnoxSyncBatchExecutor.class) <1>
|
||||
.withIdentity("knoxApprovalSyncJob")
|
||||
.withDescription("Knox Approval Sync Batch")
|
||||
.storeDurably(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CronTriggerFactoryBean knoxApprovalSyncTrigger() {
|
||||
CronTriggerFactoryBean approvalTrigger = new CronTriggerFactoryBean();
|
||||
approvalTrigger.setJobDetail(knoxApprovalSyncJobDetail());
|
||||
approvalTrigger.setCronExpression(knoxApprovalSyncCron);
|
||||
return approvalTrigger;
|
||||
}
|
||||
----
|
||||
|
||||
<1> 배치잡 실행시 QuartzJobBean을 상속 받은 KnoxSyncBatchExecutor의 executeInternal 메소드가 실행된다.
|
||||
|
||||
== Job 구현
|
||||
QuartzJobBean은 Spring Bean을 DI Job의 구현체에서 사용할 수 있도록 Job을 구현한 추상 클래스이며,
|
||||
QuartzJobBean을 상속 받아 executeInternal 메소드를 오버라이드하여 실행할 배치 Service의 메소드를 호출한다.
|
||||
|
||||
.KnoxSyncBatchExecutor.java
|
||||
[source,java]
|
||||
----
|
||||
@Component
|
||||
@Log4j2
|
||||
public class KnoxSyncBatchExecutor extends QuartzJobBean {
|
||||
|
||||
@Value("${node-id}")
|
||||
private String nodeId;
|
||||
|
||||
@Autowired
|
||||
private KnoxApprovalSyncService knoxApprovalSyncService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||
JobDetail jobDetail = jobExecutionContext.getJobDetail();
|
||||
log.info(new StringBuilder()
|
||||
.append("nodeId : ").append(nodeId).append(", ")
|
||||
.append("jobName : ").append(jobDetail.getKey().getName()).append(", ")
|
||||
.append("description : ").append(jobDetail.getDescription())
|
||||
);
|
||||
knoxApprovalSyncService.synchronizeKnox();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
CAUTION: UserBatchExecutor의 경우 JobKey(JobBuilder.withIdentity 설정 값)를 이용해 분기 처리 하고 있으므로 Job 등록시 Name 명 지정에 주의하도록 한다.
|
||||
|
||||
.UserBatchConfig.java
|
||||
[source, java]
|
||||
----
|
||||
/**
|
||||
* 장기 미사용자 관리 Job
|
||||
*/
|
||||
@Bean
|
||||
public JobDetail batchUserLongTermCheckJob() {
|
||||
return JobBuilder
|
||||
.newJob(UserBatchExecutor.class)
|
||||
.withIdentity("batchUserLongTermCheck")
|
||||
.withDescription("User LongTerm Check Batch")
|
||||
.storeDurably(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 만료 권한 삭제 Job
|
||||
*/
|
||||
@Bean
|
||||
public JobDetail batchUserAuthExpiredJob() {
|
||||
return JobBuilder
|
||||
.newJob(UserBatchExecutor.class)
|
||||
.withIdentity("batchUserAuthExpired")
|
||||
.withDescription("User Auth Expired Batch")
|
||||
.storeDurably(true)
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
.UserBatchExecutor.java
|
||||
[source, java]
|
||||
----
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext jobExecutionContext) {
|
||||
JobDetail jobDetail = jobExecutionContext.getJobDetail();
|
||||
String jobName = jobDetail.getKey().getName();
|
||||
log.info(new StringBuilder()
|
||||
.append("nodeId : ").append(nodeId).append(", ")
|
||||
.append("jobName : ").append(jobName).append(", ")
|
||||
.append("description : ").append(jobDetail.getDescription())
|
||||
);
|
||||
switch (jobName) {
|
||||
case "batchUserLongTermCheck" -> userBatchService.execUserLongTermCheck();
|
||||
case "batchUserAuthExpired" -> userBatchService.execUserAuthValidCheck();
|
||||
case "batchUserAuthExpiredMailing" -> {
|
||||
try {
|
||||
userBatchService.execUserAuthValidAlarmMail();
|
||||
} catch (IOException | MessagingException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -0,0 +1,46 @@
|
||||
= Redis 설정
|
||||
|
||||
== RedisConfig
|
||||
SDL은 어플리케이션 속도 향상을 위해 자주 사용하는 Data를 Cache하고 있다.
|
||||
기본적으로 Local, Dev 환경에서는 Spring에서 제공하고 있는 ConcurrentMapCacheManager를 사용해
|
||||
Data를 Cache하고 있지만 Clustering 환경에서 Cache Replicated를 위해서
|
||||
Redis를 사용한다.
|
||||
|
||||
IMPORTANT: Ehcache 3부터 RMI, JMS 방식이 Cache 동기화 지원이 중단 됐으니 Cache 복제가 필요한 경우 Redis를 반드시 사용해야 한다.
|
||||
|
||||
application.properties
|
||||
----
|
||||
spring.cache.type=redis
|
||||
spring.data.redis.host=redis 서버 주소
|
||||
spring.data.redis.port=redis 포트
|
||||
----
|
||||
|
||||
RedisConfig
|
||||
----
|
||||
@Bean
|
||||
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
|
||||
return (builder) -> builder
|
||||
.withCacheConfiguration("message-all",
|
||||
RedisCacheConfiguration.defaultCacheConfig())
|
||||
.withCacheConfiguration("message",
|
||||
RedisCacheConfiguration.defaultCacheConfig())
|
||||
.withCacheConfiguration("menu-all",
|
||||
RedisCacheConfiguration.defaultCacheConfig())
|
||||
.withCacheConfiguration("menu-label-all",
|
||||
RedisCacheConfiguration.defaultCacheConfig())
|
||||
.withCacheConfiguration("menu-full-path-all",
|
||||
RedisCacheConfiguration.defaultCacheConfig())
|
||||
.withCacheConfiguration("page-full-path-all",
|
||||
RedisCacheConfiguration.defaultCacheConfig())
|
||||
.withCacheConfiguration("api-full-path-all",
|
||||
RedisCacheConfiguration.defaultCacheConfig())
|
||||
.withCacheConfiguration("api-user",
|
||||
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(300)))
|
||||
.withCacheConfiguration("api-user-menu",
|
||||
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(300)))
|
||||
.withCacheConfiguration("page-all-by-menu-auth",
|
||||
RedisCacheConfiguration.defaultCacheConfig());
|
||||
}
|
||||
----
|
||||
|
||||
각 Cache별 필요한 세팅은 redisCacheManagerBuilderCustomizer 메소드에서 할수 있으니 시스템 환경에 맞춰 적절하게 수정한다.
|
||||
@@ -0,0 +1,272 @@
|
||||
= UI 라이브러리(uiSDL)
|
||||
|
||||
== UI 라이브러리(uiSDL)란?
|
||||
UI라이브러리(uiSDL)은 웹 UI 개발 리드타임 단축을 위하여 실무에 유용한 컴포넌트들을 담은 UI라이브러리입니다. +
|
||||
Vue2 및 Vue3 용 라이브러리가 나뉘어져 있습니다.
|
||||
|
||||
NOTE: UI라이브러리 시스템 접속 +
|
||||
vue2 : http://uisdl.scp.samsung.net/v2/[window=_blnak] +
|
||||
vue3 : http://uisdl.scp.samsung.net/v3/[window=_blnak]
|
||||
|
||||
IMPORTANT: 접속이 안되는 경우 window host 파일에 아래와 같이 추가해 주시기 바랍니다 +
|
||||
10.195.53.147 uisdl.scp.samsung.net
|
||||
|
||||
|
||||
== 컴포넌트 주요 특징
|
||||
|
||||
- 삼성전자 웹 시스템 개발 시 빠르게 웹 화면을 개발할 수 있도록 사용빈도가 높은 UI 환경 구성 요소들을 오픈 소스 Framework인 Vue.js와 Bootstrap 기반으로 제작하여 제공합니다.
|
||||
|
||||
- 시스템의 특성에 맞게 제작이 가능하도록 화면의 구조인 레이아웃 6종과 단일 UI구성요소인 컴포넌트 37종으로 구성되어 있습니다
|
||||
+
|
||||
image::uisdl_1.png[]
|
||||
|
||||
- 각 페이지 화면의 우측 상단에 개발과 디자인 탭으로 구분되어 있습니다.
|
||||
+
|
||||
개발 탭에서는 개발 가이드와 간단한 기능 테스트를 해볼 수 있는 Code Demo 가 있고, 디자인 탭에서는 전사 UX 표준을 바탕으로 디자인 원칙과 구성요소들을 확인할 수 있습니다.
|
||||
+
|
||||
image::uisdl_2.png[]
|
||||
|
||||
== 컴포넌트 개발 환경
|
||||
* VueJS 버전 3
|
||||
* Used Open-source
|
||||
** bootstrap: 5.3.0 or Higher
|
||||
** vue-datepicker-next: 1.0.3 or Higher
|
||||
|
||||
== 컴포넌트 적용 방법
|
||||
|
||||
. Install from Local file
|
||||
+
|
||||
로컬 파일로부터 컴포넌트를 설치할 수 있습니다. 아래의 경로에서 파일을 다운로드 받습니다.
|
||||
+
|
||||
http://uisdl.scp.samsung.net/v3/download/sdl-component-3.0.0.tgz[sdl-component-3.0.0.tgz 다운로드]
|
||||
+
|
||||
해당 파일을 로컬의 임의의 위치로 복사한 다음, NPM 명령어로 컴포넌트를 설치합니다. 설치하실 프로젝트 루트 폴더로 이동한 다음, 아래 명령어를 커맨드 창에서 입력합니다.
|
||||
+
|
||||
[source, shell]
|
||||
----
|
||||
npm install --save ./{your-path}/sdl-component-{version}.tgz
|
||||
----
|
||||
|
||||
. Import bootstrap
|
||||
+
|
||||
'bootstrap' 관련 스타일과 스크립트를 Entry Point 파일에 Import 합니다.
|
||||
+
|
||||
[source, javascript]
|
||||
----
|
||||
// bootstrap css등록 및 popper가 포함된 bootstrap bundle js 임포트
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import 'bootstrap/dist/js/bootstrap.bundle.min';
|
||||
----
|
||||
|
||||
. Import sdl-component
|
||||
+
|
||||
'sdl-component' 관련 스타일과 스크립트를 Entry Point 파일에 Import 하고
|
||||
+
|
||||
[source, javascript]
|
||||
----
|
||||
import 'sdl-component/src/assets/css/custom.css';
|
||||
import SdlComponent from 'sdl-component';
|
||||
----
|
||||
+
|
||||
전역 Vue(app)에서 사용할 수 있도록 Plugin을 등록합니다.
|
||||
+
|
||||
[source, javascript]
|
||||
----
|
||||
app.use(SdlComponent);
|
||||
----
|
||||
|
||||
. Complete
|
||||
+
|
||||
이제 시스템 전역에서 'sdl-component'를 사용하실 수 있습니다
|
||||
|
||||
== UI 라이브러리 목록
|
||||
활용빈도, 사용성을 고려하여 사내시스템 개발에 필요한 Library를 제공합니다.
|
||||
|
||||
* 레이아웃
|
||||
+
|
||||
화면의 구조를 정의하는 레이아웃 유형
|
||||
+
|
||||
[cols="7,33,60",options="header"]
|
||||
|===
|
||||
|No |Library |설명
|
||||
|
||||
|1
|
||||
|GNB + Contents
|
||||
|GNB와 콘텐츠로 구성된 화면 형태
|
||||
|
||||
|2
|
||||
|GNB + LNB + Contents
|
||||
|GNB, LNB, 콘텐츠로 구성된 화면 형태
|
||||
|
||||
|3
|
||||
|GNB + LNB + Contents + Footer
|
||||
|GNB, LNB, Footer, 콘텐츠로 구성된 화면 형태
|
||||
|
||||
|4
|
||||
|GNB + LNB + Aside + Contents + Footer
|
||||
|GNB, LNB, Footer, Aside, 콘텐츠로 구성된 화면 형태
|
||||
|
||||
|5
|
||||
|Popup
|
||||
|현재 화면에서 추가적으로 띄우는 레이어 형태의 창으로, 사용 유형에 따라 구별하여 사용
|
||||
|
||||
|6
|
||||
|Center Frame
|
||||
|본 화면의 상하좌우 중앙에 콘텐츠 박스가 위치
|
||||
|===
|
||||
|
||||
* 컴포넌트
|
||||
+
|
||||
화면을 구성하는 요소 단위
|
||||
+
|
||||
[cols="7,33,60",options="header"]
|
||||
|===
|
||||
|No |Library |설명
|
||||
|1
|
||||
|GNB
|
||||
|(Global Navigation Bar) 시스템 명과 메뉴를 포함한 네비게이션으로 화면의 최 상단에 구성
|
||||
|
||||
|2
|
||||
|LNB
|
||||
|(Left Navigation Bar) 서브메뉴라고 불리며 화면의 좌측에 구성
|
||||
|
||||
|3
|
||||
|Aside
|
||||
|자주 사용하는 기능을 빠르게 접근(Quick Access)할 수 있도록 제공하는 영역
|
||||
|
||||
|4
|
||||
|Footer
|
||||
|Copyright, 사이트 맵, 이용약관 등의 정보를 포함하며, 화면의 하단에 구성
|
||||
|
||||
|5
|
||||
|Card List
|
||||
|한 레이아웃에서 여러 개의 카드를 함께 사용할 때 사용
|
||||
|
||||
|6
|
||||
|Search Grid
|
||||
|검색조건을 입력/선택하는 조회 영역과 데이터결과를 호출하는 리스트영역으로 구성할 때 사용
|
||||
|
||||
|7
|
||||
|Form
|
||||
|여러 개의 컨트롤(Input Box, Radio Button, Dropdown Box 등)를 활용하여 하나의 입력 폼으로 구성할 때 사용
|
||||
|
||||
|8
|
||||
|Form Detail
|
||||
|폼 화면에서 입력한 정보를 화면에 표시할 때 사용
|
||||
|
||||
|9
|
||||
|Board Detail
|
||||
|게시판 게시물의 기본적인 상세 화면
|
||||
|
||||
|10
|
||||
|Tree Detail
|
||||
|계층 구조를 나타내는 트리 요소와 트리에서 선택한 정보를 페이지 전환 없이 즉각적으로 보여줄 때 사용
|
||||
|
||||
|11
|
||||
|Create Board
|
||||
|게시판 게시물의 기본적인 입력 화면
|
||||
|
||||
|12
|
||||
|Approval
|
||||
|시스템에서 Knox 결재나 시스템 내부 결재시 사용
|
||||
|
||||
|13
|
||||
|Reply
|
||||
|간단하게 사용자 간의 의사소통을 할 수 있는 폼 형태로 구성
|
||||
|
||||
|14
|
||||
|Notice
|
||||
|사용자에게 시스템 내 서비스 관련 정보들을 공지 시 사용
|
||||
|
||||
|15
|
||||
|Alert
|
||||
|공지나 안내 사항 같은 부가적인 정보, 경고 상태 혹은 사용자의 행동에 대한 즉각적인 피드백이 필요할 때 사용
|
||||
|
||||
|16
|
||||
|Badge
|
||||
|알림 목적으로 사용하거나, 데이터를 시각적으로 강조하고 싶을 때 사용
|
||||
|
||||
|17
|
||||
|Button
|
||||
|사용자가 클릭하여 기능을 수행할 수 있는 그래픽 요소
|
||||
|
||||
|18
|
||||
|Card
|
||||
|다양한 정보들을 그룹화하여 하나의 카드 모양 안에 구성할 때 사용
|
||||
|
||||
|19
|
||||
|Collapse(Toggle)
|
||||
|사용자가 두개의 반대되는 상태 중에서 선택할 수 있는 온오프 스위치
|
||||
|
||||
|20
|
||||
|Tooltip
|
||||
|페이지 요소 또는 기능에 대한 추가 정보를 제공하는 간단하고 유용한 메시지
|
||||
|
||||
|21
|
||||
|Text Input
|
||||
|입력 폼 요소 중 한 줄의 비교적 짧은 데이터를 입력할 경우 사용
|
||||
|
||||
|22
|
||||
|Textarea
|
||||
|입력 폼 요소 중 데이터의 내용이 일정하지 않거나, 여러 줄의 데이터를 입력할 경우 사용
|
||||
|
||||
|23
|
||||
|Radio
|
||||
|두개 이상의 항목 중 하나를 선택할 때 사용
|
||||
|
||||
|24
|
||||
|Select
|
||||
|사용자가 옵션 중 하나 또는 다중으로 선택할 수 있을 때 사용
|
||||
|
||||
|25
|
||||
|Checkbox
|
||||
|여러 개의 항목 중 하나 이상을 다중 선택할 때 사용하거나 On/Off의 Toggle 경우에 사용
|
||||
|
||||
|26
|
||||
|Table
|
||||
|데이터/정보를 열과 행으로 구분하여 사용자가 쉽게 읽고 이해할 수 있도록 제공
|
||||
|
||||
|27
|
||||
|Progress
|
||||
|사용자 액션에 대한 즉각 데이터가 표시가 어려운 경우(데이터의 로딩 처리 시간이 긴 경우) 작업의 진행 상태에 대한 정확한 피드백을 제공하기 위해 사용
|
||||
|
||||
|28
|
||||
|Spinner
|
||||
|사용자 액션에 대한 즉각 데이터가 표시가 어려운 경우(데이터의 로딩 처리 시간이 긴 경우) 작업의 진행 상태에 대한 정확한 피드백을 제공하지 못할 경우 사용
|
||||
|
||||
|29
|
||||
|Navbar
|
||||
|시스템 명과 메뉴를 포함한 네비게이션 영역으로 주로 상단(GNB)에 제공
|
||||
|
||||
|30
|
||||
|File Attachment
|
||||
|사용자가 첨부파일을 다운받거나 업로드할 경우에 사용
|
||||
|
||||
|31
|
||||
|Tree
|
||||
|목록의 계층 구조를 표현하기 위해 사용하는 요소
|
||||
|
||||
|32
|
||||
|Datepicker
|
||||
|날짜를 선택하고 그에 해당하는 데이터를 얻을 수 있도록 위젯 형태로 제공
|
||||
|
||||
|33
|
||||
|Breadcrumb
|
||||
|시스템 내 사용자의 메뉴의 이동경로를 의미하며 현재 사용자의 위치를 보여줄 때 사용
|
||||
|
||||
|34
|
||||
|Pagination
|
||||
|많은 양의 데이터를 여러 페이지로 나누고, 이전, 다음 페이지 혹은 특정 페이지로 이동할 수 있는 일련의 링크를 목록 하단에 배치하여 제공
|
||||
|
||||
|35
|
||||
|Tab(Basic, Progress)
|
||||
|페이지 이동 및 시점 전환 없이 그룹화된 콘텐츠를 제공할 때 사용
|
||||
|
||||
|36
|
||||
|Jumbotron
|
||||
|어떤 특별한 내용이나 정보를 눈에 띄게 보여주기 위한 박스 형태의 공간
|
||||
|
||||
|37
|
||||
|Carousel
|
||||
|한 공간에 여러 개의 콘텐츠를 슬라이드 형태로 제공
|
||||
|===
|
||||
Reference in New Issue
Block a user