first
This commit is contained in:
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
HELP.md
|
||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
bin/
|
||||||
|
!**/src/main/**/bin/
|
||||||
|
!**/src/test/**/bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
out/
|
||||||
|
!**/src/main/**/out/
|
||||||
|
!**/src/test/**/out/
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
/logs/
|
||||||
6
api/IntegratedAppr/통합결재 목록(관리자&부서)조회.http
Normal file
6
api/IntegratedAppr/통합결재 목록(관리자&부서)조회.http
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
### GET request to example server
|
||||||
|
GET http://localhost:8010/api/itg/appr?page=1&row=10
|
||||||
|
sabun: psn14020
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
6
api/IntegratedAppr/통합결재 승인목록(본인)조회.http
Normal file
6
api/IntegratedAppr/통합결재 승인목록(본인)조회.http
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
### GET request to example server
|
||||||
|
GET http://localhost:8010/api/itg/appr/req?page=1&row=10
|
||||||
|
sabun: 17131303
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
16
api/approval/가격조사 결재 상태 변경.http
Normal file
16
api/approval/가격조사 결재 상태 변경.http
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
PUT http://localhost:8010//api/appr
|
||||||
|
sabun: 17131304
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"apprNo": "APPR-0000000040",
|
||||||
|
"apprOrd": 4,
|
||||||
|
"sabun": "17131304",
|
||||||
|
"apprStatCd": "0200",
|
||||||
|
"reason": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
#APPR-20250519017,20,17131303,김진형,0000,결재요청
|
||||||
|
#APPR-20250519017,30,17131304,손원장,0000,결재요청
|
||||||
12
api/business/비밀번호 변경.http
Normal file
12
api/business/비밀번호 변경.http
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
PUT http://localhost:8010//api/business/password
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJiaXpObyI6Ijk5OS05OS05OTk5OCIsImlwIjoiMTI3LjAuMC4xIiwic3ViIjoiOTk5LTk5LTk5OTk4IiwiaWF0IjoxNzQ2MDEyNjcyLCJleHAiOjE3NDYwMTI5NzJ9.RaT9wa3_8oPeL6nWv1_uM6QlY1mcHyCN2tC6sP_N03w
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"bizNo": "999-99-99998",
|
||||||
|
"oldPwd": "kospo2025!",
|
||||||
|
"pwd": "kospo2024!",
|
||||||
|
"rePwd": "kospo2024!"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
13
api/business/회원가입.http
Normal file
13
api/business/회원가입.http
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
POST http://localhost:8010//api/business
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"bizNo": "999-99-99998",
|
||||||
|
"pwd": "kospo2025!",
|
||||||
|
"rePwd": "kospo2025!",
|
||||||
|
"compNm": "테스트2",
|
||||||
|
"repNm": "테스트2",
|
||||||
|
"email": "bangae1@gmail.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
12
api/business/회원정보 수정.http
Normal file
12
api/business/회원정보 수정.http
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
PUT http://localhost:8010//api/business
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJiaXpObyI6Ijk5OS05OS05OTk5OCIsImlwIjoiMTI3LjAuMC4xIiwic3ViIjoiOTk5LTk5LTk5OTk4IiwiaWF0IjoxNzQ2MDEyNjcyLCJleHAiOjE3NDYwMTI5NzJ9.RaT9wa3_8oPeL6nWv1_uM6QlY1mcHyCN2tC6sP_N03w
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"bizNo": "999-99-99998",
|
||||||
|
"compNm": "테스트3",
|
||||||
|
"repNm": "테스트3",
|
||||||
|
"email": "bangae3@gmail.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
5
api/business/회원탈퇴.http
Normal file
5
api/business/회원탈퇴.http
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
DELETE http://localhost:8010//api/business/999-99-99998
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJiaXpObyI6Ijk5OS05OS05OTk5OCIsImlwIjoiMTI3LjAuMC4xIiwic3ViIjoiOTk5LTk5LTk5OTk4IiwiaWF0IjoxNzQ2MDEyNjcyLCJleHAiOjE3NDYwMTI5NzJ9.RaT9wa3_8oPeL6nWv1_uM6QlY1mcHyCN2tC6sP_N03w
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
###
|
||||||
4
api/contract/계약관리 상세조회.http
Normal file
4
api/contract/계약관리 상세조회.http
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
### GET request to example server
|
||||||
|
GET http://localhost:8010/api/cont/CONT-0000000005
|
||||||
|
|
||||||
|
###
|
||||||
5
api/contract/계약관리 조회(페이징).http
Normal file
5
api/contract/계약관리 조회(페이징).http
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
### GET request to example server
|
||||||
|
GET http://localhost:8010/api/cont/page
|
||||||
|
?cateStatCd=&signSdt=&signEdt=&page=1&row=10
|
||||||
|
|
||||||
|
###
|
||||||
22
api/contract/계약관리수정.http
Normal file
22
api/contract/계약관리수정.http
Normal file
File diff suppressed because one or more lines are too long
21
api/contract/계약관리저장.http
Normal file
21
api/contract/계약관리저장.http
Normal file
File diff suppressed because one or more lines are too long
8
api/dtlSpec/상세규격 엑셀업로드.http
Normal file
8
api/dtlSpec/상세규격 엑셀업로드.http
Normal file
File diff suppressed because one or more lines are too long
4
api/estimate/견적서(첨부파일) 삭제.http
Normal file
4
api/estimate/견적서(첨부파일) 삭제.http
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
DELETE http://localhost:8010/api/bid/BID-0000000009/1
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJiaXpObyI6Ijk5OS05OS05OTk5OSIsImlwIjoiMTI3LjAuMC4xIiwic3ViIjoiOTk5LTk5LTk5OTk5IiwiaWF0IjoxNzQ1NTkwNjQ3LCJleHAiOjE3NDU1OTA5NDd9.a9nwoQTDV702VbU0HnP1jS8SGhFi_3UjkjdBcjoDzCE
|
||||||
|
|
||||||
|
###
|
||||||
24
api/estimate/견적수정.http
Normal file
24
api/estimate/견적수정.http
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
PUT http://localhost:8010/api/estimate
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJiaXpObyI6Ijk5OS05OS05OTk5OSIsImlwIjoiMTI3LjAuMC4xIiwic3ViIjoiOTk5LTk5LTk5OTk5IiwiaWF0IjoxNzQ3MDQ4NDI5LCJleHAiOjE3NDcwNTAyMjl9.6SzI3eiNVmXOz1s1p8ab26fT80rbSJBfoDlKLQ4z5Sg
|
||||||
|
|
||||||
|
{
|
||||||
|
"estimates": [
|
||||||
|
{
|
||||||
|
"estNo": "EST-0000000003",
|
||||||
|
"mngNm": "조진우",
|
||||||
|
"unitPrc": 2,
|
||||||
|
"amt": 40000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"estNo": "EST-0000000004",
|
||||||
|
"mngNm": "조진우",
|
||||||
|
"unitPrc": 12,
|
||||||
|
"amt": 60000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pbAtts": []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
28
api/estimate/견적저장.http
Normal file
28
api/estimate/견적저장.http
Normal file
File diff suppressed because one or more lines are too long
6
api/estimate/견적조회.http
Normal file
6
api/estimate/견적조회.http
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
### GET request to example server
|
||||||
|
GET http://localhost:8010/api/estimate
|
||||||
|
?prcsNo=PRCS-0000000006
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJiaXpObyI6Ijk5OS05OS05OTk5OSIsImlwIjoiMTI3LjAuMC4xIiwic3ViIjoiOTk5LTk5LTk5OTk5IiwiaWF0IjoxNzQ3MDQ4NDI5LCJleHAiOjE3NDcwNTAyMjl9.6SzI3eiNVmXOz1s1p8ab26fT80rbSJBfoDlKLQ4z5Sg
|
||||||
|
|
||||||
|
###
|
||||||
10
api/login/사업자로그인.http
Normal file
10
api/login/사업자로그인.http
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
### GET request to example server
|
||||||
|
POST http://localhost:8010/api/login
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"bizNo": "111-11-11111",
|
||||||
|
"pwd": "kospo2025!"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
6
api/price_survey/(외부)가격조사_견적관리 조회(페이징).http
Normal file
6
api/price_survey/(외부)가격조사_견적관리 조회(페이징).http
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
### GET request to example server
|
||||||
|
GET http://localhost:8010/api/prcs/external/page
|
||||||
|
?cateCd=&stCd=®Nm=®Sdt=®Edt=&page=1&row=10
|
||||||
|
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJiaXpObyI6Ijk5OS05OS05OTk5OSIsImlwIjoiMTI3LjAuMC4xIiwic3ViIjoiOTk5LTk5LTk5OTk5IiwiaWF0IjoxNzQ2MDEzMjAwLCJleHAiOjE3NDYwMTM1MDB9.2mSDvhpXtvkYHeRr_d1tiKvnSU0OGehBq0ce1AOvXnE
|
||||||
|
|
||||||
|
###
|
||||||
4
api/price_survey/가격조사상세조회.http
Normal file
4
api/price_survey/가격조사상세조회.http
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
### GET request to example server
|
||||||
|
GET http://localhost:8010/api/prcs/PRCS-20250523052
|
||||||
|
|
||||||
|
###
|
||||||
54
api/price_survey/가격조사수정.http
Normal file
54
api/price_survey/가격조사수정.http
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
PUT http://localhost:8010//api/prcs
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"prcsNo": "PRCS-20250530003",
|
||||||
|
"cateCd": "0200",
|
||||||
|
"title": "가격조사 결재 테스트",
|
||||||
|
"content": "시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n시나리오테스트입니다.\n",
|
||||||
|
"regSdat": "2025-04-01",
|
||||||
|
"regEdat": "2025-07-30",
|
||||||
|
"prvYn": false,
|
||||||
|
"prvRsn": "",
|
||||||
|
"prvPwd": "",
|
||||||
|
"aiYn": false,
|
||||||
|
"prcsAtts": [
|
||||||
|
],
|
||||||
|
"dtlSpecs": [
|
||||||
|
],
|
||||||
|
"apprReqs": [
|
||||||
|
{
|
||||||
|
"gubunCd": "",
|
||||||
|
"apprNo": "APPR-0000000066",
|
||||||
|
"sabun": "psn14020",
|
||||||
|
"name": "조진우",
|
||||||
|
"attendCd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gubunCd": "0100",
|
||||||
|
"apprNo": "APPR-0000000066",
|
||||||
|
"sabun": "17131303",
|
||||||
|
"name": "김진형",
|
||||||
|
"attendCd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gubunCd": "0200",
|
||||||
|
"apprNo": "APPR-0000000066",
|
||||||
|
"sabun": "17131304",
|
||||||
|
"name": "손원장",
|
||||||
|
"attendCd": "01"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"prcsBizs": [
|
||||||
|
{
|
||||||
|
"bizNo": "999-99-99999",
|
||||||
|
"email": "bangae2@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bizNo": "111-11-11111",
|
||||||
|
"email": "bangae1@gmail.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
46
api/price_survey/가격조사수정_Test.http
Normal file
46
api/price_survey/가격조사수정_Test.http
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
PUT https://svcm.hmsn.ink//api/prcs
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"prcsNo": "PRCS-20250521023",
|
||||||
|
"cateCd": "0000",
|
||||||
|
"title": "4층 파티션 구매123",
|
||||||
|
"content": "싼거 찾아요123",
|
||||||
|
"regSdat": "2025-05-01",
|
||||||
|
"regEdat": "2025-05-03",
|
||||||
|
"prvYn": true,
|
||||||
|
"prvRsn": "",
|
||||||
|
"prvPwd": "",
|
||||||
|
"aiYn": true,
|
||||||
|
"prcsBizs": [
|
||||||
|
{
|
||||||
|
"bizNo": "999-99-99999",
|
||||||
|
"email": "aa@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bizNo": "111-11-11111",
|
||||||
|
"email": "bb@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"prcsAtts": [],
|
||||||
|
"apprReqs": [
|
||||||
|
{
|
||||||
|
"gubunCd": "0000",
|
||||||
|
"sabun": "psn14020",
|
||||||
|
"name": "조진우",
|
||||||
|
"apprNo": "APPR-0000000023",
|
||||||
|
"apprOrd": 1,
|
||||||
|
"attendCd": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gubunCd": "0100",
|
||||||
|
"sabun": "15000007",
|
||||||
|
"name": "조용식",
|
||||||
|
"apprNo": "APPR-0000000023",
|
||||||
|
"apprOrd": 2,
|
||||||
|
"attendCd": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
4
api/price_survey/가격조사완료처리.http
Normal file
4
api/price_survey/가격조사완료처리.http
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
PUT http://localhost:8010/api/prcs/survey/PRCS-20250526039
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
###
|
||||||
66
api/price_survey/가격조사저장.http
Normal file
66
api/price_survey/가격조사저장.http
Normal file
File diff suppressed because one or more lines are too long
5
api/price_survey/가격조사조회(페이징).http
Normal file
5
api/price_survey/가격조사조회(페이징).http
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
### GET request to example server
|
||||||
|
GET http://localhost:8010/api/prcs/page
|
||||||
|
?cateCd=&stCd=®Nm=®Sdt=®Edt=&page=1&row=10
|
||||||
|
|
||||||
|
###
|
||||||
3
api/price_survey/가격조사회수.http
Normal file
3
api/price_survey/가격조사회수.http
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
PUT http://localhost:8010/api/prcs/ret/PRCS-20250522037
|
||||||
|
Content-Type: application/json
|
||||||
|
###
|
||||||
10
api/sapAppr/sap결재상태 변경.http
Normal file
10
api/sapAppr/sap결재상태 변경.http
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
PUT http://localhost:8010/api/sap/appr
|
||||||
|
sabun: 17131303
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"wkfid": "FI2025900017",
|
||||||
|
"wkfsq": 2,
|
||||||
|
"bname": "17131303",
|
||||||
|
"wkfst": "A"
|
||||||
|
}
|
||||||
33
api/sapAppr/sap협조 (신규결재) 저장.http
Normal file
33
api/sapAppr/sap협조 (신규결재) 저장.http
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
POST http://localhost:8010/api/sap/appr
|
||||||
|
sabun: 15000062
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"wkfid": "FI2025900017",
|
||||||
|
"apprs": [
|
||||||
|
{
|
||||||
|
"label": "협조",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"lineclsf": "Q",
|
||||||
|
"bname": "15000062",
|
||||||
|
"abscd": "",
|
||||||
|
"grpid": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "I",
|
||||||
|
"bname": "15000057",
|
||||||
|
"abscd": "",
|
||||||
|
"grpid": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "A",
|
||||||
|
"bname": "15000056",
|
||||||
|
"abscd": "",
|
||||||
|
"grpid": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
47
api/slip/임시전표수정(결재만 수정가능 그외 삭제).http
Normal file
47
api/slip/임시전표수정(결재만 수정가능 그외 삭제).http
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
PUT http://localhost:8010/api/slip
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"contNo": "CONT-20250527037",
|
||||||
|
"zwf0011t": {
|
||||||
|
"belnr": "0604103229",
|
||||||
|
"wkfid": "FI2025900014",
|
||||||
|
"apprs": [
|
||||||
|
{
|
||||||
|
"label": "결재",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"lineclsf": "Q",
|
||||||
|
"bname": "psn14020",
|
||||||
|
"abscd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "I",
|
||||||
|
"bname": "17131303",
|
||||||
|
"abscd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "I",
|
||||||
|
"bname": "15000004",
|
||||||
|
"abscd": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "E",
|
||||||
|
"bname": "15000037",
|
||||||
|
"abscd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "E",
|
||||||
|
"bname": "15000062",
|
||||||
|
"abscd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "A",
|
||||||
|
"bname": "15000005",
|
||||||
|
"abscd": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
71
api/slip/임시전표저장.http
Normal file
71
api/slip/임시전표저장.http
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
POST http://localhost:8010/api/slip
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"contNo": "CONT-20250527037",
|
||||||
|
"bldat": "20250501",
|
||||||
|
"budat": "20250502",
|
||||||
|
"waers": "KRW",
|
||||||
|
"bktxt": "전표 생성 테스트1",
|
||||||
|
"lifnr": "999-99-99999",
|
||||||
|
"wrbtr": "1203",
|
||||||
|
"mwskz": "V4",
|
||||||
|
"gsber": "1000",
|
||||||
|
"bupla": "1000",
|
||||||
|
"zterm": "PF00",
|
||||||
|
"banks": "KR",
|
||||||
|
"bankl": "012",
|
||||||
|
"bankn": "3510876657453",
|
||||||
|
"hkont": "5366010",
|
||||||
|
"wrbtrS": "1102",
|
||||||
|
"kostl": "12330",
|
||||||
|
"projk": "",
|
||||||
|
"trtGubun": "11",
|
||||||
|
"txBillSeq": "202503231",
|
||||||
|
"slipAtts": [
|
||||||
|
{
|
||||||
|
"logiFnm": "sapApprTest.pdf",
|
||||||
|
"data": "4paR4paS4paT4paI4pa6IFdpbmRvd3MgWC1MaXRlIOKXhOKWiOKWk+KWkuKWkQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0K4oCiIEN1c3RvbSBXaW5kb3dzIEJ1aWxkcyBkZXNpZ25lZCB0byBCcmVhdGhlIE5ldyBMaWZlIGludG8geW91ciBQQyENCuKAoiBTbWFsbGVyLCBMaWdodGVyLCBGYXN0ZXIgYW5kIE1vcmUgUmVzcG9uc2l2ZS4NCuKAoiBFbmhhbmNlZCBQcml2YWN5LCBBY2Nlc3NpYmlsaXR5IGFuZCBDb250cm9sLg0K4oCiIDEwMCUgQ2xlYW4uIDEwMCUgU2FmZS4gMTAwJSBPcHRpbWl6ZWQuDQrigKIgQXJlIFlvdSBSZWFkeSBUbyBUYWtlIFlvdXIgV2luZG93cyBleHBlcmllbmNlIHRvIHRoZSBOZXh0IExldmVsIT8NCg0KICDimJEgQ09NRSBWSVNJVCBVUyEg4piRDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCuKeoe+4jyBodHRwczovL3dpbmRvd3N4bGl0ZS5jb20NCuKeoe+4jyBodHRwczovL3d3dy55b3V0dWJlLmNvbS9AV2luZG93c1gtTGl0ZQ0K4p6h77iPIGh0dHBzOi8va28tZmkuY29tL3dpbmRvd3N4bGl0ZQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQrinaTvuI8gWW91IGNhbiBIZWxwIFN1cHBvcnQgVGhpcyBQcm9qZWN0IGJ5IERvbmF0aW5nIQ0K4p6h77iPIGh0dHBzOi8va28tZmkuY29tL3dpbmRvd3N4bGl0ZQ0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQrinaTvuI8gWW91IENhbiBIZWxwIFN1cHBvcnQgVGhpcyBQcm9qZWN0IGJ5IERpc2FibGluZyBBZCBCbG9ja2Vycw0KYW5kIGNsaWNraW5nIGFkcyB0aGF0IGludGVyZXN0IHlvdSB3aGlsZSB2aXNpdGluZyBvdXIgV2Vic2l0ZSBhbmQgb3VyIFlvdVR1YmUgQ2hhbm5lbCENCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0K4p6h77iPIFRoYW5rIHlvdSBmb3IgeW91ciBzdXBwb3J0LCBhbmQgZm9yIGJlaW5nIGEgcGFydCBvZiBvdXIgY29tbXVuaXR5IQ0K4p6h77iPIFlvdXJzIFRydWx5IC0gRkJDb25hbiAmIFRoZSBXaW5kb3dzIFgtTGl0ZSBUZWFtDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg=="
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"zwf0011t": {
|
||||||
|
"wkftx": "결재 테스트 11",
|
||||||
|
"apprs": [
|
||||||
|
{
|
||||||
|
"label": "결재",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"lineclsf": "Q",
|
||||||
|
"bname": "psn14020",
|
||||||
|
"abscd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "I",
|
||||||
|
"bname": "17131303",
|
||||||
|
"abscd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "I",
|
||||||
|
"bname": "15000004",
|
||||||
|
"abscd": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "E",
|
||||||
|
"bname": "15000037",
|
||||||
|
"abscd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "E",
|
||||||
|
"bname": "15000062",
|
||||||
|
"abscd": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lineclsf": "A",
|
||||||
|
"bname": "15000005",
|
||||||
|
"abscd": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
94
build.gradle
Normal file
94
build.gradle
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'org.springframework.boot' version '3.3.2'
|
||||||
|
id 'io.spring.dependency-management' version '1.1.6'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.kospo'
|
||||||
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
compileOnly {
|
||||||
|
extendsFrom annotationProcessor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
ext['queryDslVersion'] = '5.0.0'
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web-services'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-reactor-netty'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-mail'
|
||||||
|
implementation 'org.springframework.retry:spring-retry:2.0.8'
|
||||||
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
|
||||||
|
|
||||||
|
|
||||||
|
implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
|
||||||
|
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5'
|
||||||
|
implementation 'com.github.ulisesbocchio:jasypt-maven-plugin:3.0.5'
|
||||||
|
|
||||||
|
implementation 'org.jetbrains:annotations:24.0.0'
|
||||||
|
|
||||||
|
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta"
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
|
// annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
|
||||||
|
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
|
||||||
|
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
|
||||||
|
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
|
||||||
|
|
||||||
|
implementation 'com.google.code.gson:gson:2.9.0'
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.3'
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-core:2.15.3'
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.15.3'
|
||||||
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.3'
|
||||||
|
|
||||||
|
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
|
||||||
|
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
|
||||||
|
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
|
||||||
|
|
||||||
|
implementation 'org.apache.poi:poi:5.4.1'
|
||||||
|
implementation 'org.apache.poi:poi-ooxml:5.4.1'
|
||||||
|
|
||||||
|
implementation 'javax.xml.bind:jaxb-api:2.3.1'
|
||||||
|
implementation 'org.modelmapper:modelmapper:3.2.0'
|
||||||
|
implementation 'org.apache.commons:commons-lang3:3.13.0'
|
||||||
|
implementation 'commons-codec:commons-codec:1.16.0'
|
||||||
|
implementation 'org.apache.commons:commons-dbcp2:2.10.0'
|
||||||
|
implementation 'org.apache.commons:commons-pool2:2.12.0'
|
||||||
|
implementation 'commons-logging:commons-logging:1.2'
|
||||||
|
implementation 'commons-io:commons-io:2.19.0'
|
||||||
|
implementation 'org.apache.commons:commons-email:1.5'
|
||||||
|
implementation 'org.postgresql:postgresql:42.7.3'
|
||||||
|
|
||||||
|
|
||||||
|
// https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc10
|
||||||
|
implementation 'com.oracle.database.jdbc:ojdbc10:19.27.0.0'
|
||||||
|
// https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc8
|
||||||
|
implementation 'com.oracle.database.jdbc:ojdbc8:23.8.0.25.04'
|
||||||
|
// sockjs
|
||||||
|
|
||||||
|
runtimeOnly group: 'io.netty', name: 'netty-resolver-dns-native-macos', version: '4.1.112.Final'
|
||||||
|
compileOnly 'org.projectlombok:lombok'
|
||||||
|
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('test') {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
31
fileToByte.html
Normal file
31
fileToByte.html
Normal file
File diff suppressed because one or more lines are too long
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
249
gradlew
vendored
Normal file
249
gradlew
vendored
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
92
gradlew.bat
vendored
Normal file
92
gradlew.bat
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
0
log.config.path_IS_UNDEFINED/err_log.log
Normal file
0
log.config.path_IS_UNDEFINED/err_log.log
Normal file
1271
log.config.path_IS_UNDEFINED/log.config.filename_IS_UNDEFINED.log
Normal file
1271
log.config.path_IS_UNDEFINED/log.config.filename_IS_UNDEFINED.log
Normal file
File diff suppressed because one or more lines are too long
1
settings.gradle
Normal file
1
settings.gradle
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'svcm'
|
||||||
21
src/main/java/com/kospo/drm/Application.java
Normal file
21
src/main/java/com/kospo/drm/Application.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package com.kospo.drm;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.servers.Server;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
@OpenAPIDefinition(servers = {
|
||||||
|
@Server(url = "http://localhost:8010", description = "로컬 api"),
|
||||||
|
@Server(url = "http://hmsn.ink:8010", description = "개발 api"),
|
||||||
|
@Server(url = "https://svcm.hmsn.ink", description = "외부 api"),
|
||||||
|
})
|
||||||
|
@EnableScheduling
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
src/main/java/com/kospo/drm/config/CorsPreflightHandler.java
Normal file
19
src/main/java/com/kospo/drm/config/CorsPreflightHandler.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package com.kospo.drm.config;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class CorsPreflightHandler {
|
||||||
|
@RequestMapping(method = RequestMethod.OPTIONS, path="/api/**")
|
||||||
|
public ResponseEntity options() {
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*")
|
||||||
|
.header(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE, OPTIONS")
|
||||||
|
.header(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.kospo.drm.config.exception;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Data
|
||||||
|
@Schema(name="errorResponse", description = "오류")
|
||||||
|
public class CustomErrorResponse {
|
||||||
|
@Schema(name="code", description = "오류 코드", example = "30000")
|
||||||
|
private String code;
|
||||||
|
@Schema(name="body", description = "오류 메시지", example = "오류 발생")
|
||||||
|
private String body;
|
||||||
|
@Schema(name="errTime", description = "오류 시간", example = "1761000247")
|
||||||
|
private long errTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package com.kospo.drm.config.exception;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kospo.drm.exception.CustomException;
|
||||||
|
import com.kospo.drm.exception.CustomMessageException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@ControllerAdvice
|
||||||
|
@Controller
|
||||||
|
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
|
||||||
|
|
||||||
|
public static final String ANSI_RESET = "\u001B[0m";
|
||||||
|
public static final String ANSI_YELLOW_COLOR = "\u001B[33m";
|
||||||
|
|
||||||
|
|
||||||
|
@ExceptionHandler(CustomException.class)
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity customException(CustomException e, HandlerMethod method, HttpServletRequest request, WebRequest webRequest) {
|
||||||
|
|
||||||
|
Date errorDate = new Date();
|
||||||
|
String exceptionName = e.getClass().getSimpleName();
|
||||||
|
String controllerName = method.getMethod().getDeclaringClass().getSimpleName();
|
||||||
|
String methodName = method.getMethod().getName();
|
||||||
|
int lineNumber = e.getStackTrace()[0].getLineNumber();
|
||||||
|
String detail = e.getMessage();
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log.error(ANSI_YELLOW_COLOR + "[EVENT_TIME: {} | CONTROLLER_NAME : {} | METHOD_NAME : {} | EXCEPTION_LINE : {} | EXCEPTION_NAME : {} | DETAIL : {} | REQUEST_URI : {}]" + ANSI_RESET
|
||||||
|
, errorDate
|
||||||
|
, controllerName
|
||||||
|
, methodName
|
||||||
|
, lineNumber
|
||||||
|
, exceptionName
|
||||||
|
, detail
|
||||||
|
, requestURI);
|
||||||
|
|
||||||
|
HttpHeaders resHeaders = new HttpHeaders();
|
||||||
|
resHeaders.add("Content-Type", "application/json;charset=UTF-8");
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers(resHeaders).body(
|
||||||
|
CustomErrorResponse.builder()
|
||||||
|
.code(e.getErrorCode().getCode())
|
||||||
|
.body(e.getErrorCode().getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ExceptionHandler(CustomMessageException.class)
|
||||||
|
@ResponseBody
|
||||||
|
public ResponseEntity customMessageException(CustomMessageException e, HandlerMethod method, HttpServletRequest request, WebRequest webRequest) {
|
||||||
|
|
||||||
|
Date errorDate = new Date();
|
||||||
|
String exceptionName = e.getClass().getSimpleName();
|
||||||
|
String controllerName = method.getMethod().getDeclaringClass().getSimpleName();
|
||||||
|
String methodName = method.getMethod().getName();
|
||||||
|
int lineNumber = e.getStackTrace()[0].getLineNumber();
|
||||||
|
String detail = e.getMessage();
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log.error(ANSI_YELLOW_COLOR + "[EVENT_TIME: {} | CONTROLLER_NAME : {} | METHOD_NAME : {} | EXCEPTION_LINE : {} | EXCEPTION_NAME : {} | DETAIL : {} | REQUEST_URI : {}]" + ANSI_RESET
|
||||||
|
, errorDate
|
||||||
|
, controllerName
|
||||||
|
, methodName
|
||||||
|
, lineNumber
|
||||||
|
, exceptionName
|
||||||
|
, detail
|
||||||
|
, requestURI);
|
||||||
|
|
||||||
|
HttpHeaders resHeaders = new HttpHeaders();
|
||||||
|
resHeaders.add("Content-Type", "application/json;charset=UTF-8");
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).headers(resHeaders).body(
|
||||||
|
CustomErrorResponse.builder()
|
||||||
|
.code(e.getCusMessage().getCode())
|
||||||
|
.body(e.getCusMessage().getMessage())
|
||||||
|
.errTime(System.currentTimeMillis()).build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<?> handleException(Exception e, HandlerMethod method, HttpServletRequest request, WebRequest webRequest) {
|
||||||
|
|
||||||
|
// Date errorDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
|
||||||
|
Date errorDate = new Date();
|
||||||
|
String exceptionName = e.getClass().getSimpleName();
|
||||||
|
String controllerName = method.getMethod().getDeclaringClass().getSimpleName();
|
||||||
|
String methodName = method.getMethod().getName();
|
||||||
|
int lineNumber = e.getStackTrace()[0].getLineNumber();
|
||||||
|
String detail = e.getMessage();
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log.error(ANSI_YELLOW_COLOR + "[EVENT_TIME: {} | CONTROLLER_NAME : {} | METHOD_NAME : {} | EXCEPTION_LINE : {} | EXCEPTION_NAME : {} | DETAIL : {} | REQUEST_URI : {}]" + ANSI_RESET
|
||||||
|
, errorDate
|
||||||
|
, controllerName
|
||||||
|
, methodName
|
||||||
|
, lineNumber
|
||||||
|
, exceptionName
|
||||||
|
, detail
|
||||||
|
, requestURI);
|
||||||
|
|
||||||
|
|
||||||
|
HttpHeaders resHeaders = new HttpHeaders();
|
||||||
|
resHeaders.add("Content-Type", "application/json;charset=UTF-8");
|
||||||
|
return ResponseEntity.status(500).headers(resHeaders).body(
|
||||||
|
CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.INTERNAL_SERVER_ERROR.getCode())
|
||||||
|
.body(ErrorCode.INTERNAL_SERVER_ERROR.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
65
src/main/java/com/kospo/drm/config/exception/ErrorCode.java
Normal file
65
src/main/java/com/kospo/drm/config/exception/ErrorCode.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package com.kospo.drm.config.exception;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum ErrorCode {
|
||||||
|
/*트랜잭션 관련*/
|
||||||
|
FIND_ERR("20000", "조회에 실패했습니다."),
|
||||||
|
SAVE_ERR("20001", "저장에 실패했습니다."),
|
||||||
|
UPD_ERR("20002", "수정에 실패했습니다."),
|
||||||
|
DEL_ERR("20003", "삭제에 실패했습니다."),
|
||||||
|
APPR_ERR("20004", "결재가 완료 되지 않았습니다.."),
|
||||||
|
APPR_LOCK("20005", "결재 진행중 이거나 완료 상태로 수정이 불가합니다."),
|
||||||
|
APPR_SVY_ERR("20006", "조사완료 되어 등록이 불가합니다."),
|
||||||
|
/*토큰 관련*/
|
||||||
|
EXPIRE_TOKEN("30000", "토큰이 만료 되었습니다."),
|
||||||
|
MALFORMED_JWT("30001", "JWT 문자열은 정확히 2개의 마침표 문자를 포함해야 합니다."),
|
||||||
|
/*bad request*/
|
||||||
|
BIND_ERROR("40001", "파라미터를 다시 확인해주세요."),
|
||||||
|
/*존재 하지 않음*/
|
||||||
|
NO_RESULT("44001", "존재하지 않는 데이터 입니다."),
|
||||||
|
NO_ACCOUNT("44011", "아이디 또는 비밀번호를 다시 확인해주세요."),
|
||||||
|
NO_MATCH_ACCOUNT("44012", "일치하는 사용자가 없습니다."),
|
||||||
|
NO_TOKEN("44021", "헤더에 토큰이 없습니다."),
|
||||||
|
NO_MATCH_TOKEN("44022", "일치 하지 않는 토큰입니다."),
|
||||||
|
NO_SAME_IP("44030", "등록된 아이피와 일치 하지 않습니다. 다시 로그인 해주세요"),
|
||||||
|
NO_SAME_DATA("44031", "사용자가 소유한 데이터가 아닙니다."),
|
||||||
|
No_SVY_NOT_EXECUTE("44032", "사전조사가 실행 되지 않았거나 등록 기간중 입니다."),
|
||||||
|
SAME_DATA("44020", "이미 등록된 데이터가 있습니다."),
|
||||||
|
/*권한 관련*/
|
||||||
|
|
||||||
|
AUTHENTICATION("43000", "사용자가 정보가 일치하지 않습니다."),
|
||||||
|
AUTHENTICATION_NOT_SUPPORT("43001", "허용되지 않은 사용자입니다."),
|
||||||
|
NO_REGISTER("43002", "사용자가 존재하지 않습니다."),
|
||||||
|
SLEEP_ACCOUNT("43003", "휴면 계정입니다. 개인정보에 약관에 동의해주세요."),
|
||||||
|
//500 INTERNAL SERVER ERROR
|
||||||
|
INTERNAL_SERVER_ERROR("50000", "서버 에러입니다. 유지보수관리자에게 연락주세요!"),
|
||||||
|
|
||||||
|
/*로그인 관련*/
|
||||||
|
LOGIN_ERR("60000", "사업자번호 또는 비밀번호가 일치 하지 않습니다."),
|
||||||
|
NOT_REGISTER("60001", "가입된 사업자가 아닙니다."),
|
||||||
|
ALREADY_REGISTER("60001", "이미 가입된 사업자 입니다."),
|
||||||
|
OLD_PWD_NOT_MATCH("60002", "현재 비밀번호가 일치 하지 않습니다."),
|
||||||
|
PWD_NOT_MATCH("60003", "비밀번호가 일치 하지 않습니다."),
|
||||||
|
EMAIL_VALIDATE_ERR("60004", "정상적인 이메일이 아닙니다."),
|
||||||
|
|
||||||
|
/*결재관련*/
|
||||||
|
PASS_APPR_TWO_PERSON("70001", "전결이 두명이상 될수 없습니다."),
|
||||||
|
PASS_APPR_MANIPULATE_DATA("70002", "변경 대상자 정보가 변조 되었습니다."),
|
||||||
|
|
||||||
|
/*파일 관련*/
|
||||||
|
FILE_NOT_FOUND_ERROR("80000", "파일이 존재 하지 않습니다."),
|
||||||
|
FILE_EXT_UPLOAD_ERROR("80001", "허용되지 않은 확장자 입니다."),
|
||||||
|
FILE_UPLOAD_ERROR("80002", "파일이 정상적이지 않습니다. 확인해주세요"),
|
||||||
|
FILE_DELETE_ERROR("80003", "파일 삭제중 오류가 발생했습니다. 확인해주세요"),
|
||||||
|
FILE_DOWNLOAD_ERROR("80004", "파일 다운로드에 실패 했습니다."),
|
||||||
|
FILE_REQUIRED("80005", "첨부파일은 필수 항목입니다."),
|
||||||
|
|
||||||
|
NO_SIGNAL("90000", "응답 코드가 없습니다.");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String body;
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.kospo.drm.config.filter;
|
||||||
|
|
||||||
|
import com.kospo.drm.config.exception.ErrorCode;
|
||||||
|
import com.kospo.drm.config.jwt.JwtUtil;
|
||||||
|
import com.kospo.drm.exception.*;
|
||||||
|
import com.kospo.drm.model.Token;
|
||||||
|
import com.kospo.drm.model.TokenId;
|
||||||
|
import com.kospo.drm.repository.TokenRepository;
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import io.jsonwebtoken.MalformedJwtException;
|
||||||
|
import io.jsonwebtoken.io.DecodingException;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||||
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter {
|
||||||
|
private TokenRepository tokenRepository;
|
||||||
|
private JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
public CustomBasicAuthenticationFilter(AuthenticationManager authenticationManager, TokenRepository tokenRepository, JwtUtil jwtUtil) {
|
||||||
|
super(authenticationManager);
|
||||||
|
this.jwtUtil = jwtUtil;
|
||||||
|
this.tokenRepository = tokenRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||||
|
final String requestTokenHeader = request.getHeader("Authorization");
|
||||||
|
boolean isAnonymous = false;
|
||||||
|
String jwtToken = null;
|
||||||
|
try {
|
||||||
|
jwtToken = requestTokenHeader.substring(7);
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
throw new CustomNoMatchTokenException(e.getMessage());
|
||||||
|
}
|
||||||
|
String domain = "";
|
||||||
|
String ip = "";
|
||||||
|
try {
|
||||||
|
domain = jwtUtil.extractDomain(jwtToken);
|
||||||
|
ip = jwtUtil.extractIp(jwtToken);
|
||||||
|
} catch(ExpiredJwtException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
throw new CustomExpireTokenException(e.getMessage());
|
||||||
|
} catch (DecodingException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
throw new CustomNoMatchTokenException(e.getMessage());
|
||||||
|
} catch(MalformedJwtException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
throw new CustomMalformedJwtException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Token> optionalToken = tokenRepository.findById(TokenId.builder()
|
||||||
|
.domain(domain)
|
||||||
|
.ip(ip)
|
||||||
|
.build());
|
||||||
|
if(optionalToken.isPresent()) {
|
||||||
|
if(jwtUtil.validateToken(jwtToken, optionalToken.get())) {
|
||||||
|
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(optionalToken.get(), null, null);
|
||||||
|
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
||||||
|
securityContext.setAuthentication(usernamePasswordAuthenticationToken);
|
||||||
|
SecurityContextHolder.setContext(securityContext);
|
||||||
|
SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new CustomException(ErrorCode.AUTHENTICATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomBasicAuthenticationFilter(AuthenticationManager authenticationManager) {
|
||||||
|
super(authenticationManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomBasicAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
|
||||||
|
super(authenticationManager, authenticationEntryPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
18
src/main/java/com/kospo/drm/config/filter/LoggingFilter.java
Normal file
18
src/main/java/com/kospo/drm/config/filter/LoggingFilter.java
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package com.kospo.drm.config.filter;
|
||||||
|
|
||||||
|
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||||
|
import ch.qos.logback.core.filter.Filter;
|
||||||
|
import ch.qos.logback.core.spi.FilterReply;
|
||||||
|
|
||||||
|
public class LoggingFilter extends Filter<ILoggingEvent> {
|
||||||
|
@Override
|
||||||
|
public FilterReply decide(ILoggingEvent iLoggingEvent) {
|
||||||
|
if(iLoggingEvent.getMessage().contains("locale")) {
|
||||||
|
return FilterReply.DENY;
|
||||||
|
} else {
|
||||||
|
return FilterReply.ACCEPT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package com.kospo.drm.config.filter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.kospo.drm.config.exception.CustomErrorResponse;
|
||||||
|
import com.kospo.drm.config.exception.ErrorCode;
|
||||||
|
import com.kospo.drm.exception.*;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class SecurityExceptionFilter extends OncePerRequestFilter {
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException {
|
||||||
|
ObjectMapper om = new ObjectMapper();
|
||||||
|
try {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
} catch (CustomExpireTokenException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.EXPIRE_TOKEN.getCode())
|
||||||
|
.body(ErrorCode.EXPIRE_TOKEN.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
} catch (CustomNoAccountException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.NO_ACCOUNT.getCode())
|
||||||
|
.body(ErrorCode.NO_ACCOUNT.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
} catch (CustomNoMatchTokenException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.NO_MATCH_TOKEN.getCode())
|
||||||
|
.body(ErrorCode.NO_MATCH_TOKEN.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
} catch (CustomNoResultException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.NO_RESULT.getCode())
|
||||||
|
.body(ErrorCode.NO_RESULT.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
} catch (CustomNoTokenException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.NO_TOKEN.getCode())
|
||||||
|
.body(ErrorCode.NO_TOKEN.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
} catch (ServletException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.INTERNAL_SERVER_ERROR.getCode())
|
||||||
|
.body(ErrorCode.INTERNAL_SERVER_ERROR.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
} catch (IOException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.INTERNAL_SERVER_ERROR.getCode())
|
||||||
|
.body(ErrorCode.INTERNAL_SERVER_ERROR.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
} catch (CustomMalformedJwtException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.MALFORMED_JWT.getCode())
|
||||||
|
.body(ErrorCode.MALFORMED_JWT.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
} catch (CustomNoSameIpException e) {
|
||||||
|
response.setStatus(500);
|
||||||
|
response.setContentType("application/json;charset=utf-8");
|
||||||
|
response.getWriter().println(
|
||||||
|
om.writeValueAsString(CustomErrorResponse.builder()
|
||||||
|
.code(ErrorCode.NO_SAME_IP.getCode())
|
||||||
|
.body(ErrorCode.NO_SAME_IP.getBody())
|
||||||
|
.errTime(System.currentTimeMillis()).build())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.kospo.drm.config.jasypt;
|
||||||
|
|
||||||
|
import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties;
|
||||||
|
import org.jasypt.encryption.StringEncryptor;
|
||||||
|
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
|
||||||
|
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableEncryptableProperties
|
||||||
|
public class JasyptConfigAES {
|
||||||
|
@Value("${jasypt.encryptor.key}")
|
||||||
|
private String key;
|
||||||
|
|
||||||
|
@Bean("jasyptEncryptorAES")
|
||||||
|
public StringEncryptor stringEncryptor() {
|
||||||
|
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
|
||||||
|
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
|
||||||
|
|
||||||
|
config.setPassword(key); // 암호화키
|
||||||
|
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256"); // 알고리즘
|
||||||
|
config.setKeyObtentionIterations("1000"); // 반복할 해싱 회수
|
||||||
|
config.setPoolSize("1"); // 인스턴스 pool
|
||||||
|
config.setProviderName("SunJCE");
|
||||||
|
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); // salt 생성 클래스
|
||||||
|
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
|
||||||
|
config.setStringOutputType("base64"); //인코딩 방식
|
||||||
|
encryptor.setConfig(config);
|
||||||
|
return encryptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/main/java/com/kospo/drm/config/jwt/JwtRequestFilter.java
Normal file
54
src/main/java/com/kospo/drm/config/jwt/JwtRequestFilter.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package com.kospo.drm.config.jwt;
|
||||||
|
|
||||||
|
import com.kospo.drm.config.exception.ErrorCode;
|
||||||
|
import com.kospo.drm.exception.CustomExpireTokenException;
|
||||||
|
import com.kospo.drm.exception.CustomMalformedJwtException;
|
||||||
|
import com.kospo.drm.exception.CustomNoMatchTokenException;
|
||||||
|
import com.kospo.drm.repository.TokenRepository;
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import io.jsonwebtoken.MalformedJwtException;
|
||||||
|
import io.jsonwebtoken.io.DecodingException;
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JwtRequestFilter extends OncePerRequestFilter {
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
private final TokenRepository tokenRepository;
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
final String requestTokenHeader = request.getHeader("Authorization");
|
||||||
|
boolean isAnonymous = false;
|
||||||
|
String domain = null;
|
||||||
|
String jwtToken = null;
|
||||||
|
if(requestTokenHeader != null && requestTokenHeader.startsWith("Bearer")) {
|
||||||
|
jwtToken = requestTokenHeader.substring(7);
|
||||||
|
try {
|
||||||
|
domain = jwtUtil.extractDomain(jwtToken);
|
||||||
|
} catch(ExpiredJwtException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
throw new CustomExpireTokenException(e.getMessage());
|
||||||
|
} catch (DecodingException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
throw new CustomNoMatchTokenException(e.getMessage());
|
||||||
|
} catch(MalformedJwtException e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
throw new CustomMalformedJwtException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("noToken");
|
||||||
|
isAnonymous = true;
|
||||||
|
}
|
||||||
|
// String ip = jwtUtil.extractIp(jwtToken);
|
||||||
|
// if(!CommonUtils.getClientIp(request).equals(ip)) {
|
||||||
|
// throw new CustomNoSameIpException(ErrorCode.NO_SAME_IP.getBody());
|
||||||
|
// }
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
172
src/main/java/com/kospo/drm/config/jwt/JwtUtil.java
Normal file
172
src/main/java/com/kospo/drm/config/jwt/JwtUtil.java
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
package com.kospo.drm.config.jwt;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kospo.drm.config.exception.ErrorCode;
|
||||||
|
import com.kospo.drm.config.utils.CommonUtils;
|
||||||
|
import com.kospo.drm.dto.res.TokenResponse;
|
||||||
|
import com.kospo.drm.exception.CustomException;
|
||||||
|
import com.kospo.drm.model.Token;
|
||||||
|
import io.jsonwebtoken.*;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class JwtUtil {
|
||||||
|
@Value("${jwt.key}")
|
||||||
|
private String SECRET_KEY;
|
||||||
|
|
||||||
|
/* 토큰 사용자이름을 추출한다. */
|
||||||
|
public String extractDomain(String token) {
|
||||||
|
return extractClaim(token, Claims::getSubject);
|
||||||
|
}
|
||||||
|
/* 토큰 IP을 추출한다. */
|
||||||
|
public String extractIp(String token) {
|
||||||
|
return extractClaim(token, claims -> (String)claims.get("ip"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 만료시간을 추출한다. */
|
||||||
|
public Date extractExpirationDate(String token) {
|
||||||
|
return extractClaim(token, Claims::getExpiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 토큰 정보를 추출한다. */
|
||||||
|
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
|
||||||
|
final Claims claims = extractAllClaims(token);
|
||||||
|
return claimsResolver.apply(claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 모든 정보를 추출한다. */
|
||||||
|
private Claims extractAllClaims(String token) {
|
||||||
|
return Jwts.parserBuilder().setSigningKey(SECRET_KEY).build().parseClaimsJws(token).getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 만료여부 확인. */
|
||||||
|
private Boolean isTokenExpired(String token) {
|
||||||
|
return extractExpirationDate(token).before(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 엑서스 토큰을 생성한다. */
|
||||||
|
public String generateAccessToken(Token token, Date expired) {
|
||||||
|
RequestAttributes reqAttr = RequestContextHolder.getRequestAttributes();
|
||||||
|
ServletRequestAttributes servlReqAttr = (ServletRequestAttributes)reqAttr;
|
||||||
|
HttpServletRequest req = servlReqAttr.getRequest();
|
||||||
|
Map<String, Object> claims = new HashMap<>();
|
||||||
|
claims.put("domain", token.getId().getDomain());
|
||||||
|
claims.put("ip", token.getId().getIp());
|
||||||
|
return createAccessToken(claims, token.getId().getDomain(), expired);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 엑서스 토큰을 생성한다. */
|
||||||
|
private String createAccessToken(Map<String, Object> claims, String subject, Date expired) {
|
||||||
|
return Jwts.builder().setClaims(claims).setSubject(subject)
|
||||||
|
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||||
|
.setExpiration(expired) // 5minutes
|
||||||
|
.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 리플래쉬토큰을 생성한다. */
|
||||||
|
public String generateRefreshToken(Token token, Date expired) {
|
||||||
|
return createRefreshToken(token.getId().getDomain(), expired);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 리플래쉬토큰을 생성한다. */
|
||||||
|
private String createRefreshToken(String subject, Date expired) {
|
||||||
|
return Jwts.builder().setSubject(subject)
|
||||||
|
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||||
|
.setExpiration(expired) // 30minutes
|
||||||
|
.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 토큰 생성(엑서스, 리플레시) 동시 생성. */
|
||||||
|
public TokenResponse generateToken(Token token) {
|
||||||
|
return TokenResponse.builder()
|
||||||
|
.accessToken(generateAccessToken(token, getAccessDate()))
|
||||||
|
.accessTokenExpired(getAccessDate().getTime())
|
||||||
|
// .refreshToken(generateRefreshToken(token, getRefreshDate()))
|
||||||
|
// .refreshTokenExpired(getRefreshDate().getTime())
|
||||||
|
.domain(token.getId().getDomain()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 리플레쉬토큰으로 엑서스토큰을 재발급한다. */
|
||||||
|
public TokenResponse renewalToken(String accessToken, Token token) {
|
||||||
|
if(validateToken(accessToken, token)) {
|
||||||
|
Claims claims = extractAllClaims(accessToken);
|
||||||
|
return TokenResponse.builder()
|
||||||
|
.accessToken(generateAccessToken(token, getAccessDate()))
|
||||||
|
.accessTokenExpired(getAccessDate().getTime())
|
||||||
|
// .refreshToken(accessToken)
|
||||||
|
// .refreshTokenExpired(claims.getExpiration().getTime())
|
||||||
|
.domain(token.getId().getDomain()).build();
|
||||||
|
} else {
|
||||||
|
throw new CustomException(ErrorCode.EXPIRE_TOKEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*토큰 정보로 jws 정보를 맵핑한다..*/
|
||||||
|
private Jws<Claims> parseToken(String token) {
|
||||||
|
return Jwts.parserBuilder().setSigningKey(SECRET_KEY).build().parseClaimsJws(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 토큰 리플레시토큰 만료기간을 연장한다. */
|
||||||
|
public TokenResponse extendRefreshToken(String refreshToken) {
|
||||||
|
Date refreshDate = getRefreshDate();
|
||||||
|
Jws<Claims> jws = parseToken(refreshToken);
|
||||||
|
jws.getBody().setExpiration(refreshDate).getSubject();
|
||||||
|
return TokenResponse.builder()
|
||||||
|
.refreshToken(refreshToken)
|
||||||
|
.refreshTokenExpired(refreshDate.getTime()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 토큰 액세스토큰 만료기간을 연장한다. */
|
||||||
|
public TokenResponse extendAccessToken(String AccessToken) {
|
||||||
|
Date accessDate = getAccessDate();
|
||||||
|
Jws<Claims> jws = parseToken(AccessToken);
|
||||||
|
jws.getBody().setExpiration(accessDate).getSubject();
|
||||||
|
return TokenResponse.builder()
|
||||||
|
.accessToken(AccessToken)
|
||||||
|
.accessTokenExpired(accessDate.getTime()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 토큰 만료 여부 확인. */
|
||||||
|
public Boolean validateToken(String accessToken, Token token) {
|
||||||
|
try {
|
||||||
|
final String domain = extractDomain(accessToken);
|
||||||
|
return (domain.equals(token.getId().getDomain()) && !isTokenExpired(accessToken));
|
||||||
|
} catch(ExpiredJwtException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*리플레시토큰 만료기간 생성 30분*/
|
||||||
|
private Date getRefreshDate() {
|
||||||
|
return new Date(System.currentTimeMillis() + (1000 * 60 * 60 * 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*엑서스토큰 만료기간 생성 5분*/
|
||||||
|
private Date getAccessDate() {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
Date dt = new Date();
|
||||||
|
cal.setTime(dt);
|
||||||
|
cal.add(Calendar.YEAR, 30);
|
||||||
|
return cal.getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.kospo.drm.config.security;
|
||||||
|
|
||||||
|
import org.postgresql.shaded.com.ongres.scram.common.bouncycastle.pbkdf2.SHA256Digest;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.codec.Hex;
|
||||||
|
import org.springframework.security.crypto.password.*;
|
||||||
|
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class PasswordEncoder {
|
||||||
|
|
||||||
|
public String sha256Encoding(String password) {
|
||||||
|
String encPassword = "";
|
||||||
|
MessageDigest digest = null;
|
||||||
|
try {
|
||||||
|
digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8));
|
||||||
|
encPassword = new String(Hex.encode(hash));
|
||||||
|
return encPassword;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean matches(String passwd1, String passwd2) {
|
||||||
|
if(passwd2.equals(sha256Encoding(passwd1))) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
114
src/main/java/com/kospo/drm/config/security/SecurityConfig.java
Normal file
114
src/main/java/com/kospo/drm/config/security/SecurityConfig.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package com.kospo.drm.config.security;
|
||||||
|
|
||||||
|
import com.kospo.drm.config.filter.CustomBasicAuthenticationFilter;
|
||||||
|
import com.kospo.drm.config.filter.SecurityExceptionFilter;
|
||||||
|
import com.kospo.drm.config.jwt.JwtUtil;
|
||||||
|
import com.kospo.drm.repository.TokenRepository;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.session.SessionRegistry;
|
||||||
|
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
|
||||||
|
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SecurityConfig {
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
private final TokenRepository tokenRepository;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain securityFilterChain(HttpSecurity http,
|
||||||
|
TokenRepository tokenRepository) throws Exception {
|
||||||
|
http
|
||||||
|
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry
|
||||||
|
.requestMatchers("/admin*").hasRole("ADMIN")
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
|
.formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer.disable())
|
||||||
|
.addFilterBefore(new CustomBasicAuthenticationFilter(authenticationManager(), tokenRepository, jwtUtil), UsernamePasswordAuthenticationFilter.class)
|
||||||
|
// .addFilterBefore(new SecurityExceptionFilter(), JwtRequestFilter.class)
|
||||||
|
.addFilterBefore(new SecurityExceptionFilter(), CustomBasicAuthenticationFilter.class)
|
||||||
|
.headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.frameOptions(frameOptionsConfig -> frameOptionsConfig.disable()))
|
||||||
|
.sessionManagement(httpSecuritySessionManagementConfigurer ->
|
||||||
|
httpSecuritySessionManagementConfigurer
|
||||||
|
.maximumSessions(1)
|
||||||
|
.sessionRegistry(sessionRegistry())
|
||||||
|
)
|
||||||
|
.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(new CorsConfigurationSource() {
|
||||||
|
@Override
|
||||||
|
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
|
||||||
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
// String origin = request.getHeader("Origin");
|
||||||
|
config.setAllowCredentials(true);
|
||||||
|
config.setAllowedOrigins(List.of("https://dmz.hmsn.ink","http://localhost:3000", "http://hmsn.ink:3000", "http://hmsn.ink:8010", "http://hmsn.ink", "https://svcm.hmsn.ink", "http://182.227.15.92:3000"));
|
||||||
|
config.setAllowedMethods(List.of("POST","GET","DELETE","PUT","OPTIONS"));
|
||||||
|
config.setAllowedHeaders(List.of("*"));
|
||||||
|
config.setExposedHeaders(List.of("*"));
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEncoding (String str) {
|
||||||
|
BCryptPasswordEncoder b = new BCryptPasswordEncoder();
|
||||||
|
return b.encode(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SessionRegistry sessionRegistry() {
|
||||||
|
return new SessionRegistryImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManager() {
|
||||||
|
AuthenticationManager manager = new AuthenticationManager() {
|
||||||
|
@Override
|
||||||
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*특정 url 필터 제외 처리*/
|
||||||
|
@Bean
|
||||||
|
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||||
|
return (web) -> web.ignoring().requestMatchers(
|
||||||
|
"/api/token/generate", "/api/token"
|
||||||
|
// -- Swagger UI v2
|
||||||
|
, "/v2/api-docs/**", "/swagger-resources/**", "/v3/api-docs/**"
|
||||||
|
, "/swagger-ui/**", "/swagger/**", "/api-docs/**"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
|
||||||
|
return new RegisterSessionAuthenticationStrategy(sessionRegistry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.kospo.drm.config.security;
|
||||||
|
|
||||||
|
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
|
||||||
|
|
||||||
|
public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer {
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.kospo.drm.config.swagger;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.Components;
|
||||||
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class SwaggerConfig {
|
||||||
|
private final String JWTSCHEMENAME = "jwtAuth";
|
||||||
|
private SecurityRequirement securityRequirement = new SecurityRequirement().addList(JWTSCHEMENAME);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OpenAPI openAPI() {
|
||||||
|
// return new OpenAPI()
|
||||||
|
// .info(apiInfo())
|
||||||
|
// .components(new Components().addSecuritySchemes("basicScheme", new SecurityScheme()
|
||||||
|
// .type(SecurityScheme.Type.HTTP).scheme("basic")));
|
||||||
|
// }
|
||||||
|
|
||||||
|
return new OpenAPI()
|
||||||
|
.info(apiInfo())
|
||||||
|
.addSecurityItem(securityRequirement)
|
||||||
|
.components(new Components()
|
||||||
|
.addSecuritySchemes(JWTSCHEMENAME, new SecurityScheme()
|
||||||
|
.name(JWTSCHEMENAME)
|
||||||
|
.type(SecurityScheme.Type.HTTP)
|
||||||
|
.scheme("bearer")
|
||||||
|
.bearerFormat("JWT")
|
||||||
|
.name("Authentication"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private Info apiInfo() {
|
||||||
|
return new Info()
|
||||||
|
.title("소액계약거래")
|
||||||
|
.description("소액계약거래 api")
|
||||||
|
.version("1.0.0");
|
||||||
|
}
|
||||||
|
}
|
||||||
144
src/main/java/com/kospo/drm/config/utils/CommonUtils.java
Normal file
144
src/main/java/com/kospo/drm/config/utils/CommonUtils.java
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package com.kospo.drm.config.utils;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
import java.io.*;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class CommonUtils {
|
||||||
|
private static HashMap<String, Set<HashMap<String, Integer>>> listenings;
|
||||||
|
|
||||||
|
public CommonUtils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getClientIp(HttpServletRequest request) {
|
||||||
|
String ip = "";
|
||||||
|
ip = request.getHeader("X-FORWARDED-FOR");
|
||||||
|
if (ip == null) {
|
||||||
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
log.info("Proxy-Client-IP : " + ip);
|
||||||
|
}
|
||||||
|
if (ip == null) {
|
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||||
|
log.info("WL-Proxy-Client-IP : " + ip);
|
||||||
|
}
|
||||||
|
if (ip == null) {
|
||||||
|
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||||
|
log.info("HTTP_CLIENT_IP : " + ip);
|
||||||
|
}
|
||||||
|
if (ip == null) {
|
||||||
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||||
|
log.info("HTTP_X_FORWARDED_FOR : " + ip);
|
||||||
|
}
|
||||||
|
if (ip == null) {
|
||||||
|
ip = request.getRemoteAddr();
|
||||||
|
log.info("getRemoteAddr : "+ip);
|
||||||
|
}
|
||||||
|
log.info("Result : IP Address : "+ip);
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Sort convertSort(String field) {
|
||||||
|
String[] propertyAndDirection = field.split(",");
|
||||||
|
if(propertyAndDirection.length == 1) propertyAndDirection = field.split(" ");
|
||||||
|
String property = propertyAndDirection[0];
|
||||||
|
Sort.Direction direction = Sort.DEFAULT_DIRECTION;
|
||||||
|
if(propertyAndDirection.length > 1) {
|
||||||
|
String directionString = propertyAndDirection[1];
|
||||||
|
direction = Sort.Direction.fromOptionalString(directionString)
|
||||||
|
.orElse(Sort.DEFAULT_DIRECTION);
|
||||||
|
}
|
||||||
|
Sort.Order order = new Sort.Order(direction, property);
|
||||||
|
return Sort.by(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String dateFormat(Date setDate, String pattern) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||||
|
return sdf.format(setDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String dateFormat(String pattern) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||||
|
return sdf.format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date plusDays(Date dt, int day) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(dt);
|
||||||
|
cal.add(Calendar.DAY_OF_MONTH, day);
|
||||||
|
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
System.out.println(sdf.format(cal.getTime()));
|
||||||
|
return cal.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String stringToPlusYear(String str, int year) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
Date dt = null;
|
||||||
|
try {
|
||||||
|
dt = sdf.parse(str);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(dt);
|
||||||
|
cal.add(Calendar.YEAR, year);
|
||||||
|
|
||||||
|
SimpleDateFormat result = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
return result.format(cal.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String stringToPlusDay(String str, String pattern, int day) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat( pattern);
|
||||||
|
Date dt = null;
|
||||||
|
try {
|
||||||
|
dt = sdf.parse(str);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(dt);
|
||||||
|
cal.add(Calendar.DAY_OF_MONTH, day);
|
||||||
|
|
||||||
|
SimpleDateFormat result = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
return result.format(cal.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date stringToDate(String dd) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
|
||||||
|
Date result = null;
|
||||||
|
try {
|
||||||
|
result = sdf.parse(dd);
|
||||||
|
System.out.println(sdf.format(result));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date stringToDate(String dd, String pattern) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||||
|
Date result = null;
|
||||||
|
try {
|
||||||
|
result = sdf.parse(dd);
|
||||||
|
System.out.println(sdf.format(result));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/main/java/com/kospo/drm/config/utils/CryptoUtil.java
Normal file
89
src/main/java/com/kospo/drm/config/utils/CryptoUtil.java
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package com.kospo.drm.config.utils;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Configurable;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.ResourceLoader;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.security.spec.RSAKeyGenParameterSpec;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class CryptoUtil {
|
||||||
|
|
||||||
|
public static String encrypt(String plainText) throws Exception {
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, loadPublicKey());
|
||||||
|
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
|
||||||
|
return Base64.getEncoder().encodeToString(encryptedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decrypt(String encryptText) throws Exception {
|
||||||
|
encryptText = encryptText.replaceAll(" ", "+");
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA");
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, loadPrivateKey());
|
||||||
|
byte[] decryptBytes = cipher.doFinal(Base64.getDecoder().decode(encryptText));
|
||||||
|
return new String(decryptBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PublicKey loadPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
|
||||||
|
ClassPathResource resource = new ClassPathResource("public.key");
|
||||||
|
byte[] keyBytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
|
||||||
|
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
return keyFactory.generatePublic(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PrivateKey loadPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
|
||||||
|
ClassPathResource resource = new ClassPathResource("private.key");
|
||||||
|
byte[] keyBytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
|
||||||
|
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
return keyFactory.generatePrivate(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] readKeyFromFile(String fileName) {
|
||||||
|
File file = new File(fileName);
|
||||||
|
byte[] keyBytes = new byte[(int) file.length()];
|
||||||
|
try(FileInputStream fis = new FileInputStream(file)) {
|
||||||
|
fis.read(keyBytes);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void saveKeyToFile(String fileName, byte[] keyBytes) {
|
||||||
|
File file = new File(fileName);
|
||||||
|
try(FileOutputStream fos = new FileOutputStream(file)) {
|
||||||
|
fos.write(keyBytes);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// System.out.println(encrypt("psn14020"));
|
||||||
|
// KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
|
||||||
|
// keygen.initialize(new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4));
|
||||||
|
// KeyPair pair = keygen.generateKeyPair();
|
||||||
|
//
|
||||||
|
// saveKeyToFile("C:\\Users\\psn14020\\IdeaProjects\\talk\\src\\main\\resources\\private.key", pair.getPrivate().getEncoded());
|
||||||
|
// saveKeyToFile("C:\\Users\\psn14020\\IdeaProjects\\talk\\src\\main\\resources\\public.key", pair.getPublic().getEncoded());
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/main/java/com/kospo/drm/config/utils/FileUtils.java
Normal file
70
src/main/java/com/kospo/drm/config/utils/FileUtils.java
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package com.kospo.drm.config.utils;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
public synchronized boolean fileUpload(String full, String base64) {
|
||||||
|
boolean result = false;
|
||||||
|
File file = new File(full);
|
||||||
|
|
||||||
|
BufferedOutputStream bos = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] data = DatatypeConverter.parseBase64Binary(base64);
|
||||||
|
bos = new BufferedOutputStream(new FileOutputStream(file));
|
||||||
|
bos.write(data);
|
||||||
|
bos.flush();
|
||||||
|
result = true;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
if (bos != null) {
|
||||||
|
try {
|
||||||
|
bos.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean fileDelete(String full) {
|
||||||
|
File file = new File(full);
|
||||||
|
if(!file.exists()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
file.delete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized byte[] fileRead(String full) {
|
||||||
|
File file = new File(full);
|
||||||
|
if(file.exists()) {
|
||||||
|
BufferedImage originalImage = null;
|
||||||
|
FileInputStream fis = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
|
||||||
|
return fis.readAllBytes();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/main/java/com/kospo/drm/config/utils/XmlUtils.java
Normal file
42
src/main/java/com/kospo/drm/config/utils/XmlUtils.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package com.kospo.drm.config.utils;
|
||||||
|
|
||||||
|
import jakarta.xml.bind.JAXBContext;
|
||||||
|
import jakarta.xml.bind.JAXBException;
|
||||||
|
import jakarta.xml.bind.Marshaller;
|
||||||
|
import jakarta.xml.bind.Unmarshaller;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class XmlUtils {
|
||||||
|
|
||||||
|
public Object xmlToModel(String xml , Class cla) throws JAXBException, IOException, NoSuchMethodException {
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes());
|
||||||
|
JAXBContext jaxbContext = JAXBContext.newInstance(cla);
|
||||||
|
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||||
|
|
||||||
|
Object obj = unmarshaller.unmarshal(bais);
|
||||||
|
|
||||||
|
bais.close();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String modelToXml(Object obj) throws JAXBException, IOException {
|
||||||
|
JAXBContext jaxbContext = JAXBContext.newInstance(obj.getClass());
|
||||||
|
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||||
|
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||||
|
marshaller.marshal(obj, baos);
|
||||||
|
|
||||||
|
String list = baos.toString();
|
||||||
|
|
||||||
|
baos.close();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/main/java/com/kospo/drm/controller/DrmController.java
Normal file
89
src/main/java/com/kospo/drm/controller/DrmController.java
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package com.kospo.drm.controller;
|
||||||
|
|
||||||
|
import com.kospo.drm.config.exception.CustomErrorResponse;
|
||||||
|
import com.kospo.drm.dto.req.DrmRequest;
|
||||||
|
import com.kospo.drm.dto.req.TokenRequest;
|
||||||
|
import com.kospo.drm.dto.res.DrmResponse;
|
||||||
|
import com.kospo.drm.dto.res.TokenResponse;
|
||||||
|
import com.kospo.drm.service.DrmService;
|
||||||
|
import com.kospo.drm.service.TokenService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Tag(name = "Drm Api", description = "drm 관리")
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/api")
|
||||||
|
public class DrmController {
|
||||||
|
private final TokenService tokenService;
|
||||||
|
private final DrmService drmService;
|
||||||
|
|
||||||
|
@Operation(summary = "복호화", description = "문서파일 복호화")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(description = "Success", responseCode = "200",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(oneOf =
|
||||||
|
{DrmResponse.class }))),
|
||||||
|
@ApiResponse(description = "Not found", responseCode = "404",
|
||||||
|
content = @Content(mediaType = "text/plain", schema = @Schema(oneOf =
|
||||||
|
{String.class}))),
|
||||||
|
@ApiResponse(description = "Internal Error", responseCode = "500",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(oneOf =
|
||||||
|
{CustomErrorResponse.class })))
|
||||||
|
})
|
||||||
|
@PostMapping("/drm/decrypt")
|
||||||
|
public ResponseEntity decrypt(
|
||||||
|
HttpServletRequest request,
|
||||||
|
@RequestBody DrmRequest drmRequest
|
||||||
|
) {
|
||||||
|
|
||||||
|
return ResponseEntity.ok(drmService.decrypt(drmRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "도메인 등록", description = "토큰을 생성한다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(description = "Success", responseCode = "200",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(oneOf =
|
||||||
|
{TokenResponse.class }))),
|
||||||
|
@ApiResponse(description = "Not found", responseCode = "404",
|
||||||
|
content = @Content(mediaType = "text/plain", schema = @Schema(oneOf =
|
||||||
|
{String.class}))),
|
||||||
|
@ApiResponse(description = "Internal Error", responseCode = "500",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(oneOf =
|
||||||
|
{CustomErrorResponse.class })))
|
||||||
|
})
|
||||||
|
@PostMapping("/token/generate")
|
||||||
|
public ResponseEntity generate(
|
||||||
|
HttpServletRequest request,
|
||||||
|
@RequestBody TokenRequest tokenRequest
|
||||||
|
) {
|
||||||
|
return ResponseEntity.ok(tokenService.generateToken(tokenRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "토큰 조회", description = "도메인 아이피로 토큰 조회")
|
||||||
|
@ApiResponses({
|
||||||
|
@ApiResponse(description = "Success", responseCode = "200",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(oneOf =
|
||||||
|
{TokenResponse.class }))),
|
||||||
|
@ApiResponse(description = "Not found", responseCode = "404",
|
||||||
|
content = @Content(mediaType = "text/plain", schema = @Schema(oneOf =
|
||||||
|
{String.class}))),
|
||||||
|
@ApiResponse(description = "Internal Error", responseCode = "500",
|
||||||
|
content = @Content(mediaType = "application/json", schema = @Schema(oneOf =
|
||||||
|
{CustomErrorResponse.class })))
|
||||||
|
})
|
||||||
|
@PostMapping("/token")
|
||||||
|
public ResponseEntity search(
|
||||||
|
HttpServletRequest request,
|
||||||
|
@RequestBody TokenRequest tokenRequest
|
||||||
|
) {
|
||||||
|
return ResponseEntity.ok(tokenService.generateToken(tokenRequest));
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/main/java/com/kospo/drm/dto/req/DrmRequest.java
Normal file
30
src/main/java/com/kospo/drm/dto/req/DrmRequest.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package com.kospo.drm.dto.req;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "암호화 목록")
|
||||||
|
public class DrmRequest {
|
||||||
|
@Schema(description = "첨부파일명")
|
||||||
|
private List<EncryptFile> encryptFiles;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "암호화 파일")
|
||||||
|
public static class EncryptFile {
|
||||||
|
@Schema(description = "첨부파일명")
|
||||||
|
private String fileName;
|
||||||
|
@Schema(description = "첨부파일(byte)")
|
||||||
|
private String data;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/com/kospo/drm/dto/req/TokenRequest.java
Normal file
17
src/main/java/com/kospo/drm/dto/req/TokenRequest.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.kospo.drm.dto.req;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "토큰 생성")
|
||||||
|
public class TokenRequest {
|
||||||
|
@Schema(description = "도메인")
|
||||||
|
private String domain;
|
||||||
|
@Schema(description = "아이피")
|
||||||
|
private String ip;
|
||||||
|
}
|
||||||
31
src/main/java/com/kospo/drm/dto/res/DrmResponse.java
Normal file
31
src/main/java/com/kospo/drm/dto/res/DrmResponse.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package com.kospo.drm.dto.res;
|
||||||
|
|
||||||
|
import com.kospo.drm.dto.req.DrmRequest;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "복호화 목록")
|
||||||
|
public class DrmResponse {
|
||||||
|
@Schema(description = "첨부파일명")
|
||||||
|
private List<DrmResponse.DecryptFile> decryptFiles;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Schema(description = "복호화 파일")
|
||||||
|
public static class DecryptFile {
|
||||||
|
@Schema(description = "첨부파일명")
|
||||||
|
private String fileName;
|
||||||
|
@Schema(description = "첨부파일(byte)")
|
||||||
|
private String data;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/main/java/com/kospo/drm/dto/res/TokenResponse.java
Normal file
40
src/main/java/com/kospo/drm/dto/res/TokenResponse.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package com.kospo.drm.dto.res;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.kospo.drm.model.Token;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@Schema(name = "TokenResponse", description = "토큰")
|
||||||
|
public class TokenResponse implements Serializable {
|
||||||
|
@Schema(name = "domain", description = "도메인주소")
|
||||||
|
private String domain;
|
||||||
|
@Schema(name = "accessToken", description = "엑서스 토큰")
|
||||||
|
private String accessToken;
|
||||||
|
@Schema(name = "accessTokenExpired", description = "엑서스 토큰 만료시간")
|
||||||
|
private Long accessTokenExpired;
|
||||||
|
@Schema(name = "refreshToken", description = "리플레시 토큰")
|
||||||
|
private String refreshToken;
|
||||||
|
@Schema(name = "refreshTokenExpired", description = "리플레시 토큰 만료시간")
|
||||||
|
private Long refreshTokenExpired;
|
||||||
|
|
||||||
|
public static TokenResponse from(Token token) {
|
||||||
|
return TokenResponse.builder()
|
||||||
|
.accessToken(token.getToken())
|
||||||
|
.domain(token.getId().getDomain())
|
||||||
|
.accessTokenExpired(token.getTokenExpire())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/main/java/com/kospo/drm/exception/CustomException.java
Normal file
14
src/main/java/com/kospo/drm/exception/CustomException.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kospo.drm.config.exception.ErrorCode;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class CustomException extends RuntimeException{
|
||||||
|
private ErrorCode errorCode;
|
||||||
|
|
||||||
|
public CustomException(ErrorCode errorCode) {
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
public class CustomExpireTokenException extends RuntimeException{
|
||||||
|
public CustomExpireTokenException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
public class CustomMalformedJwtException extends RuntimeException{
|
||||||
|
public CustomMalformedJwtException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kospo.drm.config.exception.ErrorCode;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class CustomMessageException extends RuntimeException{
|
||||||
|
private CusMessage cusMessage;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public static class CusMessage {
|
||||||
|
String code;
|
||||||
|
String message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public CustomMessageException(CusMessage cusMessage) {
|
||||||
|
this.cusMessage = cusMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
public class CustomNoAccountException extends RuntimeException{
|
||||||
|
public CustomNoAccountException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
public class CustomNoMatchTokenException extends RuntimeException{
|
||||||
|
public CustomNoMatchTokenException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
public class CustomNoResultException extends RuntimeException{
|
||||||
|
public CustomNoResultException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
public class CustomNoSameIpException extends RuntimeException{
|
||||||
|
public CustomNoSameIpException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.kospo.drm.exception;
|
||||||
|
|
||||||
|
public class CustomNoTokenException extends RuntimeException{
|
||||||
|
public CustomNoTokenException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/main/java/com/kospo/drm/model/Token.java
Normal file
33
src/main/java/com/kospo/drm/model/Token.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package com.kospo.drm.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.EmbeddedId;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@Entity
|
||||||
|
@Table(name = "token")
|
||||||
|
public class Token {
|
||||||
|
@EmbeddedId
|
||||||
|
private TokenId id;
|
||||||
|
|
||||||
|
@Size(max = 500)
|
||||||
|
@Column(name = "token", length = 500)
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
@Column(name = "token_expire")
|
||||||
|
private long tokenExpire;
|
||||||
|
|
||||||
|
@Column(name = "ins_date")
|
||||||
|
private Timestamp insDate;
|
||||||
|
|
||||||
|
}
|
||||||
45
src/main/java/com/kospo/drm/model/TokenId.java
Normal file
45
src/main/java/com/kospo/drm/model/TokenId.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package com.kospo.drm.model;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Embeddable;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.*;
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Embeddable
|
||||||
|
public class TokenId implements Serializable {
|
||||||
|
private static final long serialVersionUID = -5489321559837728499L;
|
||||||
|
@Size(max = 200)
|
||||||
|
@NotNull
|
||||||
|
@Column(name = "domain", nullable = false, length = 200)
|
||||||
|
private String domain;
|
||||||
|
|
||||||
|
@Size(max = 20)
|
||||||
|
@NotNull
|
||||||
|
@Column(name = "ip", nullable = false, length = 20)
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
|
||||||
|
TokenId entity = (TokenId) o;
|
||||||
|
return Objects.equals(this.domain, entity.domain) &&
|
||||||
|
Objects.equals(this.ip, entity.ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(domain, ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.kospo.drm.repository;
|
||||||
|
|
||||||
|
import com.kospo.drm.model.Token;
|
||||||
|
import com.kospo.drm.model.TokenId;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
public interface TokenRepository extends JpaRepository<Token, TokenId> {
|
||||||
|
}
|
||||||
8
src/main/java/com/kospo/drm/service/DrmService.java
Normal file
8
src/main/java/com/kospo/drm/service/DrmService.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package com.kospo.drm.service;
|
||||||
|
|
||||||
|
import com.kospo.drm.dto.req.DrmRequest;
|
||||||
|
import com.kospo.drm.dto.res.DrmResponse;
|
||||||
|
|
||||||
|
public interface DrmService {
|
||||||
|
DrmResponse decrypt(DrmRequest drmRequest);
|
||||||
|
}
|
||||||
8
src/main/java/com/kospo/drm/service/TokenService.java
Normal file
8
src/main/java/com/kospo/drm/service/TokenService.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package com.kospo.drm.service;
|
||||||
|
|
||||||
|
import com.kospo.drm.dto.req.TokenRequest;
|
||||||
|
import com.kospo.drm.dto.res.TokenResponse;
|
||||||
|
|
||||||
|
public interface TokenService {
|
||||||
|
TokenResponse generateToken(TokenRequest tokenRequest);
|
||||||
|
}
|
||||||
56
src/main/java/com/kospo/drm/service/impl/DrmServiceImpl.java
Normal file
56
src/main/java/com/kospo/drm/service/impl/DrmServiceImpl.java
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package com.kospo.drm.service.impl;
|
||||||
|
|
||||||
|
import com.kospo.drm.dto.req.DrmRequest;
|
||||||
|
import com.kospo.drm.dto.res.DrmResponse;
|
||||||
|
import com.kospo.drm.service.DrmService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DrmServiceImpl implements DrmService {
|
||||||
|
@Value("${server.drm.attach}")
|
||||||
|
private String drmAttach;
|
||||||
|
@Override
|
||||||
|
public DrmResponse decrypt(DrmRequest drmRequest) {
|
||||||
|
List<DrmResponse.DecryptFile> decryptFiles = drmRequest.getEncryptFiles().stream().map(en -> {
|
||||||
|
String fileName = en.getFileName();
|
||||||
|
String data = en.getData();
|
||||||
|
String[] reData = data.split(",");
|
||||||
|
byte[] decodedFile = Base64.getDecoder()
|
||||||
|
.decode(reData[1].getBytes(StandardCharsets.UTF_8));
|
||||||
|
String psyFileName = UUID.randomUUID().toString();
|
||||||
|
Path encryptFile = Paths.get(drmAttach + "/enc", psyFileName);
|
||||||
|
Path decryptFile = Paths.get(drmAttach + "/dec", psyFileName);
|
||||||
|
String resultData = "";
|
||||||
|
try {
|
||||||
|
Files.write(encryptFile, decodedFile);
|
||||||
|
|
||||||
|
/*복호화 로직 시작*/
|
||||||
|
|
||||||
|
/*복호화 로직 종료*/
|
||||||
|
Files.copy(encryptFile, decryptFile);
|
||||||
|
|
||||||
|
resultData = Base64.getEncoder().encodeToString(Files.readAllBytes(decryptFile));
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DrmResponse.DecryptFile.builder().fileName(fileName).data(resultData).build();
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
return DrmResponse.builder().decryptFiles(decryptFiles).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.kospo.drm.service.impl;
|
||||||
|
|
||||||
|
import com.kospo.drm.config.exception.ErrorCode;
|
||||||
|
import com.kospo.drm.config.jwt.JwtUtil;
|
||||||
|
import com.kospo.drm.dto.req.TokenRequest;
|
||||||
|
import com.kospo.drm.dto.res.TokenResponse;
|
||||||
|
import com.kospo.drm.exception.CustomException;
|
||||||
|
import com.kospo.drm.model.Token;
|
||||||
|
import com.kospo.drm.model.TokenId;
|
||||||
|
import com.kospo.drm.repository.TokenRepository;
|
||||||
|
import com.kospo.drm.service.TokenService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class TokenServiceImpl implements TokenService {
|
||||||
|
private final TokenRepository tokenRepository;
|
||||||
|
private final JwtUtil jwtUtil;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TokenResponse generateToken(TokenRequest tokenRequest) {
|
||||||
|
Token token = Token.builder().id(
|
||||||
|
TokenId.builder()
|
||||||
|
.domain(tokenRequest.getDomain())
|
||||||
|
.ip(tokenRequest.getIp())
|
||||||
|
.build()
|
||||||
|
).build()
|
||||||
|
;
|
||||||
|
TokenResponse tokenResponse = jwtUtil.generateToken(token);
|
||||||
|
token.setToken(tokenResponse.getAccessToken());
|
||||||
|
token.setInsDate(new Timestamp(new Date().getTime()));
|
||||||
|
|
||||||
|
|
||||||
|
tokenRepository.save(token);
|
||||||
|
return tokenResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenResponse findToken(TokenRequest tokenRequest) {
|
||||||
|
Optional<Token> optionalToken = tokenRepository.findById(TokenId.builder()
|
||||||
|
.domain(tokenRequest.getDomain())
|
||||||
|
.ip(tokenRequest.getIp())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
if(optionalToken.isEmpty()) throw new CustomException(ErrorCode.AUTHENTICATION);
|
||||||
|
|
||||||
|
return TokenResponse.from(optionalToken.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/main/java/javax/servlet/http/HttpServletRequest.java
Normal file
5
src/main/java/javax/servlet/http/HttpServletRequest.java
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package javax.servlet.http;
|
||||||
|
|
||||||
|
public interface HttpServletRequest extends jakarta.servlet.http.HttpServletRequest {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package javax.servlet.http;
|
||||||
|
|
||||||
|
public interface HttpServletResponse extends jakarta.servlet.http.HttpServletResponse {
|
||||||
|
|
||||||
|
}
|
||||||
4
src/main/java/javax/servlet/http/HttpSession.java
Normal file
4
src/main/java/javax/servlet/http/HttpSession.java
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package javax.servlet.http;
|
||||||
|
|
||||||
|
public interface HttpSession extends jakarta.servlet.http.HttpSession {
|
||||||
|
}
|
||||||
135
src/main/java/javax/servlet/http/HttpUtils.java
Normal file
135
src/main/java/javax/servlet/http/HttpUtils.java
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package javax.servlet.http;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import jakarta.servlet.ServletInputStream;
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
|
public class HttpUtils {
|
||||||
|
private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
|
||||||
|
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
|
||||||
|
|
||||||
|
public HttpUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Hashtable<String, String[]> parseQueryString(String s) {
|
||||||
|
String[] valArray = null;
|
||||||
|
if (s == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
} else {
|
||||||
|
Hashtable<String, String[]> ht = new Hashtable();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
String key;
|
||||||
|
for(StringTokenizer st = new StringTokenizer(s, "&"); st.hasMoreTokens(); ht.put(key, valArray)) {
|
||||||
|
String pair = st.nextToken();
|
||||||
|
int pos = pair.indexOf(61);
|
||||||
|
if (pos == -1) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
key = parseName(pair.substring(0, pos), sb);
|
||||||
|
String val = parseName(pair.substring(pos + 1, pair.length()), sb);
|
||||||
|
if (!ht.containsKey(key)) {
|
||||||
|
valArray = new String[]{val};
|
||||||
|
} else {
|
||||||
|
String[] oldVals = (String[])ht.get(key);
|
||||||
|
valArray = new String[oldVals.length + 1];
|
||||||
|
|
||||||
|
for(int i = 0; i < oldVals.length; ++i) {
|
||||||
|
valArray[i] = oldVals[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
valArray[oldVals.length] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Hashtable<String, String[]> parsePostData(int len, ServletInputStream in) {
|
||||||
|
if (len <= 0) {
|
||||||
|
return new Hashtable();
|
||||||
|
} else if (in == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
} else {
|
||||||
|
byte[] postedBytes = new byte[len];
|
||||||
|
|
||||||
|
try {
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
int inputLen = in.read(postedBytes, offset, len - offset);
|
||||||
|
if (inputLen <= 0) {
|
||||||
|
String msg = lStrings.getString("err.io.short_read");
|
||||||
|
throw new IllegalArgumentException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += inputLen;
|
||||||
|
} while(len - offset > 0);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String postedBody = new String(postedBytes, 0, len, "8859_1");
|
||||||
|
return parseQueryString(postedBody);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new IllegalArgumentException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseName(String s, StringBuilder sb) {
|
||||||
|
sb.setLength(0);
|
||||||
|
|
||||||
|
for(int i = 0; i < s.length(); ++i) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
switch (c) {
|
||||||
|
case '%':
|
||||||
|
try {
|
||||||
|
sb.append((char)Integer.parseInt(s.substring(i + 1, i + 3), 16));
|
||||||
|
i += 2;
|
||||||
|
} catch (NumberFormatException var6) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
} catch (StringIndexOutOfBoundsException var7) {
|
||||||
|
String rest = s.substring(i);
|
||||||
|
sb.append(rest);
|
||||||
|
if (rest.length() == 2) {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
sb.append(' ');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringBuffer getRequestURL(HttpServletRequest req) {
|
||||||
|
StringBuffer url = new StringBuffer();
|
||||||
|
String scheme = req.getScheme();
|
||||||
|
int port = req.getServerPort();
|
||||||
|
String urlPath = req.getRequestURI();
|
||||||
|
url.append(scheme);
|
||||||
|
url.append("://");
|
||||||
|
url.append(req.getServerName());
|
||||||
|
if (scheme.equals("http") && port != 80 || scheme.equals("https") && port != 443) {
|
||||||
|
url.append(':');
|
||||||
|
url.append(req.getServerPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
url.append(urlPath);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/main/java/javax/servlet/http/ServletException.java
Normal file
4
src/main/java/javax/servlet/http/ServletException.java
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
package javax.servlet.http;
|
||||||
|
|
||||||
|
public class ServletException extends jakarta.servlet.ServletException {
|
||||||
|
}
|
||||||
27
src/main/java/jwtSample.java
Normal file
27
src/main/java/jwtSample.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import org.jasypt.encryption.StringEncryptor;
|
||||||
|
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
|
||||||
|
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
|
||||||
|
|
||||||
|
public class jwtSample {
|
||||||
|
private static final String SECRET_KEY = "5840f916c19111ee86fcf38ac250f21377907a8ac19111eebab6e7179f8e5c87";
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(stringEncryptor().decrypt("gLQTvX57MBGvHAhEGckIJbVdgG5w1YPO+bZk5+xOvg6DhAIcGcyXib8T605t2Icd"));
|
||||||
|
System.out.println(stringEncryptor().decrypt("KpBRMJPNmBe/zi0mPo32beSSXteAaSEp/Kf7dVV3ss8Qi4mS2bJ+P0eFw2Qs15sV"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringEncryptor stringEncryptor() {
|
||||||
|
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
|
||||||
|
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
|
||||||
|
|
||||||
|
config.setPassword("kospo2025"); // 암호화키
|
||||||
|
config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256"); // 알고리즘
|
||||||
|
config.setKeyObtentionIterations("1000"); // 반복할 해싱 회수
|
||||||
|
config.setPoolSize("1"); // 인스턴스 pool
|
||||||
|
config.setProviderName("SunJCE");
|
||||||
|
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); // salt 생성 클래스
|
||||||
|
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
|
||||||
|
config.setStringOutputType("base64"); //인코딩 방식
|
||||||
|
encryptor.setConfig(config);
|
||||||
|
return encryptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
79
src/main/resources/application-dev.yml
Normal file
79
src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
jasypt:
|
||||||
|
encryptor:
|
||||||
|
key: kospo2025
|
||||||
|
bean: jasyptEncryptorAES
|
||||||
|
property:
|
||||||
|
prefix: ENC(
|
||||||
|
suffix: )
|
||||||
|
server:
|
||||||
|
address: 0.0.0.0
|
||||||
|
port: 8010
|
||||||
|
servlet:
|
||||||
|
encoding:
|
||||||
|
charset: UTF-8
|
||||||
|
enabled: true
|
||||||
|
force: true
|
||||||
|
session:
|
||||||
|
timeout: 1800s
|
||||||
|
drm:
|
||||||
|
attach: '/appl/drm/attach/'
|
||||||
|
jetty:
|
||||||
|
max-http-form-post-size: 100MB
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: drm
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 100MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
data:
|
||||||
|
rest:
|
||||||
|
detection-strategy: annotated
|
||||||
|
devtools:
|
||||||
|
livereload:
|
||||||
|
enabled: true
|
||||||
|
remote:
|
||||||
|
restart:
|
||||||
|
enabled: false
|
||||||
|
jpa:
|
||||||
|
show-sql: false
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: validate
|
||||||
|
naming:
|
||||||
|
physical-strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
|
||||||
|
open-in-view: false
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ant_path_matcher
|
||||||
|
datasource:
|
||||||
|
hikari:
|
||||||
|
username: ENC(gLQTvX57MBGvHAhEGckIJbVdgG5w1YPO+bZk5+xOvg6DhAIcGcyXib8T605t2Icd)
|
||||||
|
password: ENC(KpBRMJPNmBe/zi0mPo32beSSXteAaSEp/Kf7dVV3ss8Qi4mS2bJ+P0eFw2Qs15sV)
|
||||||
|
connection-timeout: 20000
|
||||||
|
maximum-pool-size: 30
|
||||||
|
data-source-properties:
|
||||||
|
cachePrepStmts: true
|
||||||
|
prepStmtCacheSize: 200
|
||||||
|
prepStmtCacheSqlLimit: 2048
|
||||||
|
useServerPrepStmts: true
|
||||||
|
url: jdbc:log4jdbc:postgresql://hmsn.ink:35432/svcm
|
||||||
|
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||||
|
jwt:
|
||||||
|
key: 5840f916c19111ee86fcf38ac250f21377907a8ac19111eebab6e7179f8e5c87
|
||||||
|
springdoc:
|
||||||
|
packages-to-scan: com.kospo.drm
|
||||||
|
api-docs:
|
||||||
|
path: /api-docs
|
||||||
|
groups:
|
||||||
|
enabled: true
|
||||||
|
swagger-ui:
|
||||||
|
path: /swagger-ui.html
|
||||||
|
enabled: true
|
||||||
|
groups-order: ASC
|
||||||
|
tags-sorter: alpha
|
||||||
|
operations-sorter: alpha
|
||||||
|
display-request-duration: true
|
||||||
|
doc-expansion: none
|
||||||
|
cache:
|
||||||
|
disabled: true
|
||||||
|
model-and-view-allowed: true
|
||||||
79
src/main/resources/application-local.yml
Normal file
79
src/main/resources/application-local.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
jasypt:
|
||||||
|
encryptor:
|
||||||
|
key: kospo2025
|
||||||
|
bean: jasyptEncryptorAES
|
||||||
|
property:
|
||||||
|
prefix: ENC(
|
||||||
|
suffix: )
|
||||||
|
server:
|
||||||
|
address: 0.0.0.0
|
||||||
|
port: 8010
|
||||||
|
servlet:
|
||||||
|
encoding:
|
||||||
|
charset: UTF-8
|
||||||
|
enabled: true
|
||||||
|
force: true
|
||||||
|
session:
|
||||||
|
timeout: 1800s
|
||||||
|
drm:
|
||||||
|
attach: 'C:/appl/drm/attach/'
|
||||||
|
jetty:
|
||||||
|
max-http-form-post-size: 100MB
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: drm
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 100MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
data:
|
||||||
|
rest:
|
||||||
|
detection-strategy: annotated
|
||||||
|
devtools:
|
||||||
|
livereload:
|
||||||
|
enabled: true
|
||||||
|
remote:
|
||||||
|
restart:
|
||||||
|
enabled: false
|
||||||
|
jpa:
|
||||||
|
show-sql: false
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: validate
|
||||||
|
naming:
|
||||||
|
physical-strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
|
||||||
|
open-in-view: false
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ant_path_matcher
|
||||||
|
datasource:
|
||||||
|
hikari:
|
||||||
|
username: ENC(gLQTvX57MBGvHAhEGckIJbVdgG5w1YPO+bZk5+xOvg6DhAIcGcyXib8T605t2Icd)
|
||||||
|
password: ENC(KpBRMJPNmBe/zi0mPo32beSSXteAaSEp/Kf7dVV3ss8Qi4mS2bJ+P0eFw2Qs15sV)
|
||||||
|
connection-timeout: 20000
|
||||||
|
maximum-pool-size: 30
|
||||||
|
data-source-properties:
|
||||||
|
cachePrepStmts: true
|
||||||
|
prepStmtCacheSize: 200
|
||||||
|
prepStmtCacheSqlLimit: 2048
|
||||||
|
useServerPrepStmts: true
|
||||||
|
url: jdbc:log4jdbc:postgresql://hmsn.ink:35432/svcm
|
||||||
|
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||||
|
jwt:
|
||||||
|
key: 5840f916c19111ee86fcf38ac250f21377907a8ac19111eebab6e7179f8e5c87
|
||||||
|
springdoc:
|
||||||
|
packages-to-scan: com.kospo.drm
|
||||||
|
api-docs:
|
||||||
|
path: /api-docs
|
||||||
|
groups:
|
||||||
|
enabled: true
|
||||||
|
swagger-ui:
|
||||||
|
path: /swagger-ui.html
|
||||||
|
enabled: true
|
||||||
|
groups-order: ASC
|
||||||
|
tags-sorter: alpha
|
||||||
|
operations-sorter: alpha
|
||||||
|
display-request-duration: true
|
||||||
|
doc-expansion: none
|
||||||
|
cache:
|
||||||
|
disabled: true
|
||||||
|
model-and-view-allowed: true
|
||||||
79
src/main/resources/application-prod.yml
Normal file
79
src/main/resources/application-prod.yml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
jasypt:
|
||||||
|
encryptor:
|
||||||
|
key: kospo2025
|
||||||
|
bean: jasyptEncryptorAES
|
||||||
|
property:
|
||||||
|
prefix: ENC(
|
||||||
|
suffix: )
|
||||||
|
server:
|
||||||
|
address: 0.0.0.0
|
||||||
|
port: 8010
|
||||||
|
servlet:
|
||||||
|
encoding:
|
||||||
|
charset: UTF-8
|
||||||
|
enabled: true
|
||||||
|
force: true
|
||||||
|
session:
|
||||||
|
timeout: 1800s
|
||||||
|
drm:
|
||||||
|
attach: '/appl/drm/attach/'
|
||||||
|
jetty:
|
||||||
|
max-http-form-post-size: 100MB
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: drm
|
||||||
|
servlet:
|
||||||
|
multipart:
|
||||||
|
max-file-size: 100MB
|
||||||
|
max-request-size: 100MB
|
||||||
|
data:
|
||||||
|
rest:
|
||||||
|
detection-strategy: annotated
|
||||||
|
devtools:
|
||||||
|
livereload:
|
||||||
|
enabled: true
|
||||||
|
remote:
|
||||||
|
restart:
|
||||||
|
enabled: false
|
||||||
|
jpa:
|
||||||
|
show-sql: false
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: validate
|
||||||
|
naming:
|
||||||
|
physical-strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
|
||||||
|
open-in-view: false
|
||||||
|
mvc:
|
||||||
|
pathmatch:
|
||||||
|
matching-strategy: ant_path_matcher
|
||||||
|
datasource:
|
||||||
|
hikari:
|
||||||
|
username: ENC(gLQTvX57MBGvHAhEGckIJbVdgG5w1YPO+bZk5+xOvg6DhAIcGcyXib8T605t2Icd)
|
||||||
|
password: ENC(KpBRMJPNmBe/zi0mPo32beSSXteAaSEp/Kf7dVV3ss8Qi4mS2bJ+P0eFw2Qs15sV)
|
||||||
|
connection-timeout: 20000
|
||||||
|
maximum-pool-size: 30
|
||||||
|
data-source-properties:
|
||||||
|
cachePrepStmts: true
|
||||||
|
prepStmtCacheSize: 200
|
||||||
|
prepStmtCacheSqlLimit: 2048
|
||||||
|
useServerPrepStmts: true
|
||||||
|
url: jdbc:log4jdbc:postgresql://hmsn.ink:35432/svcm
|
||||||
|
driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
|
||||||
|
jwt:
|
||||||
|
key: 5840f916c19111ee86fcf38ac250f21377907a8ac19111eebab6e7179f8e5c87
|
||||||
|
springdoc:
|
||||||
|
packages-to-scan: com.kospo.drm
|
||||||
|
api-docs:
|
||||||
|
path: /api-docs
|
||||||
|
groups:
|
||||||
|
enabled: true
|
||||||
|
swagger-ui:
|
||||||
|
path: /swagger-ui.html
|
||||||
|
enabled: true
|
||||||
|
groups-order: ASC
|
||||||
|
tags-sorter: alpha
|
||||||
|
operations-sorter: alpha
|
||||||
|
display-request-duration: true
|
||||||
|
doc-expansion: none
|
||||||
|
cache:
|
||||||
|
disabled: true
|
||||||
|
model-and-view-allowed: true
|
||||||
3
src/main/resources/application.yml
Normal file
3
src/main/resources/application.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: local
|
||||||
4
src/main/resources/logback-dev.properties
Normal file
4
src/main/resources/logback-dev.properties
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
log.config.path=./logs/
|
||||||
|
log.config.filename=api_log
|
||||||
|
log.level=debug
|
||||||
|
|
||||||
4
src/main/resources/logback-prod.properties
Normal file
4
src/main/resources/logback-prod.properties
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
log.config.path=./logs/
|
||||||
|
log.config.filename=api_log
|
||||||
|
log.level=warn
|
||||||
|
|
||||||
144
src/main/resources/logback-spring.xml
Normal file
144
src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- 60초마다 설정 파일의 변경을 확인 하여 변경시 갱신 -->
|
||||||
|
<configuration scan="true" scanPeriod="60 seconds">
|
||||||
|
<!--springProfile 태그를 사용하면 logback 설정파일에서 복수개의 프로파일을 설정할 수 있다.-->
|
||||||
|
<springProfile name="prod">
|
||||||
|
<property resource="logback-prod.properties"/>
|
||||||
|
</springProfile>
|
||||||
|
<springProfile name="dev">
|
||||||
|
<property resource="logback-dev.properties"/>
|
||||||
|
</springProfile>
|
||||||
|
<!--Environment 내의 프로퍼티들을 개별적으로 설정할 수도 있다.-->
|
||||||
|
<!-- <springProperty scope="context" name="LOG_LEVEL" source="logging.level.root"/> -->
|
||||||
|
|
||||||
|
<!-- log level -->
|
||||||
|
<property name="LOG_LEVEL" value="${log.level}"/>
|
||||||
|
<!-- log file path -->
|
||||||
|
<property name="LOG_PATH" value="${log.config.path}"/>
|
||||||
|
<!-- log file name -->
|
||||||
|
<property name="LOG_FILE_NAME" value="${log.config.filename}"/>
|
||||||
|
<!-- err log file name -->
|
||||||
|
<property name="ERR_LOG_FILE_NAME" value="err_log"/>
|
||||||
|
<!-- pattern -->
|
||||||
|
<property name="LOG_PATTERN" value="%-5level %d{yy-MM-dd HH:mm:ss}[%thread] [%logger{0}:%line] - %msg%n"/>
|
||||||
|
|
||||||
|
<!-- Console Appender -->
|
||||||
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<filter class="com.kospo.drm.config.filter.LoggingFilter"/>
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${LOG_PATTERN}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- File Appender -->
|
||||||
|
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<filter class="com.kospo.drm.config.filter.LoggingFilter"/>
|
||||||
|
<!-- 파일경로 설정 -->
|
||||||
|
<file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>
|
||||||
|
|
||||||
|
<!-- 출력패턴 설정-->
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${LOG_PATTERN}</pattern>
|
||||||
|
</encoder>
|
||||||
|
|
||||||
|
<!-- Rolling 정책 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
|
||||||
|
<fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
|
||||||
|
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||||
|
<!-- 파일당 최고 용량 kb, mb, gb -->
|
||||||
|
<maxFileSize>10MB</maxFileSize>
|
||||||
|
</timeBasedFileNamingAndTriggeringPolicy>
|
||||||
|
<!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
|
||||||
|
<maxHistory>30</maxHistory>
|
||||||
|
<!--<MinIndex>1</MinIndex>
|
||||||
|
<MaxIndex>10</MaxIndex>-->
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- 에러의 경우 파일에 로그 처리 -->
|
||||||
|
<appender name="Error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||||
|
<level>error</level>
|
||||||
|
<onMatch>ACCEPT</onMatch>
|
||||||
|
<onMismatch>DENY</onMismatch>
|
||||||
|
</filter>
|
||||||
|
<file>${LOG_PATH}/${ERR_LOG_FILE_NAME}.log</file>
|
||||||
|
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||||
|
<pattern>${LOG_PATTERN}</pattern>
|
||||||
|
</encoder>
|
||||||
|
<!-- Rolling 정책 -->
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
|
<!-- .gz,.zip 등을 넣으면 자동 일자별 로그파일 압축 -->
|
||||||
|
<fileNamePattern>${LOG_PATH}/${ERR_LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log</fileNamePattern>
|
||||||
|
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||||
|
<!-- 파일당 최고 용량 kb, mb, gb -->
|
||||||
|
<maxFileSize>10MB</maxFileSize>
|
||||||
|
</timeBasedFileNamingAndTriggeringPolicy>
|
||||||
|
<!-- 일자별 로그파일 최대 보관주기(~일), 해당 설정일 이상된 파일은 자동으로 제거-->
|
||||||
|
<maxHistory>60</maxHistory>
|
||||||
|
</rollingPolicy>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!-- root레벨 설정 -->
|
||||||
|
<root level="${LOG_LEVEL}" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</root>
|
||||||
|
|
||||||
|
<!-- 특정패키지 로깅레벨 설정 -->
|
||||||
|
<logger name="org.springframework" level="WARN" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<!-- log4jdbc 옵션 설정 -->
|
||||||
|
<logger name="jdbc" level="OFF" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
<!-- sql문만 로깅할지 여부 -->
|
||||||
|
<logger name="jdbc.sqlonly" level="${LOG_LEVEL}" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
<!-- 쿼리문 수행시간 로깅 여부 -->
|
||||||
|
<logger name="jdbc.sqltiming" level="OFF" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
<!-- ResultSet외 모든 JDBC 호출 정보 로깅할지 여부 -->
|
||||||
|
<logger name="jdbc.audit" level="OFF" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
<!-- ResultSet 포함 모든 JDBC 호출 정보를 로깅 -->
|
||||||
|
<logger name="jdbc.resultset" level="OFF" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
<logger name="jdbc.resultsettable" level="OFF" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
<!-- connection open close 로깅 여부 -->
|
||||||
|
<logger name="jdbc.connection" level="OFF" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<logger name="com.zaxxer.hikari.pool.HikariProxyResultSet" level="OFF" additivity="false">
|
||||||
|
<appender-ref ref="CONSOLE"/>
|
||||||
|
<appender-ref ref="FILE"/>
|
||||||
|
<appender-ref ref="Error"/>
|
||||||
|
</logger>
|
||||||
|
</configuration>
|
||||||
BIN
src/main/resources/private.key
Normal file
BIN
src/main/resources/private.key
Normal file
Binary file not shown.
BIN
src/main/resources/public.key
Normal file
BIN
src/main/resources/public.key
Normal file
Binary file not shown.
Reference in New Issue
Block a user