Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nouiWithSpringMVC
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
gechengyang
nouiWithSpringMVC
Commits
98654e0b
Commit
98654e0b
authored
Oct 07, 2020
by
WeiCong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
完善数据安全框架
parent
a20df4c4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
361 additions
and
269 deletions
+361
-269
NoUiRequest.java
...va/org/sss/presentation/noui/api/request/NoUiRequest.java
+19
-0
AbstractCommonController.java
...resentation/noui/controller/AbstractCommonController.java
+198
-200
DataSecurityUtil.java
...java/org/sss/presentation/noui/util/DataSecurityUtil.java
+136
-67
security.properties
src/main/resources/security.properties
+8
-2
No files found.
src/main/java/org/sss/presentation/noui/api/request/NoUiRequest.java
View file @
98654e0b
...
@@ -19,6 +19,8 @@ public class NoUiRequest {
...
@@ -19,6 +19,8 @@ public class NoUiRequest {
private
Map
<
String
,
?>
dataMap
=
new
HashMap
<
String
,
Object
>();
private
Map
<
String
,
?>
dataMap
=
new
HashMap
<
String
,
Object
>();
private
Map
<
String
,
?>
saveDisplayMap
=
new
HashMap
<
String
,
Object
>();
private
Map
<
String
,
?>
saveDisplayMap
=
new
HashMap
<
String
,
Object
>();
private
boolean
isSecurity
=
false
;
private
boolean
isSecurity
=
false
;
private
String
reqUrl
;
private
String
trnName
;
public
NoUiRequest
()
{
public
NoUiRequest
()
{
...
@@ -34,6 +36,15 @@ public class NoUiRequest {
...
@@ -34,6 +36,15 @@ public class NoUiRequest {
this
.
userId
=
userId
;
this
.
userId
=
userId
;
this
.
terminalType
=
terminalType
;
this
.
terminalType
=
terminalType
;
this
.
mappingUrl
=
mappingUrl
;
this
.
mappingUrl
=
mappingUrl
;
String
[]
mappingArgs
=
mappingUrl
.
split
(
"/"
);
if
(
mappingArgs
.
length
>
1
){
this
.
trnName
=
mappingArgs
[
mappingArgs
.
length
-
2
];
if
(
request
.
getRequestURI
().
indexOf
(
this
.
trnName
)>
0
){
this
.
reqUrl
=
request
.
getRequestURI
().
substring
(
request
.
getRequestURI
().
indexOf
(
this
.
trnName
)-
1
);
}
else
{
this
.
reqUrl
=
mappingUrl
;
}
}
if
(!
StringUtil
.
isEmpty
(
security
)){
if
(!
StringUtil
.
isEmpty
(
security
)){
this
.
isSecurity
=
true
;
this
.
isSecurity
=
true
;
}
}
...
@@ -125,4 +136,12 @@ public class NoUiRequest {
...
@@ -125,4 +136,12 @@ public class NoUiRequest {
public
boolean
isSecurity
()
{
public
boolean
isSecurity
()
{
return
isSecurity
;
return
isSecurity
;
}
}
public
String
getReqUrl
()
{
return
reqUrl
;
}
public
String
getTrnName
()
{
return
trnName
;
}
}
}
src/main/java/org/sss/presentation/noui/controller/AbstractCommonController.java
View file @
98654e0b
...
@@ -32,218 +32,216 @@ import java.util.HashMap;
...
@@ -32,218 +32,216 @@ import java.util.HashMap;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
public
abstract
class
AbstractCommonController
{
public
abstract
class
AbstractCommonController
{
protected
static
final
Log
log
=
LogFactory
.
getLog
(
AbstractCommonController
.
class
);
protected
static
final
Log
log
=
LogFactory
.
getLog
(
AbstractCommonController
.
class
);
protected
static
String
ON_CLICK
=
"ON_CLICK"
;
protected
static
String
ON_CLICK
=
"ON_CLICK"
;
protected
static
String
INIT
=
"INIT"
;
protected
static
String
INIT
=
"INIT"
;
protected
static
String
ON_CHANGE
=
"ON_CHANGE"
;
protected
static
String
ON_CHANGE
=
"ON_CHANGE"
;
protected
static
String
ON_CHECK
=
"ON_CHECK"
;
protected
static
String
ON_CHECK
=
"ON_CHECK"
;
protected
static
String
ON_STREAM_UPLOAD
=
"ON_STREAM_UPLOAD"
;
protected
static
String
ON_STREAM_UPLOAD
=
"ON_STREAM_UPLOAD"
;
protected
static
String
ON_STREAM_DOWNLOAD
=
"ON_STREAM_DOWNLOAD"
;
protected
static
String
ON_STREAM_DOWNLOAD
=
"ON_STREAM_DOWNLOAD"
;
@Autowired
@Autowired
private
NoUiVersion
noUiVersion
;
private
NoUiVersion
noUiVersion
;
public
String
getMainPanel
()
{
public
String
getMainPanel
()
{
return
""
;
return
""
;
}
}
@SuppressWarnings
(
"unchecked"
)
@SuppressWarnings
(
"unchecked"
)
public
Object
event
(
String
mappingUrl
,
String
eventType
,
Map
<
String
,
Object
>
dataMap
,
MultipartFile
file
,
HttpServletRequest
request
,
HttpServletResponse
response
)
{
public
Object
event
(
String
mappingUrl
,
String
eventType
,
Map
<
String
,
Object
>
dataMap
,
MultipartFile
file
,
HttpServletRequest
request
,
HttpServletResponse
response
)
{
NoUiContext
context
=
null
;
NoUiContext
context
=
null
;
Result
ret
=
null
;
Result
ret
=
null
;
String
serverEnc
=
null
;
String
serverEnc
=
null
;
try
{
try
{
NoUiRequest
noUiRequest
=
new
NoUiRequest
(
request
,
mappingUrl
,
dataMap
);
NoUiRequest
noUiRequest
=
new
NoUiRequest
(
request
,
mappingUrl
,
dataMap
);
Alias
alias
=
new
Alias
(
mappingUrl
);
Alias
alias
=
new
Alias
(
mappingUrl
);
String
trnName
=
alias
.
getTrnName
();
String
trnName
=
alias
.
getTrnName
();
Map
<
String
,
?>
paramsMap
=
noUiRequest
.
getParamsMap
();
Map
<
String
,
?>
paramsMap
=
noUiRequest
.
getParamsMap
();
context
=
NoUiContextManager
.
createNoUiContext
(
noUiRequest
);
context
=
NoUiContextManager
.
createNoUiContext
(
noUiRequest
);
// 交易参数赋值
// 交易参数赋值
for
(
String
key
:
paramsMap
.
keySet
())
{
for
(
String
key
:
paramsMap
.
keySet
())
{
context
.
getSession
().
storeData
(
key
,
paramsMap
.
get
(
key
));
context
.
getSession
().
storeData
(
key
,
paramsMap
.
get
(
key
));
}
}
// 设置old sysmod
// 设置old sysmod
RedisLoginInfo
redisLoginInfo
=
null
;
RedisLoginInfo
redisLoginInfo
=
null
;
if
(
!
StringUtils
.
isEmpty
(
noUiRequest
.
getUserId
())
)
//开放模式下
if
(!
StringUtils
.
isEmpty
(
noUiRequest
.
getUserId
()))
//开放模式下
redisLoginInfo
=
(
RedisLoginInfo
)
RedisUtil
.
get
(
StringUtil
.
userUniqueId
(
noUiRequest
));
redisLoginInfo
=
(
RedisLoginInfo
)
RedisUtil
.
get
(
StringUtil
.
userUniqueId
(
noUiRequest
));
if
(
redisLoginInfo
!=
null
)
if
(
redisLoginInfo
!=
null
)
{
{
NoUiPresentationUtil
.
setSysmod
(
context
,
(
byte
[])
redisLoginInfo
.
getSysmod
());
NoUiPresentationUtil
.
setSysmod
(
context
,
(
byte
[])
redisLoginInfo
.
getSysmod
());
context
.
setRedisLoginInfo
(
redisLoginInfo
);
context
.
setRedisLoginInfo
(
redisLoginInfo
);
}
}
// 交易跳转
// 交易跳转
context
.
getSession
().
chain
(
true
,
trnName
);
context
.
getSession
().
chain
(
true
,
trnName
);
//执行可能存在的主面板的初始化
//执行可能存在的主面板的初始化
if
(
this
.
getMainPanel
().
length
()
>
0
)
{
if
(
this
.
getMainPanel
().
length
()
>
0
)
IPanel
mainPanel
=
(
IPanel
)
context
.
getSession
().
getBaseObject
(
null
,
this
.
getMainPanel
());
{
mainPanel
.
invokeDefaultRules
(
context
);
IPanel
mainPanel
=
(
IPanel
)
context
.
getSession
().
getBaseObject
(
null
,
this
.
getMainPanel
());
}
mainPanel
.
invokeDefaultRules
(
context
);
// 模型赋值
}
NoUiPresentationUtil
.
hanleInput
(
context
,
noUiRequest
,
alias
);
// 模型赋值
NoUiPresentationUtil
.
hanleInput
(
context
,
noUiRequest
,
alias
);
//数据安全性拦截-篡改数据拦截
//数据安全性拦截-篡改数据拦截
if
(
DataSecurityUtil
.
isSafeMode
()
&&
noUiRequest
.
isSecurity
()){
if
(
DataSecurityUtil
.
isSafeMode
()
&&
noUiRequest
.
isSecurity
())
{
if
(
paramsMap
.
containsKey
(
DataSecurityUtil
.
BACKGROUND_ID
)){
if
(
DataSecurityUtil
.
needDecrypt
(
noUiRequest
.
getReqUrl
()))
{
String
[]
clientpars
=
DataSecurityUtil
.
getSafeConfigByTrnName
(
context
,
trnName
);
if
(
paramsMap
.
containsKey
(
DataSecurityUtil
.
BACKGROUND_ID
))
{
if
(!
ArrayUtils
.
isEmpty
(
clientpars
)){
String
[]
clientpars
=
DataSecurityUtil
.
getSafeConfigByReqUrl
(
context
,
noUiRequest
.
getReqUrl
()
+
DataSecurityUtil
.
DECRYPT_FIX
);
//合法性校验操作(场景:用户做修改、删除时调用)
if
(!
ArrayUtils
.
isEmpty
(
clientpars
))
{
serverEnc
=
(
String
)
paramsMap
.
get
(
DataSecurityUtil
.
BACKGROUND_ID
);
//合法性校验操作(场景:用户做修改、删除时调用)
String
errmsg
=
null
;
serverEnc
=
(
String
)
paramsMap
.
get
(
DataSecurityUtil
.
BACKGROUND_ID
);
if
((
errmsg
=
DataSecurityUtil
.
checkIllegalData
(
serverEnc
,
clientpars
,
noUiRequest
.
getUserId
()))!=
null
){
String
errmsg
=
null
;
Result
rt
=
new
Result
(
ErrorCodes
.
ERROR
,
errmsg
,
null
,
noUiVersion
.
getVersion
());
if
((
errmsg
=
DataSecurityUtil
.
checkIllegalData
(
serverEnc
,
clientpars
,
noUiRequest
.
getUserId
()))
!=
null
)
{
return
rt
;
Result
rt
=
new
Result
(
ErrorCodes
.
ERROR
,
errmsg
,
null
,
noUiVersion
.
getVersion
());
return
rt
;
}
}
}
}
else
{
Result
rt
=
new
Result
(
ErrorCodes
.
ERROR
,
DataSecurityUtil
.
ERROR_SERVERENC_NULL
,
null
,
noUiVersion
.
getVersion
());
return
rt
;
}
}
}
}
}
}
if
(
eventType
.
equals
(
ON_CLICK
))
{
if
(
eventType
.
equals
(
ON_CLICK
))
{
IBaseObject
dataField
=
baseObject
(
context
,
noUiRequest
,
alias
);
IBaseObject
dataField
=
baseObject
(
context
,
noUiRequest
,
alias
);
((
IDatafield
<?>)
dataField
).
invokeEventRules
(
context
,
EventType
.
ON_CLICK
,
null
);
((
IDatafield
<?>)
dataField
).
invokeEventRules
(
context
,
EventType
.
ON_CLICK
,
null
);
}
else
if
(
eventType
.
equals
(
ON_CHANGE
))
{
}
else
if
(
eventType
.
equals
(
ON_CHANGE
))
{
IBaseObject
dataField
=
baseObject
(
context
,
noUiRequest
,
alias
);
IBaseObject
dataField
=
baseObject
(
context
,
noUiRequest
,
alias
);
((
IDatafield
<?>)
dataField
).
invokeEventRules
(
context
,
EventType
.
ON_CHANGE
,
null
);
((
IDatafield
<?>)
dataField
).
invokeEventRules
(
context
,
EventType
.
ON_CHANGE
,
null
);
}
else
if
(
eventType
.
equals
(
ON_CHECK
))
{
}
else
if
(
eventType
.
equals
(
ON_CHECK
))
{
for
(
String
aliasKey
:
noUiRequest
.
getDataMap
().
keySet
())
{
for
(
String
aliasKey
:
noUiRequest
.
getDataMap
().
keySet
())
{
if
(
aliasKey
.
startsWith
(
Constants
.
MAPPING_PRE
))
{
if
(
aliasKey
.
startsWith
(
Constants
.
MAPPING_PRE
))
{
continue
;
continue
;
}
}
String
realPath
=
alias
.
getRelPath
(
aliasKey
);
String
realPath
=
alias
.
getRelPath
(
aliasKey
);
IBaseObject
currentDataField
=
(
IDatafield
<?>)
context
.
getSession
().
getBaseObject
(
context
.
getRoot
(),
realPath
);
IBaseObject
currentDataField
=
(
IDatafield
<?>)
context
.
getSession
().
getBaseObject
(
context
.
getRoot
(),
realPath
);
if
(
currentDataField
instanceof
IDatafield
<?>)
{
if
(
currentDataField
instanceof
IDatafield
<?>)
{
((
IDatafield
<?>)
currentDataField
).
invokeCheckRules
(
context
);
((
IDatafield
<?>)
currentDataField
).
invokeCheckRules
(
context
);
}
}
}
}
}
else
if
(
eventType
.
equals
(
ON_STREAM_UPLOAD
)
&&
file
!=
null
)
{
}
else
if
(
eventType
.
equals
(
ON_STREAM_UPLOAD
)
&&
file
!=
null
)
{
@SuppressWarnings
(
"rawtypes"
)
@SuppressWarnings
(
"rawtypes"
)
IDatafield
dataField
=
(
IDatafield
)
baseObject
(
context
,
noUiRequest
,
alias
);
IDatafield
dataField
=
(
IDatafield
)
baseObject
(
context
,
noUiRequest
,
alias
);
IStream
stream
=
(
IStream
)
dataField
.
getValue
();
IStream
stream
=
(
IStream
)
dataField
.
getValue
();
stream
.
setName
(
file
.
getOriginalFilename
());
stream
.
setName
(
file
.
getOriginalFilename
());
stream
.
setType
(
file
.
getContentType
());
stream
.
setType
(
file
.
getContentType
());
log
.
info
(
"文件表单 key:"
+
file
.
getOriginalFilename
()
+
",size:"
+
file
.
getSize
());
log
.
info
(
"文件表单 key:"
+
file
.
getOriginalFilename
()
+
",size:"
+
file
.
getSize
());
IOUtils
.
copy
(
file
.
getInputStream
(),
stream
.
getOutputStream
(),
1024
);
IOUtils
.
copy
(
file
.
getInputStream
(),
stream
.
getOutputStream
(),
1024
);
dataField
.
invokeEventRules
(
context
,
EventType
.
ON_STREAM_UPLOAD
,
null
);
dataField
.
invokeEventRules
(
context
,
EventType
.
ON_STREAM_UPLOAD
,
null
);
}
else
if
(
eventType
.
equals
(
ON_STREAM_DOWNLOAD
))
{
}
else
if
(
eventType
.
equals
(
ON_STREAM_DOWNLOAD
))
{
@SuppressWarnings
(
"rawtypes"
)
@SuppressWarnings
(
"rawtypes"
)
IDatafield
dataField
=
(
IDatafield
)
baseObject
(
context
,
noUiRequest
,
alias
);
IDatafield
dataField
=
(
IDatafield
)
baseObject
(
context
,
noUiRequest
,
alias
);
IStream
stream
=
(
IStream
)
dataField
.
getValue
();
IStream
stream
=
(
IStream
)
dataField
.
getValue
();
byte
[]
data
=
IOUtils
.
readFully
(
stream
.
getInputStream
(),
(
int
)
stream
.
size
());
byte
[]
data
=
IOUtils
.
readFully
(
stream
.
getInputStream
(),
(
int
)
stream
.
size
());
dataField
.
invokeEventRules
(
context
,
EventType
.
ON_STREAM_DOWNLOAD
,
null
);
dataField
.
invokeEventRules
(
context
,
EventType
.
ON_STREAM_DOWNLOAD
,
null
);
IOUtils
.
write
(
data
,
response
.
getOutputStream
());
IOUtils
.
write
(
data
,
response
.
getOutputStream
());
}
}
// 保存新的RedisLoginInfo
// 保存新的RedisLoginInfo
if
(
redisLoginInfo
!=
null
)
//当为开放模式下,redisLoginInfo 为空
if
(
redisLoginInfo
!=
null
)
//当为开放模式下,redisLoginInfo 为空
{
{
byte
[]
sysmodBytes
=
NoUiPresentationUtil
.
sysmodToBytes
(
context
);
byte
[]
sysmodBytes
=
NoUiPresentationUtil
.
sysmodToBytes
(
context
);
redisLoginInfo
.
setSysmod
(
sysmodBytes
);
redisLoginInfo
.
setSysmod
(
sysmodBytes
);
RedisUtil
.
set
(
StringUtil
.
userUniqueId
(
noUiRequest
),
redisLoginInfo
);
RedisUtil
.
set
(
StringUtil
.
userUniqueId
(
noUiRequest
),
redisLoginInfo
);
}
}
Map
<
String
,
Object
>
afterReturnData
=
handleReturnData
(
eventType
,
context
,
noUiRequest
,
alias
);
Map
<
String
,
Object
>
afterReturnData
=
handleReturnData
(
eventType
,
context
,
noUiRequest
,
alias
);
//数据安全性拦截-篡改数据加密
//数据安全性拦截-篡改数据加密
if
(
DataSecurityUtil
.
isSafeMode
()
&&
noUiRequest
.
isSecurity
())
{
if
(
DataSecurityUtil
.
isSafeMode
()
&&
noUiRequest
.
isSecurity
())
{
if
(!
paramsMap
.
containsKey
(
DataSecurityUtil
.
BACKGROUND_ID
))
{
if
(
DataSecurityUtil
.
needEncrypt
(
noUiRequest
.
getReqUrl
()))
{
//加密操作(场景:用户查询指定信息时调用,后续会做修改,删除等操作)
//加密操作(场景:用户查询指定信息时调用,后续会做修改,删除等操作)
String
[]
pars
=
DataSecurityUtil
.
getSafeConfigByTrnName
(
context
,
trnName
);
String
[]
pars
=
DataSecurityUtil
.
getSafeConfigByReqUrl
(
context
,
noUiRequest
.
getReqUrl
()
+
DataSecurityUtil
.
ENCRYPT_FIX
);
if
(!
ArrayUtils
.
isEmpty
(
pars
)){
serverEnc
=
DataSecurityUtil
.
encrypt
(
pars
,
noUiRequest
.
getUserId
());
serverEnc
=
DataSecurityUtil
.
encrypt
(
pars
,
noUiRequest
.
getUserId
());
afterReturnData
.
put
(
DataSecurityUtil
.
BACKGROUND_ID
,
serverEnc
);
afterReturnData
.
put
(
DataSecurityUtil
.
BACKGROUND_ID
,
serverEnc
);
}
}
}
}
}
ret
=
ResultUtil
.
result
(
NoUiPresentationUtil
.
retCode
(
context
),
NoUiPresentationUtil
.
retMsg
(
context
),
afterReturnData
,
ret
=
ResultUtil
.
result
(
NoUiPresentationUtil
.
retCode
(
context
),
NoUiPresentationUtil
.
retMsg
(
context
),
afterReturnData
,
NoUiPresentationUtil
.
handleErrorReturnData
(
context
,
alias
),
NoUiPresentationUtil
.
handleCodeTableReturnData
(
context
,
alias
),
noUiVersion
.
getVersion
());
NoUiPresentationUtil
.
handleErrorReturnData
(
context
,
alias
),
NoUiPresentationUtil
.
handleCodeTableReturnData
(
context
,
alias
),
noUiVersion
.
getVersion
());
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
log
.
error
(
"OnClick command error"
,
e
);
log
.
error
(
"OnClick command error"
,
e
);
ret
=
ResultUtil
.
result
(
ErrorCodes
.
ERROR
,
"hander error"
,
"service error"
,
noUiVersion
.
getVersion
());
ret
=
ResultUtil
.
result
(
ErrorCodes
.
ERROR
,
"hander error"
,
"service error"
,
noUiVersion
.
getVersion
());
}
finally
{
}
finally
{
if
(
context
!=
null
)
if
(
context
!=
null
)
{
{
//context.getSupport().disconnect();
//context.getSupport().disconnect();
context
.
dispose
();
context
.
dispose
();
}
}
}
}
return
ret
;
return
ret
;
}
}
private
IBaseObject
baseObject
(
NoUiContext
context
,
NoUiRequest
noUiRequest
,
Alias
alias
)
{
private
IBaseObject
baseObject
(
NoUiContext
context
,
NoUiRequest
noUiRequest
,
Alias
alias
)
{
String
aliasActionUrl
=
alias
.
getAliasActionUrl
();
String
aliasActionUrl
=
alias
.
getAliasActionUrl
();
String
actionUrl
=
alias
.
getRel
().
get
(
aliasActionUrl
);
String
actionUrl
=
alias
.
getRel
().
get
(
aliasActionUrl
);
IBaseObject
baseObject
=
context
.
getSession
().
getBaseObject
(
context
.
getRoot
(),
actionUrl
);
IBaseObject
baseObject
=
context
.
getSession
().
getBaseObject
(
context
.
getRoot
(),
actionUrl
);
if
(
null
==
baseObject
)
if
(
null
==
baseObject
)
throw
new
NoUiException
(
"onClickUrl :"
+
actionUrl
+
" is not exsit"
);
throw
new
NoUiException
(
"onClickUrl :"
+
actionUrl
+
" is not exsit"
);
return
baseObject
;
return
baseObject
;
}
}
private
Map
<
String
,
Object
>
handleReturnData
(
String
eventType
,
NoUiContext
context
,
NoUiRequest
noUiRequest
,
Alias
alias
)
{
private
Map
<
String
,
Object
>
handleReturnData
(
String
eventType
,
NoUiContext
context
,
NoUiRequest
noUiRequest
,
Alias
alias
)
{
Map
<
String
,
Object
>
data
=
new
HashMap
<
String
,
Object
>();
Map
<
String
,
Object
>
data
=
new
HashMap
<
String
,
Object
>();
// 初始化事件
// 初始化事件
if
(
eventType
.
equals
(
INIT
))
{
if
(
eventType
.
equals
(
INIT
))
{
for
(
String
aliasKey
:
alias
.
getRel
().
keySet
())
{
for
(
String
aliasKey
:
alias
.
getRel
().
keySet
())
{
if
(
aliasKey
.
startsWith
(
Constants
.
MAPPING_PRE
))
{
if
(
aliasKey
.
startsWith
(
Constants
.
MAPPING_PRE
))
{
continue
;
continue
;
}
}
String
realPath
=
alias
.
getRelPath
(
aliasKey
);
String
realPath
=
alias
.
getRelPath
(
aliasKey
);
IBaseObject
baseObject
=
context
.
getSession
().
getBaseObject
(
context
.
getRoot
(),
realPath
);
IBaseObject
baseObject
=
context
.
getSession
().
getBaseObject
(
context
.
getRoot
(),
realPath
);
data
.
put
(
aliasKey
,
NoUiPresentationUtil
.
handIBaseObject
(
context
,
baseObject
,
realPath
));
data
.
put
(
aliasKey
,
NoUiPresentationUtil
.
handIBaseObject
(
context
,
baseObject
,
realPath
));
}
}
return
data
;
return
data
;
}
}
NoUiPresentation
gui
=
(
NoUiPresentation
)
context
.
getGui
();
NoUiPresentation
gui
=
(
NoUiPresentation
)
context
.
getGui
();
Map
<
String
,
Object
>
modifyMap
=
gui
.
getModifyMap
();
Map
<
String
,
Object
>
modifyMap
=
gui
.
getModifyMap
();
List
<
String
>
containsKeys
=
new
ArrayList
<
String
>();
List
<
String
>
containsKeys
=
new
ArrayList
<
String
>();
for
(
Map
.
Entry
<
String
,
Object
>
modifyEntry
:
modifyMap
.
entrySet
())
{
for
(
Map
.
Entry
<
String
,
Object
>
modifyEntry
:
modifyMap
.
entrySet
())
{
for
(
Map
.
Entry
<
String
,
String
>
aliasEntry
:
alias
.
getRel
().
entrySet
())
{
for
(
Map
.
Entry
<
String
,
String
>
aliasEntry
:
alias
.
getRel
().
entrySet
())
{
String
aliasKey
=
aliasEntry
.
getKey
();
String
aliasKey
=
aliasEntry
.
getKey
();
String
aliasPath
=
aliasEntry
.
getValue
();
String
aliasPath
=
aliasEntry
.
getValue
();
if
(
aliasPath
==
null
)
{
if
(
aliasPath
==
null
)
log
.
error
(
"错误的mapping:"
+
noUiRequest
.
getMappingUrl
()
+
"--"
+
aliasKey
+
"--"
+
aliasPath
);
{
continue
;
log
.
error
(
"错误的mapping:"
+
noUiRequest
.
getMappingUrl
()+
"--"
+
aliasKey
+
"--"
+
aliasPath
);
}
continue
;
if
(
modifyEntry
.
getKey
()
==
null
)
{
}
log
.
error
(
"错误的modifymap:"
+
noUiRequest
.
getMappingUrl
()
+
"--"
+
modifyMap
);
if
(
modifyEntry
.
getKey
()
==
null
)
continue
;
{
}
log
.
error
(
"错误的modifymap:"
+
noUiRequest
.
getMappingUrl
()+
"--"
+
modifyMap
);
if
(
aliasPath
.
startsWith
(
modifyEntry
.
getKey
()))
{
continue
;
Object
val
=
modifyEntry
.
getValue
();
}
if
(
aliasKey
==
null
)
if
(
aliasPath
.
startsWith
(
modifyEntry
.
getKey
()))
{
continue
;
Object
val
=
modifyEntry
.
getValue
();
data
.
put
(
aliasKey
,
NoUiPresentationUtil
.
handIBaseObject
(
context
,
val
,
aliasEntry
.
getValue
()));
if
(
aliasKey
==
null
)
containsKeys
.
add
(
modifyEntry
.
getKey
());
continue
;
}
data
.
put
(
aliasKey
,
NoUiPresentationUtil
.
handIBaseObject
(
context
,
val
,
aliasEntry
.
getValue
()));
containsKeys
.
add
(
modifyEntry
.
getKey
());
}
}
}
}
// 不能修改
}
for
(
String
key
:
modifyMap
.
keySet
())
{
if
(!
containsKeys
.
contains
(
key
))
{
// 不能修改
System
.
out
.
println
(
"modify test:"
+
modifyMap
.
get
(
key
).
getClass
());
for
(
String
key
:
modifyMap
.
keySet
())
{
System
.
out
.
println
(
"modify datafield:"
+
(
modifyMap
.
get
(
key
)
instanceof
IDatafield
));
if
(!
containsKeys
.
contains
(
key
))
{
System
.
out
.
println
(
"modify module:"
+
(
modifyMap
.
get
(
key
)
instanceof
IModule
));
System
.
out
.
println
(
"modify test:"
+
modifyMap
.
get
(
key
).
getClass
());
System
.
out
.
println
(
"modify moduleList:"
+
(
modifyMap
.
get
(
key
)
instanceof
IModuleList
));
System
.
out
.
println
(
"modify datafield:"
+
(
modifyMap
.
get
(
key
)
instanceof
IDatafield
));
if
(
key
==
null
)
System
.
out
.
println
(
"modify module:"
+
(
modifyMap
.
get
(
key
)
instanceof
IModule
));
continue
;
System
.
out
.
println
(
"modify moduleList:"
+
(
modifyMap
.
get
(
key
)
instanceof
IModuleList
));
data
.
put
(
key
,
NoUiPresentationUtil
.
handIBaseObject
(
context
,
modifyMap
.
get
(
key
),
key
));
if
(
key
==
null
)
}
continue
;
}
data
.
put
(
key
,
NoUiPresentationUtil
.
handIBaseObject
(
context
,
modifyMap
.
get
(
key
),
key
));
}
return
data
;
}
}
return
data
;
}
}
}
src/main/java/org/sss/presentation/noui/util/DataSecurityUtil.java
View file @
98654e0b
...
@@ -6,6 +6,8 @@ import org.apache.commons.lang.ArrayUtils;
...
@@ -6,6 +6,8 @@ import org.apache.commons.lang.ArrayUtils;
import
org.springframework.core.io.ClassPathResource
;
import
org.springframework.core.io.ClassPathResource
;
import
org.sss.common.model.IBaseObject
;
import
org.sss.common.model.IBaseObject
;
import
org.sss.common.model.IDatafield
;
import
org.sss.common.model.IDatafield
;
import
org.sss.common.model.IModule
;
import
org.sss.common.model.IModuleList
;
import
org.sss.presentation.noui.common.Constants
;
import
org.sss.presentation.noui.common.Constants
;
import
org.sss.presentation.noui.context.NoUiContext
;
import
org.sss.presentation.noui.context.NoUiContext
;
...
@@ -16,72 +18,123 @@ import java.util.*;
...
@@ -16,72 +18,123 @@ import java.util.*;
* 使用动态盐机制,每个盐只做一次双向校验后就失效
* 使用动态盐机制,每个盐只做一次双向校验后就失效
*/
*/
public
class
DataSecurityUtil
{
public
class
DataSecurityUtil
{
public
static
final
String
ENCRYPT_FIX
=
"_encode"
;
public
static
final
String
DECRYPT_FIX
=
"_decode"
;
public
static
final
String
ENCRYPT_ERROR
=
"encrypt exception"
;
public
static
final
String
ENCRYPT_ERROR
=
"encrypt exception"
;
public
static
final
String
DECRYPT_ERROR
=
"decrypt exception"
;
public
static
final
String
DECRYPT_ERROR
=
"decrypt exception"
;
public
static
final
String
BACKGROUND_ID
=
"__bgid__"
;
public
static
final
String
BACKGROUND_ID
=
"__bgid__"
;
public
static
final
String
ERROR_SERVERENC_NULL
=
"[服务端密文串异常],数据存在篡改风险!"
;
public
static
final
String
ERROR_SERVERENC_NULL
=
"[服务端密文串异常],数据存在篡改风险!"
;
public
static
final
String
ERROR_DYNAMICSALT_NULL
=
"[获取的动态盐不存在],可能存在后台暴力破解风险,从而导致数据存在篡改被篡改的风险!"
;
public
static
final
String
ERROR_DYNAMICSALT_NULL
=
"[获取的动态盐不存在],可能存在后台暴力破解风险,从而导致数据存在篡改被篡改的风险!"
;
public
static
final
String
ERROR_DYNAMICSALT_EXCEPTION
=
"[获取动态盐异常],可能redis异常,从而导致数据存在篡改被篡改的风险!"
;
public
static
final
String
ERROR_DYNAMICSALT_EXCEPTION
=
"[获取动态盐异常],可能redis异常,从而导致数据存在篡改被篡改的风险!"
;
public
static
final
String
ERROR_AES_DECODE
=
"[服务端密文串解密失败],数据存在篡改风险!"
;
public
static
final
String
ERROR_AES_DECODE
=
"[服务端密文串解密失败],数据存在篡改风险!"
;
public
static
final
String
ERROR_NOTMATCH
=
"[服务端密文串解密后的值与客户端密文串不同],数据存在篡改风险!"
;
public
static
final
String
ERROR_NOTMATCH
=
"[服务端密文串解密后的值与客户端密文串不同],数据存在篡改风险!"
;
public
static
final
String
SWITCH_KEY
=
"switch"
;
public
static
final
String
SAFE_MODE
=
"ON"
;
public
static
final
String
UNSAFE_MODE
=
"OFF"
;
private
static
HashMap
<
String
,
String
>
securityConfig
;
private
static
final
Log
log
=
LogFactory
.
getLog
(
DataSecurityUtil
.
class
);
private
static
final
Log
log
=
LogFactory
.
getLog
(
DataSecurityUtil
.
class
);
public
static
boolean
SWITCH
=
false
;
private
static
HashMap
<
String
,
String
[]>
securityConfig
=
new
HashMap
<
String
,
String
[]>();
static
{
static
{
ClassPathResource
resource
=
new
ClassPathResource
(
"security.properties"
,
DataSecurityUtil
.
class
);
ClassPathResource
resource
=
new
ClassPathResource
(
"security.properties"
,
DataSecurityUtil
.
class
);
try
{
try
{
securityConfig
=
new
HashMap
<
String
,
String
>((
Map
)
PropertyUtil
.
load
(
"security.properties"
));
Map
<
String
,
String
>
tmp
=
new
HashMap
<
String
,
String
>((
Map
)
PropertyUtil
.
load
(
"security.properties"
));
for
(
Map
.
Entry
<
String
,
String
>
entry
:
tmp
.
entrySet
())
{
String
key
=
entry
.
getKey
();
String
val
=
entry
.
getValue
();
if
(
"switch"
.
equals
(
key
)
&&
"ON"
.
equals
(
val
))
{
SWITCH
=
true
;
}
else
{
//数据安全配置
if
(
securityConfig
.
containsKey
(
key
))
{
log
.
warn
(
"忽略重复的安全信息:"
+
key
);
continue
;
}
if
(
StringUtil
.
isEmpty
(
val
))
{
log
.
warn
(
"忽略值为空的安全信息:"
+
key
);
continue
;
}
securityConfig
.
put
(
key
,
val
.
split
(
";"
));
}
}
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
//降级处理
//降级处理
log
.
warn
(
"加载安全配置文件失败:"
+
e
.
getMessage
())
;
SWITCH
=
false
;
securityConfig
=
new
HashMap
<
String
,
String
>();
securityConfig
=
new
HashMap
<
String
,
String
[]
>();
securityConfig
.
put
(
SWITCH_KEY
,
UNSAFE_MODE
);
log
.
warn
(
"加载安全配置文件失败:"
+
e
.
getMessage
()
);
}
}
}
}
/**
/**
* 是否开启安全模式,开启后会对前端传输的数据根据配置文件做篡改性校验
* 是否开启安全模式,开启后会对前端传输的数据根据配置文件做篡改性校验
*
* @return true表示开启;false不开启
* @return true表示开启;false不开启
*/
*/
public
static
boolean
isSafeMode
(){
public
static
boolean
isSafeMode
()
{
return
SAFE_MODE
.
equalsIgnoreCase
(
securityConfig
.
get
(
SWITCH_KEY
));
return
SWITCH
;
}
public
static
boolean
needEncrypt
(
String
reqUrl
)
{
return
securityConfig
.
containsKey
(
reqUrl
+
ENCRYPT_FIX
);
}
public
static
boolean
needDecrypt
(
String
reqUrl
)
{
return
securityConfig
.
containsKey
(
reqUrl
+
DECRYPT_FIX
);
}
}
/**
/**
* 获取指定交易的安全配置
* 获取指定交易的安全配置
* @param context 交易上下文
*
* @param trnName 交易名
* @param context 交易上下文
* @param configKey 安全配置的key
* @return 指定交易的安全配置信息
* @return 指定交易的安全配置信息
*/
*/
public
static
String
[]
getSafeConfigByTrnName
(
NoUiContext
context
,
String
trnName
){
public
static
String
[]
getSafeConfigByReqUrl
(
NoUiContext
context
,
String
configKey
)
{
String
safeConfigByTrnName
=
securityConfig
.
get
(
trnName
);
String
[]
safeConfigByTrnNames
=
securityConfig
.
get
(
configKey
);
if
(!
StringUtil
.
isEmpty
(
safeConfigByTrnName
)){
for
(
String
safeConfigByTrnName
:
safeConfigByTrnNames
)
{
String
[]
urls
=
safeConfigByTrnName
.
split
(
","
);
if
(!
StringUtil
.
isEmpty
(
safeConfigByTrnName
))
{
String
[]
pars
=
new
String
[
urls
.
length
];
String
[]
urls
=
safeConfigByTrnName
.
split
(
","
);
for
(
int
i
=
0
;
i
<
urls
.
length
;
i
++){
String
[]
pars
=
new
String
[
urls
.
length
];
String
url
=
urls
[
i
];
boolean
flag
=
true
;
IBaseObject
baseObject
=
context
.
getSession
().
getBaseObject
(
context
.
getRoot
(),
url
);
for
(
int
i
=
0
;
i
<
urls
.
length
;
i
++)
{
if
(
null
==
baseObject
)
{
String
url
=
urls
[
i
];
StringBuilder
sb
=
new
StringBuilder
();
String
lstTemplate
=
null
;
sb
.
append
(
"交易["
).
append
(
trnName
).
append
(
"]对应的待校验字段["
);
if
(
url
.
indexOf
(
"[]"
)>
0
){
sb
.
append
(
url
).
append
(
"]在模型中未找到"
);
lstTemplate
=
url
;
log
.
error
(
sb
.
toString
());
url
=
url
.
substring
(
0
,
url
.
indexOf
(
"[]"
));
}
else
{
if
(
baseObject
instanceof
IDatafield
<?>)
{
IDatafield
<
Object
>
dataField
=
(
IDatafield
<
Object
>)
baseObject
;
pars
[
i
]=
dataField
.
getValue
().
toString
();
}
else
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"交易["
).
append
(
trnName
).
append
(
"]对应的待校验字段["
);
sb
.
append
(
url
).
append
(
"]在模型中不是IDatafield类型"
);
log
.
error
(
sb
.
toString
());
}
}
IBaseObject
baseObject
=
context
.
getSession
().
getBaseObject
(
context
.
getRoot
(),
url
);
if
(
null
==
baseObject
)
{
flag
=
false
;
break
;
}
else
{
if
(
baseObject
instanceof
IDatafield
<?>)
{
IDatafield
<
Object
>
dataField
=
(
IDatafield
<
Object
>)
baseObject
;
String
val
=
dataField
.
getValue
().
toString
();
pars
[
i
]
=
val
;
}
else
if
(
baseObject
instanceof
IModuleList
<?>)
{
IModuleList
<?>
moduleList
=
(
IModuleList
<?>)
baseObject
;
String
[]
checkList
=
new
String
[
moduleList
.
size
()];
for
(
int
index
=
0
;
index
<
moduleList
.
size
();
index
++)
{
IModule
module
=
moduleList
.
get
(
index
);
String
npth
=
lstTemplate
.
replace
(
"[]"
,
"["
+
index
+
"]"
);
IDatafield
<
Object
>
dataField
=
(
IDatafield
<
Object
>)
context
.
getSession
().
getBaseObject
(
module
,
npth
);
String
val
=
dataField
.
getValue
().
toString
();
checkList
[
index
]=
val
;
}
pars
[
i
]=
String
.
join
(
"_"
,
checkList
);
}
else
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"安全配置["
).
append
(
configKey
).
append
(
"]对应的待校验字段["
);
sb
.
append
(
url
).
append
(
"]在模型中不是IDatafield类型"
);
log
.
error
(
sb
.
toString
());
}
}
}
if
(
flag
)
{
return
pars
;
}
}
}
}
return
pars
;
}
}
return
null
;
return
null
;
}
}
...
@@ -110,50 +163,66 @@ public class DataSecurityUtil {
...
@@ -110,50 +163,66 @@ public class DataSecurityUtil {
/**
/**
* 判断数据是否篡改
* 判断数据是否篡改
*
*
* @param serverEnc 服务端产生的密文串
* @param serverEnc
服务端产生的密文串
* @param clientpars 客户端待校验参数
* @param clientpars 客户端待校验参数
* @param userId 用户id
* @param userId
用户id
* @return 返回错误描述
* @return 返回错误描述
*/
*/
public
static
String
checkIllegalData
(
String
serverEnc
,
String
[]
clientpars
,
String
userId
)
{
public
static
String
checkIllegalData
(
String
serverEnc
,
String
[]
clientpars
,
String
userId
)
{
//1.判断服务端的密文串是否异常
//1.判断服务端的密文串是否异常
if
(
StringUtil
.
isEmpty
(
serverEnc
)
||
ENCRYPT_ERROR
.
equals
(
serverEnc
))
{
if
(
StringUtil
.
isEmpty
(
serverEnc
)
||
ENCRYPT_ERROR
.
equals
(
serverEnc
))
{
log
.
warn
(
ERROR_SERVERENC_NULL
);
log
.
warn
(
ERROR_SERVERENC_NULL
);
return
ERROR_SERVERENC_NULL
;
return
ERROR_SERVERENC_NULL
;
}
}
//2.用aes动态盐解密服务端密文串与客户端的密文串比对
//2.用aes动态盐解密服务端密文串与客户端的密文串比对
DynamicSalt
dynamicSalt
;
DynamicSalt
dynamicSalt
;
try
{
try
{
dynamicSalt
=
getDynamicSaltFromRedis
(
userId
);
dynamicSalt
=
getDynamicSaltFromRedis
(
userId
);
if
(
dynamicSalt
==
null
)
{
if
(
dynamicSalt
==
null
)
{
log
.
warn
(
ERROR_DYNAMICSALT_NULL
);
log
.
warn
(
ERROR_DYNAMICSALT_NULL
);
return
ERROR_DYNAMICSALT_NULL
;
return
ERROR_DYNAMICSALT_NULL
;
}
}
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
log
.
warn
(
ERROR_DYNAMICSALT_EXCEPTION
+
e
.
getMessage
());
log
.
warn
(
ERROR_DYNAMICSALT_EXCEPTION
+
e
.
getMessage
());
return
ERROR_DYNAMICSALT_EXCEPTION
;
return
ERROR_DYNAMICSALT_EXCEPTION
;
}
}
serverEnc
=
decrypt
(
serverEnc
,
dynamicSalt
);
serverEnc
=
decrypt
(
serverEnc
,
dynamicSalt
);
if
(
StringUtil
.
isEmpty
(
serverEnc
)
||
DECRYPT_ERROR
.
equals
(
serverEnc
))
{
if
(
StringUtil
.
isEmpty
(
serverEnc
)
||
DECRYPT_ERROR
.
equals
(
serverEnc
)){
log
.
warn
(
ERROR_AES_DECODE
);
log
.
warn
(
ERROR_AES_DECODE
);
return
ERROR_AES_DECODE
;
return
ERROR_AES_DECODE
;
}
}
String
clientEnc
=
preHandle
(
clientpars
);
String
clientEnc
=
preHandle
(
clientpars
);
if
(!
serverEnc
.
equals
(
clientEnc
)){
if
(
serverEnc
.
indexOf
(
"_"
)>
0
){
//集合处理
String
[]
parts
=
serverEnc
.
split
(
","
);
for
(
String
part:
parts
){
if
(!
StringUtil
.
isEmpty
(
part
)){
for
(
String
item:
part
.
split
(
"_"
)){
if
(
item
.
equals
(
clientEnc
)){
return
null
;
}
}
}
}
log
.
warn
(
ERROR_NOTMATCH
);
log
.
warn
(
ERROR_NOTMATCH
);
return
ERROR_NOTMATCH
;
return
ERROR_NOTMATCH
;
}
else
{
if
(!
serverEnc
.
equals
(
clientEnc
))
{
log
.
warn
(
ERROR_NOTMATCH
);
return
ERROR_NOTMATCH
;
}
}
}
try
{
try
{
RedisUtil
.
delete
(
getCacheSaltKey
(
userId
));
RedisUtil
.
delete
(
getCacheSaltKey
(
userId
));
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
log
.
warn
(
"删除失效盐出现异常:"
+
e
.
getMessage
());
log
.
warn
(
"删除失效盐出现异常:"
+
e
.
getMessage
());
}
}
return
null
;
return
null
;
}
}
/**
/**
* 获取指定用户缓存盐的key
* 获取指定用户缓存盐的key
*
* @param userId 用户ID
* @param userId 用户ID
* @return 返回指定用户缓存盐的key
* @return 返回指定用户缓存盐的key
*/
*/
...
@@ -167,32 +236,32 @@ public class DataSecurityUtil {
...
@@ -167,32 +236,32 @@ public class DataSecurityUtil {
return
content
==
null
?
DECRYPT_ERROR
:
content
;
return
content
==
null
?
DECRYPT_ERROR
:
content
;
}
}
private
static
String
preHandle
(
String
[]
pars
){
private
static
String
preHandle
(
String
[]
pars
)
{
if
(
ArrayUtils
.
isEmpty
(
pars
))
{
if
(
ArrayUtils
.
isEmpty
(
pars
))
{
return
null
;
return
null
;
}
}
List
<
String
>
lst
=
Arrays
.
asList
(
pars
);
List
<
String
>
lst
=
Arrays
.
asList
(
pars
);
String
md5
=
String
.
join
(
"
"
,
lst
);
String
md5
=
String
.
join
(
",
"
,
lst
);
return
md5
;
return
md5
;
}
}
private
static
DynamicSalt
getDynamicSaltFromRedis
(
String
userId
)
throws
Exception
{
private
static
DynamicSalt
getDynamicSaltFromRedis
(
String
userId
)
throws
Exception
{
String
key
=
getCacheSaltKey
(
userId
);
String
key
=
getCacheSaltKey
(
userId
);
Object
obj
=
RedisUtil
.
get
(
key
);
Object
obj
=
RedisUtil
.
get
(
key
);
DynamicSalt
rs
=
parseDynamicSalt
(
obj
);
DynamicSalt
rs
=
parseDynamicSalt
(
obj
);
if
(
rs
!=
null
)
{
if
(
rs
!=
null
)
{
return
rs
;
return
rs
;
}
}
return
null
;
return
null
;
}
}
private
static
DynamicSalt
parseDynamicSalt
(
Object
raw
){
private
static
DynamicSalt
parseDynamicSalt
(
Object
raw
)
{
if
(
raw
instanceof
String
)
{
if
(
raw
instanceof
String
)
{
String
[]
parts
=
((
String
)
raw
).
split
(
"_"
);
String
[]
parts
=
((
String
)
raw
).
split
(
"_"
);
if
(
parts
!=
null
&&
parts
.
length
==
2
)
{
if
(
parts
!=
null
&&
parts
.
length
==
2
)
{
return
new
DynamicSalt
(
parts
[
0
],
parts
[
1
]);
return
new
DynamicSalt
(
parts
[
0
],
parts
[
1
]);
}
}
}
else
if
(
raw
instanceof
DynamicSalt
)
{
}
else
if
(
raw
instanceof
DynamicSalt
)
{
return
(
DynamicSalt
)
raw
;
return
(
DynamicSalt
)
raw
;
}
}
return
null
;
return
null
;
...
@@ -222,7 +291,7 @@ public class DataSecurityUtil {
...
@@ -222,7 +291,7 @@ public class DataSecurityUtil {
@Override
@Override
public
String
toString
()
{
public
String
toString
()
{
return
iv
+
"_"
+
pwd
;
return
iv
+
"_"
+
pwd
;
}
}
}
}
}
}
src/main/resources/security.properties
View file @
98654e0b
#安全开关(ON:开;OFF:关)
#安全开关(ON:开;OFF:关)
switch
=
ON
switch
=
ON
#客户管理——查询
/dblpty/
sel_encode
=
\\
ptyp
\\
ptylst[]
\\
inr
#客户管理——修改
#客户管理——修改
dbepty
=
\\
ptygrp
\\
rec
\\
inr,
\\
ptygrp
\\
rec
\\
extkey
/dbepty/
init_decode
=
\\
ptygrp
\\
rec
\\
inr
/dbepty/
init_encode
=
\\
ptygrp
\\
rec
\\
inr,
\\
ptygrp
\\
rec
\\
extkey
/dbepty/
sav_decode
=
\\
ptygrp
\\
rec
\\
inr,
\\
ptygrp
\\
rec
\\
extkey
#客户管理——删除
#客户管理——删除
dbdpty
=
\\
ptygrp
\\
rec
\\
inr
/dbdpty/
init_decode
=
\\
ptygrp
\\
rec
\\
inr
/dbdpty/
init_encode
=
\\
ptygrp
\\
rec
\\
inr
/dbdpty/
sav_decode
=
\\
ptygrp
\\
rec
\\
inr
#
#
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment