秘钥管理改造

1.x
tanghc 6 years ago
parent ce1c18f6b2
commit 091845f684
  1. 1
      sop-admin/sop-admin-server/readme.md
  2. 124
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/IsvApi.java
  3. 18
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvInfoForm.java
  4. 9
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvInfoFormAdd.java
  5. 52
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvKeysFormUpdate.java
  6. 18
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/param/IsvKeysGen.java
  7. 11
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvAppKeyGenVO.java
  8. 34
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvDetailDTO.java
  9. 23
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvFormVO.java
  10. 14
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvInfoVO.java
  11. 25
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvKeysGenVO.java
  12. 38
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/isv/result/IsvKeysVO.java
  13. 410
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/common/RSATool.java
  14. 6
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/AdminUserInfo.java
  15. 12
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IsvInfo.java
  16. 57
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/IsvKeys.java
  17. 18
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/IsvInfoMapper.java
  18. 12
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/IsvKeysMapper.java
  19. 2
      sop-admin/sop-admin-server/src/main/resources/public/index.html
  20. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-25908fca.89ab33e8.css
  21. 0
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-29e7142c.d10599db.css
  22. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-50417551.8bd9f4f4.css
  23. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-5f00e420.aeebecd4.css
  24. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-73b2dcec.99cf6327.css
  25. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/app.3cc1fa6b.js
  26. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-25908fca.29365d11.js
  27. 4
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-29e7142c.994a3ac0.js
  28. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d221836.574c9ab6.js
  29. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-5f00e420.5e409513.js
  30. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-73b2dcec.37433095.js
  31. 21
      sop-admin/sop-admin-server/src/test/java/com/gitee/sop/adminserver/IsvInfoMapperTest.java
  32. 2
      sop-admin/sop-admin-server/src/test/java/com/gitee/sop/adminserver/PubPriKeyTest.java
  33. 2
      sop-admin/sop-admin-server/src/test/java/com/gitee/sop/adminserver/SignTest.java
  34. 2
      sop-admin/sop-admin-server/src/test/java/com/gitee/sop/adminserver/SopAdminServerApplicationTests.java
  35. 8
      sop-admin/sop-admin-vue/src/router/index.js
  36. 2
      sop-admin/sop-admin-vue/src/utils/global.js
  37. 212
      sop-admin/sop-admin-vue/src/views/isv/index.vue
  38. 172
      sop-admin/sop-admin-vue/src/views/isv/keys.vue
  39. 14
      sop-admin/sop-admin-vue/src/views/service/limit/index2.vue

@ -4,3 +4,4 @@
- 运行`SopAdminServerApplication.java`
- 访问:`http://localhost:8082`
登录账号:admin/123456

@ -8,29 +8,32 @@ import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.easyopen.util.KeyStore;
import com.gitee.easyopen.util.RSAUtil;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.IdParam;
import com.gitee.sop.adminserver.api.isv.param.IsvInfoForm;
import com.gitee.sop.adminserver.api.isv.param.IsvInfoFormAdd;
import com.gitee.sop.adminserver.api.isv.param.IsvInfoFormUpdate;
import com.gitee.sop.adminserver.api.isv.param.IsvKeysFormUpdate;
import com.gitee.sop.adminserver.api.isv.param.IsvKeysGen;
import com.gitee.sop.adminserver.api.isv.param.IsvPageParam;
import com.gitee.sop.adminserver.api.isv.result.IsvFormVO;
import com.gitee.sop.adminserver.api.isv.result.IsvVO;
import com.gitee.sop.adminserver.api.isv.result.IsvDetailDTO;
import com.gitee.sop.adminserver.api.isv.result.IsvInfoVO;
import com.gitee.sop.adminserver.api.isv.result.IsvKeysGenVO;
import com.gitee.sop.adminserver.api.isv.result.IsvKeysVO;
import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.IdGen;
import com.gitee.sop.adminserver.common.ZookeeperOperationException;
import com.gitee.sop.adminserver.common.RSATool;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import com.gitee.sop.adminserver.entity.IsvInfo;
import com.gitee.sop.adminserver.entity.IsvKeys;
import com.gitee.sop.adminserver.entity.PermIsvRole;
import com.gitee.sop.adminserver.entity.PermRole;
import com.gitee.sop.adminserver.mapper.IsvInfoMapper;
import com.gitee.sop.adminserver.mapper.IsvKeysMapper;
import com.gitee.sop.adminserver.mapper.PermIsvRoleMapper;
import com.gitee.sop.adminserver.mapper.PermRoleMapper;
import com.gitee.sop.adminserver.service.RoutePermissionService;
@ -39,6 +42,7 @@ import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotBlank;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
@ -57,6 +61,9 @@ public class IsvApi {
@Autowired
IsvInfoMapper isvInfoMapper;
@Autowired
IsvKeysMapper isvKeysMapper;
@Autowired
PermIsvRoleMapper permIsvRoleMapper;
@ -71,20 +78,20 @@ public class IsvApi {
@ApiDocField(name = "pageIndex", description = "第几页", dataType = DataType.INT, example = "1"),
@ApiDocField(name = "pageSize", description = "每页几条数据", dataType = DataType.INT, example = "10"),
@ApiDocField(name = "total", description = "每页几条数据", dataType = DataType.LONG, example = "100"),
@ApiDocField(name = "rows", description = "数据", dataType = DataType.ARRAY, elementClass = IsvVO.class)
@ApiDocField(name = "rows", description = "数据", dataType = DataType.ARRAY, elementClass = IsvInfoVO.class)
})
PageInfo<IsvVO> pageIsv(IsvPageParam param) {
PageInfo<IsvInfoVO> pageIsv(IsvPageParam param) {
Query query = Query.build(param);
PageInfo<IsvInfo> pageInfo = MapperUtil.query(isvInfoMapper, query);
List<IsvInfo> list = pageInfo.getList();
List<IsvVO> retList = list.stream()
List<IsvInfoVO> retList = list.stream()
.map(isvInfo -> {
return buildIsvVO(isvInfo);
})
.collect(Collectors.toList());
PageInfo<IsvVO> pageInfoRet = new PageInfo<>();
PageInfo<IsvInfoVO> pageInfoRet = new PageInfo<>();
pageInfoRet.setTotal(pageInfo.getTotal());
pageInfoRet.setList(retList);
@ -93,16 +100,30 @@ public class IsvApi {
@Api(name = "isv.info.get")
@ApiDocMethod(description = "获取isv")
IsvVO getIsvVO(IdParam param) {
IsvInfoVO getIsvVO(IdParam param) {
IsvInfo isvInfo = isvInfoMapper.getById(param.getId());
return buildIsvVO(isvInfo);
}
private IsvVO buildIsvVO(IsvInfo isvInfo) {
@Api(name = "isv.keys.get")
@ApiDocMethod(description = "获取isv2")
IsvKeysVO getIsvKeys(@NotBlank(message = "appKey不能为空")
@ApiDocField(description = "appKey")
String appKey) {
IsvKeys isvKeys = isvKeysMapper.getByColumn("app_key", appKey);
IsvKeysVO isvDetailVO = new IsvKeysVO();
if (isvKeys != null) {
CopyUtil.copyProperties(isvKeys, isvDetailVO);
}
isvDetailVO.setAppKey(appKey);
return isvDetailVO;
}
private IsvInfoVO buildIsvVO(IsvInfo isvInfo) {
if (isvInfo == null) {
return null;
}
IsvVO vo = new IsvVO();
IsvInfoVO vo = new IsvInfoVO();
CopyUtil.copyProperties(isvInfo, vo);
vo.setRoleList(this.buildIsvRole(isvInfo));
return vo;
@ -134,44 +155,57 @@ public class IsvApi {
@ApiDocMethod(description = "添加isv")
@Transactional(rollbackFor = Exception.class)
public void addIsv(IsvInfoFormAdd param) throws Exception {
if (isvInfoMapper.getByColumn("app_key", param.getAppKey()) != null) {
throw new BizException("appKey已存在");
}
formatForm(param);
String appKey = new SimpleDateFormat("yyyyMMdd").format(new Date()) + IdGen.nextId();
IsvInfo rec = new IsvInfo();
rec.setAppKey(appKey);
CopyUtil.copyPropertiesIgnoreNull(param, rec);
isvInfoMapper.saveIgnoreNull(rec);
if (CollectionUtils.isNotEmpty(param.getRoleCode())) {
this.saveIsvRole(rec, param.getRoleCode());
}
IsvKeysGenVO isvKeysGenVO = this.createIsvKeys();
IsvKeys isvKeys = new IsvKeys();
CopyUtil.copyPropertiesIgnoreNull(isvKeysGenVO, isvKeys);
isvKeysMapper.saveIgnoreNull(isvKeys);
this.sendChannelMsg(rec);
this.sendChannelMsg(rec.getAppKey());
}
@Api(name = "isv.info.update")
@ApiDocMethod(description = "修改isv")
@Transactional(rollbackFor = Exception.class)
public void updateIsv(IsvInfoFormUpdate param) {
formatForm(param);
IsvInfo rec = isvInfoMapper.getById(param.getId());
CopyUtil.copyPropertiesIgnoreNull(param, rec);
isvInfoMapper.updateIgnoreNull(rec);
this.saveIsvRole(rec, param.getRoleCode());
this.sendChannelMsg(rec);
this.sendChannelMsg(rec.getAppKey());
}
private void formatForm(IsvInfoForm form) {
if (form.getSignType() == SIGN_TYPE_RSA2) {
form.setSecret("");
@Api(name = "isv.keys.update")
@ApiDocMethod(description = "修改isv")
@Transactional(rollbackFor = Exception.class)
public void updateIsvKeys(IsvKeysFormUpdate param) {
IsvKeys isvKeys = isvKeysMapper.getByColumn("app_key", param.getAppKey());
if (isvKeys == null) {
isvKeys = new IsvKeys();
CopyUtil.copyPropertiesIgnoreNull(param, isvKeys);
isvKeysMapper.saveIgnoreNull(isvKeys);
} else {
form.setPubKey("");
form.setPriKey("");
CopyUtil.copyPropertiesIgnoreNull(param, isvKeys);
isvKeysMapper.updateIgnoreNull(isvKeys);
}
this.sendChannelMsg(isvKeys.getAppKey());
}
private void sendChannelMsg(IsvInfo rec) {
ChannelMsg channelMsg = new ChannelMsg("update", rec);
private void sendChannelMsg(String appKey) {
IsvDetailDTO isvDetail = isvInfoMapper.getIsvDetail(appKey);
if (isvDetail == null) {
return;
}
ChannelMsg channelMsg = new ChannelMsg("update", isvDetail);
String path = ZookeeperContext.getIsvInfoChannelPath();
String data = JSON.toJSONString(channelMsg);
try {
@ -183,23 +217,41 @@ public class IsvApi {
}
}
@Api(name = "isv.form.gen")
@ApiDocMethod(description = "isv表单内容一键生成")
IsvFormVO createIsvForm() throws Exception {
IsvFormVO isvFormVO = new IsvFormVO();
String appKey = new SimpleDateFormat("yyyyMMdd").format(new Date()) + IdGen.nextId();
private IsvKeysGenVO createIsvKeys() throws Exception {
IsvKeysGenVO isvFormVO = new IsvKeysGenVO();
String secret = IdGen.uuid();
isvFormVO.setAppKey(appKey);
isvFormVO.setSecret(secret);
KeyStore keyStore = RSAUtil.createKeys();
isvFormVO.setPubKey(keyStore.getPublicKey());
isvFormVO.setPriKey(keyStore.getPrivateKey());
RSATool rsaToolIsv = new RSATool(RSATool.KeyFormat.PKCS8, RSATool.KeyLength.LENGTH_2048);
RSATool.KeyStore keyStoreIsv = rsaToolIsv.createKeys();
isvFormVO.setPublicKeyIsv(keyStoreIsv.getPublicKey());
isvFormVO.setPrivateKeyIsv(keyStoreIsv.getPrivateKey());
RSATool rsaToolPlatform = new RSATool(RSATool.KeyFormat.PKCS8, RSATool.KeyLength.LENGTH_2048);
RSATool.KeyStore keyStorePlatform = rsaToolPlatform.createKeys();
isvFormVO.setPublicKeyPlatform(keyStorePlatform.getPublicKey());
isvFormVO.setPrivateKeyPlatform(keyStorePlatform.getPrivateKey());
return isvFormVO;
}
@Api(name = "isv.keys.gen")
@ApiDocMethod(description = "生成公私钥")
RSATool.KeyStore createPubPriKey(IsvKeysGen param) throws Exception {
RSATool.KeyFormat format = RSATool.KeyFormat.PKCS8;
Byte keyFormat = param.getKeyFormat();
if (keyFormat != null && keyFormat == 2) {
format = RSATool.KeyFormat.PKCS1;
}
RSATool rsaTool = new RSATool(format, RSATool.KeyLength.LENGTH_2048);
return rsaTool.createKeys();
}
@Api(name = "isv.secret.gen")
@ApiDocMethod(description = "生成MD秘钥")
String createSecret() throws Exception {
return IdGen.uuid();
}
void saveIsvRole(IsvInfo isvInfo, List<String> roleCodeList) {
Query query = new Query();

@ -2,9 +2,7 @@ package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.List;
@ -14,22 +12,6 @@ import java.util.List;
@Data
public class IsvInfoForm {
@NotNull(message = "signType不能为null")
private Byte signType;
/** secret, 数据库字段:secret */
@ApiDocField(description = "secret")
@Length(max = 100,message = "secret长度不能超过100")
private String secret = "";
/** 公钥, 数据库字段:pub_key */
@ApiDocField(description = "pubKey")
private String pubKey;
/** 私钥, 数据库字段:pri_key */
@ApiDocField(description = "priKey")
private String priKey;
/** 0启用,1禁用, 数据库字段:status */
@ApiDocField(description = "状态:0:启用,1:禁用")
private Byte status = 0;

@ -1,11 +1,7 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* @author tanghc
@ -13,9 +9,4 @@ import javax.validation.constraints.NotBlank;
@Getter
@Setter
public class IsvInfoFormAdd extends IsvInfoForm {
/** appKey, 数据库字段:app_key */
@ApiDocField(description = "appKey", required = true)
@NotBlank(message = "appKey不能为空")
@Length(max = 100,message = "appKey长度不能超过100")
private String appKey;
}

@ -0,0 +1,52 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author tanghc
*/
@Data
public class IsvKeysFormUpdate {
/** appKey, 数据库字段:app_key */
@ApiDocField(description = "appKey", example = "aaaa")
@NotBlank(message = "appKey不能为空")
@Length(max = 100,message = "appKey长度不能超过100")
private String appKey;
/** secret, 数据库字段:secret */
@ApiDocField(description = "secret", example = "bbbb")
private String secret;
/** 秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用), 数据库字段:key_format */
@ApiDocField(description = "秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用)", example = "1")
@Min(value = 1, message = "秘钥格式错误")
@Max(value = 2, message = "秘钥格式错误")
private Byte keyFormat;
/** 开发者生成的公钥, 数据库字段:public_key_isv */
@ApiDocField(description = "开发者生成的公钥")
private String publicKeyIsv;
/** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */
@ApiDocField(description = "开发者生成的私钥")
private String privateKeyIsv;
/** 平台生成的公钥(交给开发者), 数据库字段:public_key_platform */
@ApiDocField(description = "平台生成的公钥")
private String publicKeyPlatform;
/** 平台生成的私钥, 数据库字段:private_key_platform */
@ApiDocField(description = "平台生成的私钥")
private String privateKeyPlatform;
@ApiDocField(description = "签名类型:1:RSA2,2:MD5")
@NotNull(message = "signType不能为空")
private Byte signType;
}

@ -0,0 +1,18 @@
package com.gitee.sop.adminserver.api.isv.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
/**
* @author tanghc
*/
@Data
public class IsvKeysGen {
@ApiDocField(description = "秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用)", example = "1")
@Min(value = 1, message = "秘钥格式错误")
@Max(value = 2, message = "秘钥格式错误")
private Byte keyFormat;
}

@ -0,0 +1,11 @@
package com.gitee.sop.adminserver.api.isv.result;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class IsvAppKeyGenVO {
private String appKey;
}

@ -0,0 +1,34 @@
package com.gitee.sop.adminserver.api.isv.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class IsvDetailDTO {
/** appKey, 数据库字段:app_key */
@ApiDocField(description = "appKey", example = "aaaa")
private String appKey;
/** 0启用,1禁用, 数据库字段:status */
@ApiDocField(description = "状态:0启用,1禁用")
private Byte status;
// keys
/** secret, 数据库字段:secret */
@ApiDocField(description = "secret", example = "bbbb")
private String secret;
/** 开发者生成的公钥, 数据库字段:public_key_isv */
private String publicKeyIsv;
/** 平台生成的私钥, 数据库字段:private_key_platform */
private String privateKeyPlatform;
@ApiDocField(description = "签名类型:1:RSA2,2:MD5")
private Byte signType;
}

@ -1,23 +0,0 @@
package com.gitee.sop.adminserver.api.isv.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class IsvFormVO {
@ApiDocField(description = "appKey", example = "aaaa")
private String appKey;
@ApiDocField(description = "secret", example = "bbbb")
private String secret;
@ApiDocField(description = "pubKey")
private String pubKey;
@ApiDocField(description = "priKey")
private String priKey;
}

@ -10,7 +10,7 @@ import java.util.List;
* @author thc
*/
@Data
public class IsvVO {
public class IsvInfoVO {
/** 数据库字段:id */
@ApiDocField(description = "id", example = "1")
private Long id;
@ -19,18 +19,6 @@ public class IsvVO {
@ApiDocField(description = "appKey", example = "aaaa")
private String appKey;
/** secret, 数据库字段:secret */
@ApiDocField(description = "secret", example = "bbbb")
private String secret;
/** 公钥, 数据库字段:pub_key */
@ApiDocField(description = "pubKey")
private String pubKey;
/** 私钥, 数据库字段:pri_key */
@ApiDocField(description = "priKey")
private String priKey;
/** 0启用,1禁用, 数据库字段:status */
@ApiDocField(description = "状态:0启用,1禁用")
private Byte status;

@ -0,0 +1,25 @@
package com.gitee.sop.adminserver.api.isv.result;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class IsvKeysGenVO {
/** secret, 数据库字段:secret */
private String secret;
/** 开发者生成的公钥, 数据库字段:public_key_isv */
private String publicKeyIsv;
/** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */
private String privateKeyIsv;
/** 平台生成的公钥(交给开发者), 数据库字段:public_key_platform */
private String publicKeyPlatform;
/** 平台生成的私钥, 数据库字段:private_key_platform */
private String privateKeyPlatform;
}

@ -0,0 +1,38 @@
package com.gitee.sop.adminserver.api.isv.result;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import lombok.Data;
/**
* @author thc
*/
@Data
public class IsvKeysVO {
/** appKey, 数据库字段:app_key */
@ApiDocField(description = "appKey", example = "aaaa")
private String appKey;
/** secret, 数据库字段:secret */
@ApiDocField(description = "secret", example = "bbbb")
private String secret;
/** 秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用), 数据库字段:key_format */
private Byte keyFormat = 1;
/** 开发者生成的公钥, 数据库字段:public_key_isv */
private String publicKeyIsv;
/** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */
private String privateKeyIsv;
/** 平台生成的公钥(交给开发者), 数据库字段:public_key_platform */
private String publicKeyPlatform;
/** 平台生成的私钥, 数据库字段:private_key_platform */
private String privateKeyPlatform;
@ApiDocField(description = "签名类型:1:RSA2,2:MD5")
private Byte signType = 1;
}

@ -0,0 +1,410 @@
package com.gitee.sop.adminserver.common;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA加解密工具<br>
*
* @author tanghc
*/
public class RSATool {
public static String RSA_ALGORITHM = "RSA";
private KeyFormat keyFormat;
private KeyLength keyLength;
public RSATool(KeyFormat keyFormat, KeyLength keyLength) {
this.keyFormat = keyFormat;
this.keyLength = keyLength;
}
/**
* 创建公钥私钥
*
* @return 返回公私钥对
* @throws Exception
*/
public KeyStore createKeys() throws Exception {
KeyPairGenerator keyPairGeno = KeyPairGenerator.getInstance(RSA_ALGORITHM);
keyPairGeno.initialize(keyLength.getLength());
KeyPair keyPair = keyPairGeno.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
KeyStore keyStore = new KeyStore();
if (this.keyFormat == KeyFormat.PKCS1) {
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded()));
keyStore.setPrivateKey(convertPkcs8ToPkcs1(privateKey.getEncoded()));
} else {
keyStore.setPublicKey(Base64.encodeBase64String(publicKey.getEncoded()));
keyStore.setPrivateKey(Base64.encodeBase64String(privateKey.getEncoded()));
}
return keyStore;
}
/**
* 获取公钥对象
*
* @param pubKeyData 公钥
* @return 返回公钥对象
* @throws Exception
*/
public RSAPublicKey getPublicKey(byte[] pubKeyData) throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyData);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
/**
* 获取公钥对象
*
* @param pubKey 公钥
* @return 返回私钥对象
* @throws Exception
*/
public RSAPublicKey getPublicKey(String pubKey) throws Exception {
return getPublicKey(Base64.decodeBase64(pubKey));
}
/**
* 获取私钥对象
*
* @param priKey 私钥
* @return 私钥对象
* @throws Exception
*/
public RSAPrivateKey getPrivateKey(String priKey) throws Exception {
return getPrivateKey(Base64.decodeBase64(priKey));
}
/**
* 通过私钥byte[]将公钥还原适用于RSA算法
*
* @param keyBytes
* @return 返回私钥
* @throws Exception
*/
public RSAPrivateKey getPrivateKey(byte[] keyBytes) throws Exception {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
/**
* 公钥加密
*
* @param data 待加密内容
* @param publicKey 公钥
* @return 返回密文
* @throws Exception
*/
public String encryptByPublicKey(String data, RSAPublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 模长
int key_len = publicKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
// 如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
public String encryptByPrivateKey(String data, String privateKey) throws Exception {
return encryptByPrivateKey(data, getPrivateKey(privateKey));
}
/**
* 私钥加密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 返回密文
* @throws Exception
*/
public String encryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
// 模长
int key_len = privateKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
// 如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
public String decryptByPrivateKey(String data, String privateKey) throws Exception {
return decryptByPrivateKey(data, getPrivateKey(privateKey));
}
/**
* 私钥解密
*
* @param data 待解密内容
* @param privateKey 私钥
* @return 返回明文
* @throws Exception
*/
public String decryptByPrivateKey(String data, RSAPrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 模长
int key_len = privateKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
// 如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for (byte[] arr : arrays) {
ming += new String(cipher.doFinal(arr));
}
return ming;
}
/**
* 公钥解密
*
* @param data 待解密内容
* @param rsaPublicKey 公钥
* @return 返回明文
* @throws Exception
*/
public String decryptByPublicKey(String data, RSAPublicKey rsaPublicKey) throws Exception {
Cipher cipher = Cipher.getInstance(keyFormat.getCipherAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, rsaPublicKey);
// 模长
int key_len = rsaPublicKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
// 如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for (byte[] arr : arrays) {
ming += new String(cipher.doFinal(arr));
}
return ming;
}
public static String convertPkcs8ToPkcs1(byte[] privateKeyData) throws Exception{
PrivateKeyInfo pkInfo = PrivateKeyInfo.getInstance(privateKeyData);
ASN1Encodable encodable = pkInfo.parsePrivateKey();
ASN1Primitive primitive = encodable.toASN1Primitive();
byte[] privateKeyPKCS1 = primitive.getEncoded();
return Base64.encodeBase64String(privateKeyPKCS1);
}
/**
* ASCII码转BCD码
*/
public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
byte[] bcd = new byte[asc_len / 2];
int j = 0;
for (int i = 0; i < (asc_len + 1) / 2; i++) {
bcd[i] = asc_to_bcd(ascii[j++]);
bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++]) & 0xff) + (bcd[i] << 4));
}
return bcd;
}
public static byte asc_to_bcd(byte asc) {
byte bcd;
if ((asc >= '0') && (asc <= '9')) {
bcd = (byte) (asc - '0');
} else if ((asc >= 'A') && (asc <= 'F')) {
bcd = (byte) (asc - 'A' + 10);
} else if ((asc >= 'a') && (asc <= 'f')) {
bcd = (byte) (asc - 'a' + 10);
} else {
bcd = (byte) (asc - 48);
}
return bcd;
}
/**
* BCD转字符串
*/
public String bcd2Str(byte[] bytes) {
char[] temp = new char[bytes.length * 2];
char val;
for (int i = 0; i < bytes.length; i++) {
val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
val = (char) (bytes[i] & 0x0f);
temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
}
return new String(temp);
}
/**
* 拆分字符串
*/
public String[] splitString(String string, int len) {
int x = string.length() / len;
int y = string.length() % len;
int z = 0;
if (y != 0) {
z = 1;
}
String[] strings = new String[x + z];
String str = "";
for (int i = 0; i < x + z; i++) {
if (i == x + z - 1 && y != 0) {
str = string.substring(i * len, i * len + y);
} else {
str = string.substring(i * len, i * len + len);
}
strings[i] = str;
}
return strings;
}
/**
* 拆分数组
*/
public byte[][] splitArray(byte[] data, int len) {
int x = data.length / len;
int y = data.length % len;
int z = 0;
if (y != 0) {
z = 1;
}
byte[][] arrays = new byte[x + z][];
byte[] arr;
for (int i = 0; i < x + z; i++) {
arr = new byte[len];
if (i == x + z - 1 && y != 0) {
System.arraycopy(data, i * len, arr, 0, y);
} else {
System.arraycopy(data, i * len, arr, 0, len);
}
arrays[i] = arr;
}
return arrays;
}
public enum KeyLength {
LENGTH_1024(1024), LENGTH_2048(2048);
private int length;
KeyLength(int length) {
this.length = length;
}
public int getLength() {
return length;
}
}
public static class KeyStore {
private String publicKey;
private String privateKey;
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
}
public enum KeyFormat {
PKCS1("RSA/ECB/PKCS1Padding"), PKCS8("RSA");
private String cipherAlgorithm;
KeyFormat(String cipherAlgorithm) {
this.cipherAlgorithm = cipherAlgorithm;
}
public String getCipherAlgorithm() {
return cipherAlgorithm;
}
}
public KeyFormat getKeyFormat() {
return keyFormat;
}
public KeyLength getKeyLength() {
return keyLength;
}
/* ------------ Test ------------
public static void main(String[] args) throws Exception {
RSATool rsa_pkcs8_1024 = new RSATool(KeyFormat.PKCS8, KeyLength.LENGTH_1024);
RSATool rsa_pkcs8_2048 = new RSATool(KeyFormat.PKCS8, KeyLength.LENGTH_2048);
doTest(rsa_pkcs8_1024);
doTest(rsa_pkcs8_2048);
// PKCS在Java环境无法测试,可以生成一对,到非java平台测试,如C#
}
private static void doTest(RSATool rsaTool) throws Exception {
System.out.println("秘钥格式:" + rsaTool.keyFormat.name() + ", 秘钥长度:" + rsaTool.keyLength.getLength());
KeyStore keys = rsaTool.createKeys();
String pubKey = keys.getPublicKey();
System.out.println("pubKey:");
System.out.println(pubKey);
String priKey = keys.getPrivateKey();
System.out.println("priKey:");
System.out.println(priKey);
System.out.println("--------");
String ming = "你好,abc123~!@=";
// 用公钥加密
String mi = rsaTool.encryptByPublicKey(ming, rsaTool.getPublicKey(pubKey));
System.out.println("mi : " + mi);
// 用私钥解密
String ming2 = rsaTool.decryptByPrivateKey(mi, rsaTool.getPrivateKey(priKey));
System.out.println("ming : " + ming2 + ", 结果:" + ming2.equals(ming));
// 用私钥加密
String mi2 = rsaTool.encryptByPrivateKey(ming, rsaTool.getPrivateKey(priKey));
System.out.println("mi2 : " + mi2);
// 用公钥解密
String ming3 = rsaTool.decryptByPublicKey(mi2, rsaTool.getPublicKey(pubKey));
System.out.println("ming3 : " + ming3 + ", 结果:" + ming3.equals(ming));
System.out.println("---------------------");
}
*/
}

@ -2,13 +2,13 @@ package com.gitee.sop.adminserver.entity;
import lombok.Data;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Date;
/**
@ -19,7 +19,7 @@ import javax.persistence.Table;
*/
@Table(name = "admin_user_info")
@Data
public class AdminUserInfo {
public class AdminUserInfo implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)

@ -28,21 +28,9 @@ public class IsvInfo {
/** appKey, 数据库字段:app_key */
private String appKey;
/** secret, 数据库字段:secret */
private String secret;
/** 公钥, 数据库字段:pub_key */
private String pubKey;
/** 私钥, 数据库字段:pri_key */
private String priKey;
/** 1启用,2禁用, 数据库字段:status */
private Byte status;
/** 1:RSA2,2:MD5, 数据库字段:sign_type */
private Byte signType;
/** 数据库字段:gmt_create */
private Date gmtCreate;

@ -0,0 +1,57 @@
package com.gitee.sop.adminserver.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* 表名isv_keys
* 备注ISV秘钥
*
* @author tanghc
*/
@Table(name = "isv_keys")
@Data
public class IsvKeys {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
/** 数据库字段:id */
private Long id;
/** 数据库字段:app_key */
private String appKey;
/** 1:RSA2,2:MD5, 数据库字段:sign_type */
private Byte signType;
/** isv_info.sign_type=2时使用, 数据库字段:secret */
private String secret;
/** 秘钥格式,1:PKCS8(JAVA适用),2:PKCS1(非JAVA适用), 数据库字段:key_format */
private Byte keyFormat;
/** 开发者生成的公钥, 数据库字段:public_key_isv */
private String publicKeyIsv;
/** 开发者生成的私钥(交给开发者), 数据库字段:private_key_isv */
private String privateKeyIsv;
/** 平台生成的公钥(交给开发者), 数据库字段:public_key_platform */
private String publicKeyPlatform;
/** 平台生成的私钥, 数据库字段:private_key_platform */
private String privateKeyPlatform;
/** 数据库字段:gmt_create */
private Date gmtCreate;
/** 数据库字段:gmt_modified */
private Date gmtModified;
}

@ -2,11 +2,29 @@ package com.gitee.sop.adminserver.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.adminserver.api.isv.result.IsvDetailDTO;
import com.gitee.sop.adminserver.entity.IsvInfo;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* @author tanghc
*/
public interface IsvInfoMapper extends CrudMapper<IsvInfo, Long> {
@Select("SELECT " +
" t.app_key appKey " +
" ,t.status " +
" ,t2.sign_type signType " +
" ,t2.secret " +
" ,t2.public_key_isv publicKeyIsv " +
" ,t2.private_key_isv privateKeyIsv " +
" ,t2.public_key_platform publicKeyPlatform " +
" ,t2.private_key_platform privateKeyPlatform " +
"FROM isv_info t " +
"INNER JOIN isv_keys t2 ON t.app_key = t2.app_key " +
"WHERE t.app_key = #{appKey}")
IsvDetailDTO getIsvDetail(@Param("appKey") String appKey);
}

@ -0,0 +1,12 @@
package com.gitee.sop.adminserver.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.adminserver.entity.IsvKeys;
/**
* @author tanghc
*/
public interface IsvKeysMapper extends CrudMapper<IsvKeys, Long> {
}

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,o,c=t[0],i=t[1],f=t[2],l=0,s=[];l<c.length;l++)o=c[l],u[o]&&s.push(u[o][0]),u[o]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);d&&d(t);while(s.length)s.shift()();return a.push.apply(a,f||[]),n()}function n(){for(var e,t=0;t<a.length;t++){for(var n=a[t],r=!0,o=1;o<n.length;o++){var c=n[o];0!==u[c]&&(r=!1)}r&&(a.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},o={runtime:0},u={runtime:0},a=[];function c(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-238a81e9":"5955f13d","chunk-2d221836":"574c9ab6","chunk-2d22c2e3":"46f37153","chunk-37401378":"4e39ec9b","chunk-4a59cbe4":"b5e74cc4","chunk-50417551":"ea8ad933","chunk-6a68a33e":"f59ae895","chunk-ea2e58a4":"f3f85b0e"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-238a81e9":1,"chunk-37401378":1,"chunk-50417551":1,"chunk-6a68a33e":1,"chunk-ea2e58a4":1};o[e]?t.push(o[e]):0!==o[e]&&n[e]&&t.push(o[e]=new Promise(function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-238a81e9":"e8e2beee","chunk-2d221836":"31d6cfe0","chunk-2d22c2e3":"31d6cfe0","chunk-37401378":"a43114f3","chunk-4a59cbe4":"31d6cfe0","chunk-50417551":"8bd9f4f4","chunk-6a68a33e":"3b12267b","chunk-ea2e58a4":"d10599db"}[e]+".css",u=i.p+r,a=document.getElementsByTagName("link"),c=0;c<a.length;c++){var f=a[c],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===u))return t()}var s=document.getElementsByTagName("style");for(c=0;c<s.length;c++){f=s[c],l=f.getAttribute("data-href");if(l===r||l===u)return t()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=t,d.onerror=function(t){var r=t&&t.target&&t.target.src||u,a=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");a.code="CSS_CHUNK_LOAD_FAILED",a.request=r,delete o[e],d.parentNode.removeChild(d),n(a)},d.href=u;var h=document.getElementsByTagName("head")[0];h.appendChild(d)}).then(function(){o[e]=0}));var r=u[e];if(0!==r)if(r)t.push(r[2]);else{var a=new Promise(function(t,n){r=u[e]=[t,n]});t.push(r[2]=a);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=c(e),f=function(t){l.onerror=l.onload=null,clearTimeout(s);var n=u[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src,a=new Error("Loading chunk "+e+" failed.\n("+r+": "+o+")");a.type=r,a.request=o,n[1](a)}u[e]=void 0}};var s=setTimeout(function(){f({type:"timeout",target:l})},12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var s=0;s<f.length;s++)t(f[s]);var d=l;n()})([]);</script><script src=static/js/chunk-elementUI.8ebdfbab.js></script><script src=static/js/chunk-libs.9cf9cc40.js></script><script src=static/js/app.3e92351d.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,c,a=t[0],i=t[1],f=t[2],l=0,h=[];l<a.length;l++)c=a[l],u[c]&&h.push(u[c][0]),u[c]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);s&&s(t);while(h.length)h.shift()();return o.push.apply(o,f||[]),n()}function n(){for(var e,t=0;t<o.length;t++){for(var n=o[t],r=!0,c=1;c<n.length;c++){var a=n[c];0!==u[a]&&(r=!1)}r&&(o.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-238a81e9":"5955f13d","chunk-25908fca":"29365d11","chunk-29e7142c":"994a3ac0","chunk-2d22c2e3":"46f37153","chunk-37401378":"4e39ec9b","chunk-4a59cbe4":"b5e74cc4","chunk-5f00e420":"5e409513","chunk-6a68a33e":"f59ae895","chunk-73b2dcec":"37433095"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-238a81e9":1,"chunk-25908fca":1,"chunk-29e7142c":1,"chunk-37401378":1,"chunk-5f00e420":1,"chunk-6a68a33e":1,"chunk-73b2dcec":1};c[e]?t.push(c[e]):0!==c[e]&&n[e]&&t.push(c[e]=new Promise(function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-238a81e9":"e8e2beee","chunk-25908fca":"89ab33e8","chunk-29e7142c":"d10599db","chunk-2d22c2e3":"31d6cfe0","chunk-37401378":"a43114f3","chunk-4a59cbe4":"31d6cfe0","chunk-5f00e420":"aeebecd4","chunk-6a68a33e":"3b12267b","chunk-73b2dcec":"99cf6327"}[e]+".css",u=i.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var f=o[a],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===u))return t()}var h=document.getElementsByTagName("style");for(a=0;a<h.length;a++){f=h[a],l=f.getAttribute("data-href");if(l===r||l===u)return t()}var s=document.createElement("link");s.rel="stylesheet",s.type="text/css",s.onload=t,s.onerror=function(t){var r=t&&t.target&&t.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],s.parentNode.removeChild(s),n(o)},s.href=u;var d=document.getElementsByTagName("head")[0];d.appendChild(s)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)t.push(r[2]);else{var o=new Promise(function(t,n){r=u[e]=[t,n]});t.push(r[2]=o);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=a(e),f=function(t){l.onerror=l.onload=null,clearTimeout(h);var n=u[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),c=t&&t.target&&t.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,n[1](o)}u[e]=void 0}};var h=setTimeout(function(){f({type:"timeout",target:l})},12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var h=0;h<f.length;h++)t(f[h]);var s=l;n()})([]);</script><script src=static/js/chunk-elementUI.8ebdfbab.js></script><script src=static/js/chunk-libs.9cf9cc40.js></script><script src=static/js/app.3cc1fa6b.js></script></body></html>

@ -0,0 +1 @@
.gen-key{margin-bottom:0!important}fieldset{border:1px solid #ccc;color:grey;margin-left:40px;margin-bottom:20px}fieldset label{width:110px!important}fieldset .el-form-item__content{margin-left:110px!important}.key-view .el-form-item{margin-bottom:10px!important}

@ -1 +0,0 @@
.limit-tip[data-v-9b075514]{cursor:pointer;margin-left:10px}

@ -0,0 +1 @@
.custom-tree-node{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;padding-right:8px}.el-input.is-disabled .el-input__inner,.el-radio__input.is-disabled+span.el-radio__label{color:#909399}.limit-tip[data-v-0ec28e9a]{cursor:pointer;margin-left:10px}

@ -0,0 +1 @@
.gen-key{margin-bottom:0!important}fieldset{border:1px solid #ccc;color:grey;margin-left:40px;margin-bottom:20px}fieldset label{width:110px!important}fieldset .el-form-item__content{margin-left:110px!important}

@ -0,0 +1,21 @@
package com.gitee.sop.adminserver;
import com.gitee.sop.adminserver.api.isv.result.IsvDetailDTO;
import com.gitee.sop.adminserver.mapper.IsvInfoMapper;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author tanghc
*/
public class IsvInfoMapperTest extends SopAdminServerApplicationTests {
@Autowired
IsvInfoMapper isvInfoMapper;
@Test
public void testGet() {
IsvDetailDTO isvDetail = isvInfoMapper.getIsvDetail("2019032617262200001");
System.out.println(isvDetail);
}
}

@ -1,4 +1,4 @@
package com.gitee.durcframework.easyopen.server;
package com.gitee.sop.adminserver;
import java.io.IOException;
import java.util.HashMap;

@ -92,12 +92,20 @@ export const constantRoutes = [
path: '/isv',
component: Layout,
meta: { title: 'ISV管理', icon: 'user' },
redirect: '/isv/list',
children: [
{
path: 'list',
name: 'IsvList',
component: () => import('@/views/isv/index'),
meta: { title: 'ISV列表' }
},
{
path: 'keys',
name: 'Keys',
component: () => import('@/views/isv/keys'),
hidden: true,
meta: { title: '秘钥管理' }
}
]
},

@ -8,7 +8,7 @@ import { getToken, removeToken } from './auth'
// 创建axios实例
const client = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // api 的 base_url
timeout: 10000 // 请求超时时间
timeout: 60000 // 请求超时时间,60秒
})
Object.assign(Vue.prototype, {

@ -25,32 +25,13 @@
label="appKey"
width="250"
/>
<el-table-column
prop="secret"
label="secret"
width="80"
>
<template slot-scope="scope">
<el-button v-if="scope.row.signType === 2" type="text" size="mini" @click="onShowSecret(scope.row)">查看</el-button>
</template>
</el-table-column>
<el-table-column
prop=""
label="公私钥"
width="80"
>
<template slot-scope="scope">
<el-button v-if="scope.row.signType === 1" type="text" size="mini" @click="onShowPriPubKey(scope.row)">查看</el-button>
</template>
</el-table-column>
<el-table-column
prop="signType"
label="签名类型"
label="秘钥"
width="80"
>
<template slot-scope="scope">
<span v-if="scope.row.signType === 1">RSA2</span>
<span v-if="scope.row.signType === 2">MD5</span>
<el-button type="text" size="mini" @click="onShowKeys(scope.row)">查看</el-button>
</template>
</el-table-column>
<el-table-column
@ -83,11 +64,11 @@
/>
<el-table-column
label="操作"
fixed="right"
width="100"
width="150"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button>
<el-button type="text" size="mini" @click="onKeysUpdate(scope.row)">秘钥管理</el-button>
</template>
</el-table-column>
</el-table>
@ -116,26 +97,9 @@
label-width="120px"
size="mini"
>
<el-form-item label="">
<el-button size="mini" @click="onDataGen">一键生成数据</el-button>
</el-form-item>
<el-form-item prop="appKey" label="appKey">
<el-input v-model="isvDialogFormData.appKey" size="mini" />
</el-form-item>
<el-form-item prop="signType" label="签名方式">
<el-radio-group v-model="isvDialogFormData.signType">
<el-radio :label="1" name="status">RSA2</el-radio>
<el-radio :label="2" name="status">MD5</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-show="isvDialogFormData.signType === 2" prop="secret" label="secret">
<el-input v-model="isvDialogFormData.secret" size="mini" />
</el-form-item>
<el-form-item v-show="isvDialogFormData.signType === 1" prop="pubKey" label="公钥">
<el-input v-model="isvDialogFormData.pubKey" type="textarea" />
</el-form-item>
<el-form-item v-show="isvDialogFormData.signType === 1" prop="priKey" label="私钥">
<el-input v-model="isvDialogFormData.priKey" type="textarea" />
<el-form-item label="appKey">
<span v-if="isvDialogFormData.id === 0" style="color: gray;">(系统自动生成)</span>
<span v-else>{{ isvDialogFormData.appKey }}</span>
</el-form-item>
<el-form-item label="角色">
<el-checkbox-group v-model="isvDialogFormData.roleCode">
@ -154,31 +118,75 @@
<el-button type="primary" @click="onIsvDialogSave"> </el-button>
</div>
</el-dialog>
<!--view keys dialog-->
<el-dialog
title="秘钥信息"
:visible.sync="isvKeysDialogVisible"
@close="resetForm('isvKeysFrom')"
>
<el-form
ref="isvKeysFrom"
:model="isvKeysFormData"
label-width="160px"
size="mini"
class="key-view"
>
<el-form-item label="">
<el-alert
title="带 ★ 的分配给开发者"
type="warning"
:closable="false"
/>
</el-form-item>
<el-form-item :label="selfLabel('appKey')">
<span>{{ isvKeysFormData.appKey }}</span>
</el-form-item>
<el-form-item label="签名方式">
<span v-if="isvKeysFormData.signType === 1">RSA2</span>
<span v-if="isvKeysFormData.signType === 2">MD5</span>
</el-form-item>
<el-form-item v-show="showKeys()" label="秘钥格式">
<span v-if="isvKeysFormData.keyFormat === 1">PKCS8(JAVA适用)</span>
<span v-if="isvKeysFormData.keyFormat === 2">PKCS1(非JAVA适用)</span>
</el-form-item>
<el-form-item v-show="isvKeysFormData.signType === 2" :label="selfLabel('secret')">
<span>{{ isvKeysFormData.secret }}</span>
</el-form-item>
<fieldset v-show="showKeys()">
<legend>ISV公私钥</legend>
<el-form-item label="ISV公钥">
<el-input v-model="isvKeysFormData.publicKeyIsv" type="textarea" readonly />
</el-form-item>
<el-form-item :label="selfLabel('ISV私钥')">
<el-input v-model="isvKeysFormData.privateKeyIsv" type="textarea" readonly />
</el-form-item>
</fieldset>
<fieldset v-show="showKeys()">
<legend>平台公私钥</legend>
<el-form-item :label="selfLabel('平台公钥')">
<el-input v-model="isvKeysFormData.publicKeyPlatform" type="textarea" readonly />
</el-form-item>
<el-form-item prop="privateKeyPlatform" label="平台私钥">
<el-input v-model="isvKeysFormData.privateKeyPlatform" type="textarea" readonly />
</el-form-item>
</fieldset>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="isvKeysDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
<style>
.gen-key {margin-bottom: 0px !important;}
fieldset {border: 1px solid #ccc; color: gray;margin-left: 40px;margin-bottom: 20px;}
fieldset label {width: 110px !important;}
fieldset .el-form-item__content {margin-left: 110px !important;}
.key-view .el-form-item {margin-bottom: 10px !important;}
</style>
<script>
export default {
data() {
const validateSecret = (rule, value, callback) => {
if (this.isvDialogFormData.signType === 2) {
if (value === '') {
callback(new Error('不能为空'))
}
if (value.length > 200) {
callback(new Error('长度不能超过200'))
}
}
callback()
}
const validatePubPriKey = (rule, value, callback) => {
if (this.isvDialogFormData.signType === 1) {
if (value === '') {
callback(new Error('不能为空'))
}
}
callback()
}
return {
searchFormData: {
appKey: '',
@ -195,11 +203,6 @@ export default {
isvDialogTitle: '新增ISV',
isvDialogFormData: {
id: 0,
appKey: '',
secret: '',
pubKey: '',
priKey: '',
signType: 1,
status: 1,
roleCode: []
},
@ -207,16 +210,17 @@ export default {
appKey: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
],
secret: [
{ validator: validateSecret, trigger: 'blur' }
],
pubKey: [
{ validator: validatePubPriKey, trigger: 'blur' }
],
priKey: [
{ validator: validatePubPriKey, trigger: 'blur' }
]
},
isvKeysDialogVisible: false,
isvKeysFormData: {
appKey: '',
secret: '',
publicKeyIsv: '',
privateKeyIsv: '',
publicKeyPlatform: '',
privateKeyPlatform: '',
signType: ''
}
}
},
@ -237,16 +241,12 @@ export default {
})
}
},
onShowSecret: function(row) {
this.$alert(row.secret, 'secret')
},
onShowPriPubKey: function(row) {
const pubKey = row.pubKey
const priKey = row.priKey
const content = '<div>公钥:<textarea style="width: 380px;height: 100px;" readonly="readonly">' + pubKey + '</textarea><br>' +
'私钥:<textarea style="width: 380px;height: 100px;" readonly="readonly">' + priKey + '</textarea></div>'
this.$alert(content, '公私钥', {
dangerouslyUseHTMLString: true
onShowKeys: function(row) {
this.post('isv.keys.get', { appKey: row.appKey }, function(resp) {
this.isvKeysDialogVisible = true
this.$nextTick(() => {
Object.assign(this.isvKeysFormData, resp.data)
})
})
},
onSearchTable: function() {
@ -268,6 +268,9 @@ export default {
})
})
},
onKeysUpdate: function(row) {
this.$router.push({ path: `keys?appKey=${row.appKey}` })
},
onSizeChange: function(size) {
this.searchFormData.pageSize = size
this.loadTable()
@ -277,9 +280,11 @@ export default {
this.loadTable()
},
onAdd: function() {
this.isvDialogFormData.id = 0
this.isvDialogTitle = '新增ISV'
this.isvDialogVisible = true
this.$nextTick(() => {
this.isvDialogFormData.id = 0
})
},
onIsvDialogSave: function() {
const that = this
@ -294,8 +299,12 @@ export default {
})
},
onIsvDialogClose: function() {
this.$refs.isvForm.resetFields()
this.isvDialogVisible = false
this.resetForm('isvForm')
this.isvDialogFormData.status = 1
this.isvDialogFormData.roleCode = []
},
selfLabel: function(lab) {
return '★ ' + lab
},
roleRender: function(row) {
const html = []
@ -305,27 +314,8 @@ export default {
}
return html.join(', ')
},
onDataGen: function() {
this.post('isv.form.gen', {}, function(resp) {
const data = resp.data
//
if (this.isvDialogFormData.id === 0) {
Object.assign(this.isvDialogFormData, data)
} else {
const signType = this.isvDialogFormData.signType
// RSA2
if (signType === 1) {
Object.assign(this.isvDialogFormData, {
pubKey: data.pubKey,
priKey: data.priKey
})
} else if (signType === 2) {
Object.assign(this.isvDialogFormData, {
secret: data.secret
})
}
}
})
showKeys: function() {
return this.isvKeysFormData.signType === 1
}
}
}

@ -0,0 +1,172 @@
<template>
<div class="app-container">
<el-button class="el-icon-back" type="text" @click="onBack">返回</el-button>
<el-form
ref="isvKeysForm"
:rules="rulesIsvKeysForm"
:model="isvKeysFormData"
label-width="160px"
size="mini"
style="width: 700px;"
>
<el-form-item label="">
<el-alert
title="带 ★ 的分配给开发者"
type="warning"
:closable="false"
/>
</el-form-item>
<el-form-item :label="selfLabel('appKey')">
<div>{{ isvKeysFormData.appKey }}</div>
</el-form-item>
<el-form-item label="签名方式">
<el-radio-group v-model="isvKeysFormData.signType">
<el-radio :label="1" name="status">RSA2</el-radio>
<el-radio :label="2" name="status">MD5</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-show="showKeys()" label="秘钥格式">
<el-radio-group v-model="isvKeysFormData.keyFormat">
<el-radio :label="1" name="keyFormat">PKCS8(JAVA适用)</el-radio>
<el-radio :label="2" name="keyFormat">PKCS1(非JAVA适用)</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-show="isvKeysFormData.signType === 2" prop="secret" :label="selfLabel('secret')">
<el-input v-model="isvKeysFormData.secret" /> <el-button type="text" @click="onGenSecret">重新生成</el-button>
</el-form-item>
<fieldset v-show="showKeys()">
<legend>ISV公私钥</legend>
<el-form-item class="gen-key">
<el-button type="text" @click="onGenKeysIsv">重新生成</el-button>
</el-form-item>
<el-form-item prop="publicKeyIsv" label="ISV公钥">
<el-input v-model="isvKeysFormData.publicKeyIsv" type="textarea" />
</el-form-item>
<el-form-item prop="privateKeyIsv" :label="selfLabel('ISV私钥')">
<el-input v-model="isvKeysFormData.privateKeyIsv" type="textarea" />
</el-form-item>
</fieldset>
<fieldset v-show="showKeys()">
<legend>平台公私钥</legend>
<el-form-item class="gen-key">
<el-button type="text" @click="onGenKeysPlatform">重新生成</el-button>
</el-form-item>
<el-form-item prop="publicKeyPlatform" :label="selfLabel('平台公钥')">
<el-input v-model="isvKeysFormData.publicKeyPlatform" type="textarea" />
</el-form-item>
<el-form-item prop="privateKeyPlatform" label="平台私钥">
<el-input v-model="isvKeysFormData.privateKeyPlatform" type="textarea" />
</el-form-item>
</fieldset>
<el-form-item>
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="onBack">取消</el-button>
</el-form-item>
</el-form>
</div>
</template>
<style>
.gen-key {margin-bottom: 0px !important;}
fieldset {border: 1px solid #ccc; color: gray;margin-left: 40px;margin-bottom: 20px;}
fieldset label {width: 110px !important;}
fieldset .el-form-item__content {margin-left: 110px !important;}
</style>
<script>
export default {
data() {
const validateSecret = (rule, value, callback) => {
if (this.isvKeysFormData.signType === 2) {
if (value === '') {
callback(new Error('不能为空'))
}
if (value.length > 200) {
callback(new Error('长度不能超过200'))
}
}
callback()
}
const validatePubPriKey = (rule, value, callback) => {
if (this.isvKeysFormData.signType === 1) {
if (value === '') {
callback(new Error('不能为空'))
}
}
callback()
}
return {
isvKeysFormData: {
appKey: '',
secret: '',
keyFormat: 1,
publicKeyIsv: '',
privateKeyIsv: '',
publicKeyPlatform: '',
privateKeyPlatform: '',
signType: 1
},
rulesIsvKeysForm: {
secret: [
{ validator: validateSecret, trigger: 'blur' }
],
publicKeyIsv: [
{ validator: validatePubPriKey, trigger: 'blur' }
],
privateKeyIsv: [
{ validator: validatePubPriKey, trigger: 'blur' }
]
}
}
},
created() {
const query = this.$route.query
this.isvKeysFormData.appKey = query.appKey
this.loadForm()
},
methods: {
loadForm: function() {
this.post('isv.keys.get', { appKey: this.isvKeysFormData.appKey }, function(resp) {
Object.assign(this.isvKeysFormData, resp.data)
})
},
selfLabel: function(lab) {
return '★ ' + lab
},
onSubmit: function() {
this.$refs.isvKeysForm.validate((valid) => {
if (valid) {
this.post('isv.keys.update', this.isvKeysFormData, function() {
this.tip('保存成功')
})
}
})
},
onBack: function() {
this.$router.push({ path: 'list' })
},
onGenKeysPlatform: function() {
this.post('isv.keys.gen', {}, function(resp) {
this.tip('生成公私钥成功')
const data = resp.data
this.isvKeysFormData.publicKeyPlatform = data.publicKey
this.isvKeysFormData.privateKeyPlatform = data.privateKey
})
},
onGenKeysIsv: function() {
this.post('isv.keys.gen', { keyFormat: this.isvKeysFormData.keyFormat }, function(resp) {
this.tip('生成公私钥成功')
const data = resp.data
this.isvKeysFormData.publicKeyIsv = data.publicKey
this.isvKeysFormData.privateKeyIsv = data.privateKey
})
},
onGenSecret: function() {
this.post('isv.secret.gen', {}, function(resp) {
this.isvKeysFormData.secret = resp.data
})
},
showKeys: function() {
return this.isvKeysFormData.signType === 1
}
}
}
</script>

@ -231,7 +231,18 @@
</el-container>
</div>
</template>
<style>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.el-input.is-disabled .el-input__inner {color: #909399;}
.el-radio__input.is-disabled+span.el-radio__label {color: #909399;}
</style>
<script>
export default {
data() {
@ -425,6 +436,7 @@ export default {
},
onLimitDialogClose: function() {
this.resetForm('limitDialogForm')
this.limitDialogFormData.limitStatus = 0
},
infoRender: function(row) {
const html = []

Loading…
Cancel
Save