169 changed files with 5281 additions and 1143 deletions
@ -0,0 +1,49 @@ |
|||
package com.epmet.controller; |
|||
|
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.commons.tools.validator.ValidatorUtils; |
|||
import com.epmet.dto.form.LoginFormDTO; |
|||
import com.epmet.dto.result.UserTokenResultDTO; |
|||
import com.epmet.service.ThirdLoginService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
/** |
|||
* @Description 第三方-居民端、政府端登陆服务 |
|||
* @author sun |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("thirdlogin") |
|||
public class ThirdLoginController { |
|||
|
|||
@Autowired |
|||
private ThirdLoginService thirdLoginService; |
|||
|
|||
/** |
|||
* @param formDTO |
|||
* @return |
|||
* @Author sun |
|||
* @Description 单客户-居民端微信小程序登录 |
|||
**/ |
|||
@PostMapping("resilogin") |
|||
public Result<UserTokenResultDTO> resiLogin(@RequestBody LoginFormDTO formDTO) { |
|||
ValidatorUtils.validateEntity(formDTO); |
|||
return new Result<UserTokenResultDTO>().ok(thirdLoginService.resiLogin(formDTO)); |
|||
} |
|||
|
|||
/** |
|||
* @param formDTO |
|||
* @return |
|||
* @Author sun |
|||
* @Description 单客户-政府端微信小程序登录 |
|||
**/ |
|||
@PostMapping("worklogin") |
|||
public Result<UserTokenResultDTO> workLogin(@RequestBody LoginFormDTO formDTO) { |
|||
ValidatorUtils.validateEntity(formDTO); |
|||
return new Result<UserTokenResultDTO>().ok(thirdLoginService.workLogin(formDTO)); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
package com.epmet.dto.form; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotBlank; |
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @Description 单客户-居民端微信小程序登录 |
|||
* @Author sun |
|||
*/ |
|||
@Data |
|||
public class LoginFormDTO extends LoginCommonFormDTO implements Serializable { |
|||
private static final long serialVersionUID = 7950477424010655108L; |
|||
|
|||
/** |
|||
* 小程序appId |
|||
*/ |
|||
@NotBlank(message = "appId不能为空",groups = {AddUserInternalGroup.class}) |
|||
private String appId; |
|||
|
|||
/** |
|||
* 用户微信code |
|||
*/ |
|||
@NotBlank(message = "wxCode不能为空",groups = {AddUserInternalGroup.class}) |
|||
private String wxCode; |
|||
|
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package com.epmet.service; |
|||
|
|||
import com.epmet.dto.form.LoginFormDTO; |
|||
import com.epmet.dto.result.UserTokenResultDTO; |
|||
|
|||
/** |
|||
* @Description 第三方-居民端、政府端登陆服务 |
|||
* @author sun |
|||
*/ |
|||
public interface ThirdLoginService { |
|||
|
|||
/** |
|||
* @param formDTO |
|||
* @return |
|||
* @Author sun |
|||
* @Description 单客户-居民端微信小程序登录 |
|||
**/ |
|||
UserTokenResultDTO resiLogin(LoginFormDTO formDTO); |
|||
|
|||
/** |
|||
* @param formDTO |
|||
* @return |
|||
* @Author sun |
|||
* @Description 单客户-政府端微信小程序登录 |
|||
**/ |
|||
UserTokenResultDTO workLogin(LoginFormDTO formDTO); |
|||
} |
|||
@ -0,0 +1,348 @@ |
|||
package com.epmet.service.impl; |
|||
|
|||
import com.epmet.common.token.constant.LoginConstant; |
|||
import com.epmet.commons.tools.exception.EpmetErrorCode; |
|||
import com.epmet.commons.tools.exception.ExceptionUtils; |
|||
import com.epmet.commons.tools.exception.RenException; |
|||
import com.epmet.commons.tools.security.dto.GovTokenDto; |
|||
import com.epmet.commons.tools.security.dto.TokenDto; |
|||
import com.epmet.commons.tools.utils.ConvertUtils; |
|||
import com.epmet.commons.tools.utils.CpUserDetailRedis; |
|||
import com.epmet.commons.tools.utils.DateUtils; |
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.dto.CustomerAgencyDTO; |
|||
import com.epmet.dto.GovStaffRoleDTO; |
|||
import com.epmet.dto.UserDTO; |
|||
import com.epmet.dto.UserWechatDTO; |
|||
import com.epmet.dto.form.*; |
|||
import com.epmet.dto.result.DepartmentListResultDTO; |
|||
import com.epmet.dto.result.GridByStaffResultDTO; |
|||
import com.epmet.dto.result.StaffLatestAgencyResultDTO; |
|||
import com.epmet.dto.result.UserTokenResultDTO; |
|||
import com.epmet.feign.EpmetThirdFeignClient; |
|||
import com.epmet.feign.EpmetUserOpenFeignClient; |
|||
import com.epmet.feign.GovOrgOpenFeignClient; |
|||
import com.epmet.jwt.JwtTokenProperties; |
|||
import com.epmet.jwt.JwtTokenUtils; |
|||
import com.epmet.service.ThirdLoginService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.CollectionUtils; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* @author sun |
|||
* @Description 第三方-居民端、政府端登陆服务 |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
public class ThirdLoginServiceImpl implements ThirdLoginService { |
|||
|
|||
private static final Logger logger = LoggerFactory.getLogger(ThirdLoginServiceImpl.class); |
|||
@Autowired |
|||
private JwtTokenUtils jwtTokenUtils; |
|||
@Autowired |
|||
private JwtTokenProperties jwtTokenProperties; |
|||
@Autowired |
|||
private CpUserDetailRedis cpUserDetailRedis; |
|||
@Autowired |
|||
private EpmetThirdFeignClient epmetThirdFeignClient; |
|||
@Autowired |
|||
private EpmetUserOpenFeignClient epmetUserOpenFeignClient; |
|||
@Autowired |
|||
private GovOrgOpenFeignClient govOrgOpenFeignClient; |
|||
|
|||
/** |
|||
* @param formDTO |
|||
* @return |
|||
* @Author sun |
|||
* @Description 单客户-居民端微信小程序登录 |
|||
**/ |
|||
@Override |
|||
public UserTokenResultDTO resiLogin(LoginFormDTO formDTO) { |
|||
//1.调用epmet_third服务,校验appId是否有效以及是否授权,校验通过的调用微信API获取用户基本信息
|
|||
WxLoginFormDTO resiLoginFormDTO = new WxLoginFormDTO(); |
|||
resiLoginFormDTO.setAppId(formDTO.getAppId()); |
|||
resiLoginFormDTO.setWxCode(formDTO.getWxCode()); |
|||
Result<UserWechatDTO> result = epmetThirdFeignClient.resiAndWorkLogin(resiLoginFormDTO); |
|||
if (!result.success()) { |
|||
throw new RenException(result.getCode()); |
|||
} |
|||
UserWechatDTO userWechatDTO = result.getData(); |
|||
|
|||
//2.调用epmet-user服务,新增用户信息(先判断用户是否存在,不存在则新增存在则更新)
|
|||
WxUserFormDTO wxUserFormDTO = new WxUserFormDTO(); |
|||
wxUserFormDTO.setWechatDTO(userWechatDTO); |
|||
wxUserFormDTO.setApp(formDTO.getApp()); |
|||
Result<UserDTO> userResult = epmetUserOpenFeignClient.saveWxUser(wxUserFormDTO); |
|||
if (!userResult.success()) { |
|||
throw new RenException(result.getCode()); |
|||
} |
|||
UserDTO userDTO = userResult.getData(); |
|||
|
|||
//3.生成业务token
|
|||
String userId = userDTO.getId(); |
|||
String token = this.generateToken(formDTO, userId); |
|||
|
|||
//4.存放Redis
|
|||
this.saveTokenDto(formDTO, userId, userWechatDTO, token); |
|||
|
|||
//5.接口返参
|
|||
UserTokenResultDTO userTokenResultDTO = new UserTokenResultDTO(); |
|||
userTokenResultDTO.setToken(token); |
|||
|
|||
return userTokenResultDTO; |
|||
} |
|||
|
|||
/** |
|||
* @Description 居民端登陆生成业务token的key |
|||
**/ |
|||
private String generateToken(LoginCommonFormDTO formDTO, String userId) { |
|||
Map<String, Object> map = new HashMap<>(); |
|||
map.put("app", formDTO.getApp()); |
|||
map.put("client", formDTO.getClient()); |
|||
map.put("userId", userId); |
|||
String token = jwtTokenUtils.createToken(map); |
|||
logger.info("app:" + formDTO.getApp() + ";client:" + formDTO.getClient() + ";userId:" + userId + ";生成token[" + token + "]"); |
|||
return token; |
|||
} |
|||
|
|||
/** |
|||
* @Description 将token存入redis |
|||
**/ |
|||
private String saveTokenDto(LoginCommonFormDTO formDTO, String userId, UserWechatDTO userWechatDTO, String token) { |
|||
int expire = jwtTokenProperties.getExpire(); |
|||
TokenDto tokenDto = new TokenDto(); |
|||
tokenDto.setApp(formDTO.getApp()); |
|||
tokenDto.setClient(formDTO.getClient()); |
|||
tokenDto.setUserId(userId); |
|||
tokenDto.setOpenId(userWechatDTO.getWxOpenId()); |
|||
tokenDto.setSessionKey(userWechatDTO.getSessionKey()); |
|||
tokenDto.setUnionId(userWechatDTO.getUnionId()); |
|||
tokenDto.setToken(token); |
|||
tokenDto.setUpdateTime(System.currentTimeMillis()); |
|||
tokenDto.setExpireTime(jwtTokenUtils.getExpiration(token).getTime()); |
|||
cpUserDetailRedis.set(tokenDto, expire); |
|||
logger.info("截止时间:" + DateUtils.format(jwtTokenUtils.getExpiration(token), "yyyy-MM-dd HH:mm:ss")); |
|||
return token; |
|||
} |
|||
|
|||
/** |
|||
* @param formDTO |
|||
* @return |
|||
* @Author sun |
|||
* @Description 单客户-政府端微信小程序登录 |
|||
**/ |
|||
@Override |
|||
public UserTokenResultDTO workLogin(LoginFormDTO formDTO) { |
|||
//1.调用epmet_third服务,校验appId是否有效以及是否授权,校验通过的调用微信API获取用户基本信息
|
|||
WxLoginFormDTO resiLoginFormDTO = new WxLoginFormDTO(); |
|||
resiLoginFormDTO.setAppId(formDTO.getAppId()); |
|||
resiLoginFormDTO.setWxCode(formDTO.getWxCode()); |
|||
Result<UserWechatDTO> result = epmetThirdFeignClient.resiAndWorkLogin(resiLoginFormDTO); |
|||
if (!result.success()) { |
|||
throw new RenException(result.getCode()); |
|||
} |
|||
UserWechatDTO userWechatDTO = result.getData(); |
|||
|
|||
//2.根据openid查询用户是否存在历史登陆信息
|
|||
Result<StaffLatestAgencyResultDTO> latestStaffWechat = epmetUserOpenFeignClient.getLatestStaffWechatLoginRecord(userWechatDTO.getWxOpenId()); |
|||
if (!latestStaffWechat.success() || null == latestStaffWechat.getData()) { |
|||
logger.error(String.format("没有获取到用户最近一次登录账户信息,code[%s],msg[%s]", EpmetErrorCode.PLEASE_LOGIN.getCode(), EpmetErrorCode.PLEASE_LOGIN.getMsg())); |
|||
throw new RenException(EpmetErrorCode.PLEASE_LOGIN.getCode()); |
|||
} |
|||
StaffLatestAgencyResultDTO staffLatestAgencyResultDTO = latestStaffWechat.getData(); |
|||
|
|||
//3.记录staff_wechat
|
|||
this.savestaffwechat(staffLatestAgencyResultDTO.getStaffId(), userWechatDTO.getWxOpenId()); |
|||
|
|||
//4.记录登录日志
|
|||
this.saveStaffLoginRecord(staffLatestAgencyResultDTO); |
|||
|
|||
//5.获取用户token
|
|||
String token = this.generateGovWxmpToken(staffLatestAgencyResultDTO.getStaffId()); |
|||
|
|||
//6.保存到redis
|
|||
this.saveLatestGovTokenDto(staffLatestAgencyResultDTO, userWechatDTO, token); |
|||
UserTokenResultDTO userTokenResultDTO = new UserTokenResultDTO(); |
|||
userTokenResultDTO.setToken(token); |
|||
return userTokenResultDTO; |
|||
|
|||
} |
|||
|
|||
/** |
|||
* @param userId openid |
|||
* @return |
|||
* @Author sun |
|||
* @Description 保存微信和当前登录用户关系 |
|||
**/ |
|||
private Result savestaffwechat(String userId, String openid) { |
|||
StaffWechatFormDTO staffWechatFormDTO = new StaffWechatFormDTO(); |
|||
staffWechatFormDTO.setUserId(userId); |
|||
staffWechatFormDTO.setWxOpenId(openid); |
|||
return epmetUserOpenFeignClient.saveStaffWechat(staffWechatFormDTO); |
|||
} |
|||
|
|||
/** |
|||
* @param latestStaffWechatLoginDTO |
|||
* @return |
|||
* @Author sun |
|||
* @Description 保存登录日志 |
|||
**/ |
|||
private Result saveStaffLoginRecord(StaffLatestAgencyResultDTO latestStaffWechatLoginDTO) { |
|||
StaffLoginAgencyRecordFormDTO staffLoginAgencyRecordFormDTO = new StaffLoginAgencyRecordFormDTO(); |
|||
staffLoginAgencyRecordFormDTO.setCustomerId(latestStaffWechatLoginDTO.getCustomerId()); |
|||
staffLoginAgencyRecordFormDTO.setStaffId(latestStaffWechatLoginDTO.getStaffId()); |
|||
staffLoginAgencyRecordFormDTO.setWxOpenId(latestStaffWechatLoginDTO.getWxOpenId()); |
|||
staffLoginAgencyRecordFormDTO.setMobile(latestStaffWechatLoginDTO.getMobile()); |
|||
staffLoginAgencyRecordFormDTO.setAgencyId(latestStaffWechatLoginDTO.getAgencyId()); |
|||
Result staffLoginRecordResult = epmetUserOpenFeignClient.saveStaffLoginRecord(staffLoginAgencyRecordFormDTO); |
|||
return staffLoginRecordResult; |
|||
} |
|||
|
|||
/** |
|||
* @Description 生成政府端小程序业务token Key |
|||
* @Author sun |
|||
**/ |
|||
private String generateGovWxmpToken(String staffId) { |
|||
Map<String, Object> map = new HashMap<>(); |
|||
map.put("app", LoginConstant.APP_GOV); |
|||
map.put("client", LoginConstant.CLIENT_WXMP); |
|||
map.put("userId", staffId); |
|||
String token = jwtTokenUtils.createToken(map); |
|||
logger.info("app:" + LoginConstant.APP_GOV + ";client:" + LoginConstant.CLIENT_WXMP + ";userId:" + staffId + ";生成token[" + token + "]"); |
|||
return token; |
|||
} |
|||
|
|||
/** |
|||
* @Description 保存tokenDto到redis |
|||
* @Author sun |
|||
**/ |
|||
private void saveLatestGovTokenDto(StaffLatestAgencyResultDTO staffLatestAgency, UserWechatDTO userWechatDTO, String token) { |
|||
int expire = jwtTokenProperties.getExpire(); |
|||
GovTokenDto govTokenDto = new GovTokenDto(); |
|||
govTokenDto.setApp(LoginConstant.APP_GOV); |
|||
govTokenDto.setClient(LoginConstant.CLIENT_WXMP); |
|||
govTokenDto.setUserId(staffLatestAgency.getStaffId()); |
|||
govTokenDto.setOpenId(userWechatDTO.getWxOpenId()); |
|||
govTokenDto.setSessionKey(userWechatDTO.getSessionKey()); |
|||
govTokenDto.setUnionId(userWechatDTO.getUnionId()); |
|||
govTokenDto.setToken(token); |
|||
govTokenDto.setUpdateTime(System.currentTimeMillis()); |
|||
govTokenDto.setExpireTime(jwtTokenUtils.getExpiration(token).getTime()); |
|||
govTokenDto.setRootAgencyId(staffLatestAgency.getAgencyId()); |
|||
govTokenDto.setCustomerId(staffLatestAgency.getCustomerId()); |
|||
|
|||
//设置部门,网格,角色列表
|
|||
govTokenDto.setDeptIdList(getDeptartmentIdList(staffLatestAgency.getStaffId())); |
|||
govTokenDto.setGridIdList(getGridIdList(staffLatestAgency.getStaffId())); |
|||
CustomerAgencyDTO agency = getAgencyByStaffId(staffLatestAgency.getStaffId()); |
|||
if (agency != null) { |
|||
govTokenDto.setAgencyId(agency.getId()); |
|||
govTokenDto.setRoleList(queryGovStaffRoles(staffLatestAgency.getStaffId(), agency.getId())); |
|||
} |
|||
govTokenDto.setOrgIdPath(getOrgIdPath(staffLatestAgency.getStaffId())); |
|||
|
|||
cpUserDetailRedis.set(govTokenDto, expire); |
|||
logger.info("截止时间:" + DateUtils.format(jwtTokenUtils.getExpiration(token), "yyyy-MM-dd HH:mm:ss")); |
|||
} |
|||
|
|||
public Set<String> getDeptartmentIdList(String staffId) { |
|||
try { |
|||
Result<List<DepartmentListResultDTO>> deptListResult = govOrgOpenFeignClient.getDepartmentListByStaffId(staffId); |
|||
if (deptListResult.success()) { |
|||
if (!CollectionUtils.isEmpty(deptListResult.getData())) { |
|||
Set<String> deptIdLists = deptListResult.getData().stream().map(dept -> dept.getDepartmentId()).collect(Collectors.toSet()); |
|||
return deptIdLists; |
|||
} |
|||
} else { |
|||
logger.error("登录:查询部门列表,远程调用返回错误:{}", deptListResult.getMsg()); |
|||
} |
|||
} catch (Exception e) { |
|||
String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); |
|||
logger.error("登录:查询部门列表异常:{}", errorStackTrace); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 根据工作人员ID查询网格ID列表 |
|||
* |
|||
* @param staffId |
|||
* @return |
|||
*/ |
|||
public Set<String> getGridIdList(String staffId) { |
|||
Result<List<GridByStaffResultDTO>> result = govOrgOpenFeignClient.listGridsbystaffid(staffId); |
|||
if (!result.success()) { |
|||
logger.error("登录:查询网格列表,远程调用返回错误:{}", result.getMsg()); |
|||
return null; |
|||
} else { |
|||
List<GridByStaffResultDTO> grids = result.getData(); |
|||
return grids.stream().map(grid -> grid.getGridId()).collect(Collectors.toSet()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 根据staffId查询所属的组织机构 |
|||
* |
|||
* @param staffId |
|||
*/ |
|||
public CustomerAgencyDTO getAgencyByStaffId(String staffId) { |
|||
Result<CustomerAgencyDTO> result = govOrgOpenFeignClient.getAgencyByStaff(staffId); |
|||
if (!result.success()) { |
|||
logger.error("登录:查询登录人所属的机关OrgIdPath失败:{}", result.getMsg()); |
|||
return null; |
|||
} |
|||
return result.getData(); |
|||
} |
|||
|
|||
/** |
|||
* 查询人员在某机关单位下的角色列表 |
|||
* |
|||
* @param staffId orgId |
|||
*/ |
|||
public List<GovTokenDto.Role> queryGovStaffRoles(String staffId, String orgId) { |
|||
StaffRoleFormDTO formDTO = new StaffRoleFormDTO(); |
|||
formDTO.setStaffId(staffId); |
|||
formDTO.setOrgId(orgId); |
|||
Result<List<GovStaffRoleDTO>> gridResult = epmetUserOpenFeignClient.getRolesOfStaff(formDTO); |
|||
if (!CollectionUtils.isEmpty(gridResult.getData())) { |
|||
//return gridResult.getData().stream().map(role -> role.getId()).collect(Collectors.toSet());
|
|||
return ConvertUtils.sourceToTarget(gridResult.getData(), GovTokenDto.Role.class); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 查询工作人员的OrgIdPath |
|||
* |
|||
* @param staffId |
|||
* @return |
|||
*/ |
|||
public String getOrgIdPath(String staffId) { |
|||
Result<CustomerAgencyDTO> result = govOrgOpenFeignClient.getAgencyByStaff(staffId); |
|||
if (!result.success()) { |
|||
logger.error("登录:查询登录人所属的机关OrgIdPath失败:{}", result.getMsg()); |
|||
return null; |
|||
} |
|||
CustomerAgencyDTO agency = result.getData(); |
|||
if (agency != null) { |
|||
if ("0".equals(agency.getPid())) { |
|||
// 顶级
|
|||
return agency.getId(); |
|||
} else { |
|||
return agency.getPids().concat(":").concat(agency.getId()); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
package com.epmet.commons.tools.utils; |
|||
|
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import java.io.File; |
|||
import java.io.FileOutputStream; |
|||
import java.io.InputStream; |
|||
import java.io.OutputStream; |
|||
|
|||
/** |
|||
* @author zhaoqifeng |
|||
* @dscription |
|||
* @date 2020/7/17 14:01 |
|||
*/ |
|||
public class MultipartFileToFileUtils { |
|||
/** |
|||
* MultipartFile 转 File |
|||
* |
|||
* @param file |
|||
* @throws Exception |
|||
*/ |
|||
public static File multipartFileToFile(MultipartFile file) throws Exception { |
|||
File toFile = null; |
|||
if (("").equals(file) || file.getSize() <= 0) { |
|||
file = null; |
|||
} else { |
|||
InputStream ins = null; |
|||
ins = file.getInputStream(); |
|||
toFile = new File(file.getOriginalFilename()); |
|||
toFile = inputStreamToFile(ins, toFile); |
|||
ins.close(); |
|||
} |
|||
return toFile; |
|||
} |
|||
|
|||
|
|||
private static File inputStreamToFile(InputStream ins, File file) { |
|||
try { |
|||
OutputStream os = new FileOutputStream(file); |
|||
int bytesRead = 0; |
|||
byte[] buffer = new byte[8192]; |
|||
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { |
|||
os.write(buffer, 0, bytesRead); |
|||
} |
|||
os.close(); |
|||
ins.close(); |
|||
return file; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return null; |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
package com.epmet.service; |
|||
|
|||
import com.epmet.commons.tools.utils.Result; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/20 10:21 |
|||
*/ |
|||
public interface ComponentAccessTokenService { |
|||
|
|||
Result componentAccessTokenJob(); |
|||
|
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
package com.epmet.service; |
|||
|
|||
import com.epmet.commons.tools.utils.Result; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/20 10:44 |
|||
*/ |
|||
public interface RefreshAuthAccessTokenService { |
|||
|
|||
Result refreshAuthorizerAccessTokenJob(); |
|||
|
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
package com.epmet.service.impl; |
|||
|
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.feign.EpmetThirdFeignClient; |
|||
import com.epmet.service.ComponentAccessTokenService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/20 10:22 |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
public class ComponentAccessTokenServiceImpl implements ComponentAccessTokenService { |
|||
|
|||
@Autowired |
|||
private EpmetThirdFeignClient epmetThirdFeignClient; |
|||
|
|||
@Override |
|||
public Result componentAccessTokenJob() { |
|||
return epmetThirdFeignClient.getComponentAccessTokenJob(); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
package com.epmet.service.impl; |
|||
|
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.feign.EpmetThirdFeignClient; |
|||
import com.epmet.service.RefreshAuthAccessTokenService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/20 10:44 |
|||
*/ |
|||
@Service |
|||
public class RefreshAuthAccessTokenServiceImpl implements RefreshAuthAccessTokenService { |
|||
|
|||
@Autowired |
|||
private EpmetThirdFeignClient epmetThirdFeignClient; |
|||
|
|||
@Override |
|||
public Result refreshAuthorizerAccessTokenJob() { |
|||
return epmetThirdFeignClient.refreshAuthorizerAccessTokenJob(); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
package com.epmet.task; |
|||
|
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.service.ComponentAccessTokenService; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/20 10:28 |
|||
*/ |
|||
@Component("componentAccessToken") |
|||
public class ComponentAccessTokenTask implements ITask{ |
|||
|
|||
@Autowired |
|||
private ComponentAccessTokenService componentAccessTokenService; |
|||
|
|||
private Logger logger = LoggerFactory.getLogger(getClass()); |
|||
|
|||
|
|||
@Override |
|||
public void run(String params) { |
|||
logger.info("ComponentAccessTokenTask正在执行定时任务,参数为{}",params); |
|||
Result result = componentAccessTokenService.componentAccessTokenJob(); |
|||
if (result.success()){ |
|||
logger.info("ComponentAccessTokenTask定时任务执行成功"); |
|||
}else { |
|||
logger.error("ComponentAccessTokenTask定时任务执行失败:" + result.getMsg()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
package com.epmet.task; |
|||
|
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.service.RefreshAuthAccessTokenService; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/20 10:46 |
|||
*/ |
|||
@Component("refreshAuthAccessTokenTask") |
|||
public class RefreshAuthAccessTokenTask implements ITask { |
|||
|
|||
private Logger logger = LoggerFactory.getLogger(getClass()); |
|||
|
|||
@Autowired |
|||
private RefreshAuthAccessTokenService refreshAuthAccessTokenService; |
|||
|
|||
@Override |
|||
public void run(String params) { |
|||
logger.info("RefreshAuthAccessTokenTask正在执行定时任务,参数为{}",params); |
|||
Result result = refreshAuthAccessTokenService.refreshAuthorizerAccessTokenJob(); |
|||
if (result.success()){ |
|||
logger.info("RefreshAuthAccessTokenTask定时任务执行成功"); |
|||
}else { |
|||
logger.error("RefreshAuthAccessTokenTask定时任务执行失败:" + result.getMsg()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
/** |
|||
* Copyright 2018 人人开源 https://www.renren.io
|
|||
* <p> |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* <p> |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* <p> |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.epmet.dto; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.Date; |
|||
import lombok.Data; |
|||
|
|||
|
|||
/** |
|||
* 代码第三方配置 |
|||
* |
|||
* @author generator generator@elink-cn.com |
|||
* @since v1.0.0 2020-07-17 |
|||
*/ |
|||
@Data |
|||
public class CodeExtDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** |
|||
* 主键 |
|||
*/ |
|||
private String id; |
|||
|
|||
/** |
|||
* 客户ID |
|||
*/ |
|||
private String customerId; |
|||
|
|||
/** |
|||
* 所属端 居民的:resi,工作端:work |
|||
*/ |
|||
private String clientType; |
|||
|
|||
/** |
|||
* APPID |
|||
*/ |
|||
private String appId; |
|||
|
|||
/** |
|||
* 自定义配置 |
|||
*/ |
|||
private String extJson; |
|||
|
|||
/** |
|||
* 乐观锁 |
|||
*/ |
|||
private Integer revision; |
|||
|
|||
/** |
|||
* 是否删除 |
|||
*/ |
|||
private String delFlag; |
|||
|
|||
/** |
|||
* 创建人 |
|||
*/ |
|||
private String createdBy; |
|||
|
|||
/** |
|||
* 创建时间 |
|||
*/ |
|||
private Date createdTime; |
|||
|
|||
/** |
|||
* 更新人 |
|||
*/ |
|||
private String updatedBy; |
|||
|
|||
/** |
|||
* 更新时间 |
|||
*/ |
|||
private Date updatedTime; |
|||
|
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
package com.epmet.dto.form; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author zhaoqifeng |
|||
* @dscription |
|||
* @date 2020/7/17 11:15 |
|||
*/ |
|||
@Data |
|||
public class MediaUploadFormDTO implements Serializable { |
|||
private static final long serialVersionUID = -7342624180676221309L; |
|||
private String codeId; |
|||
private String type; |
|||
private MultipartFile media; |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
package com.epmet.dto.form; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import javax.validation.constraints.NotBlank; |
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @Author sun |
|||
* @Description 公众号-查询我的信息-接口入参 |
|||
*/ |
|||
@Data |
|||
public class MyInfoFormDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = -6547893374373422628L; |
|||
|
|||
public interface AddUserInternalGroup { |
|||
} |
|||
|
|||
/** |
|||
* 客户Id |
|||
*/ |
|||
//@NotBlank(message = "客户Id不能为空", groups = {MyInfoFormDTO.AddUserInternalGroup.class})
|
|||
private String customerId; |
|||
|
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package com.epmet.dto.form; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* @Author sun |
|||
* @Description 小程序居民端登陆-接口入参 |
|||
*/ |
|||
@Data |
|||
public class WxLoginFormDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = -6163303184086480522L; |
|||
|
|||
/** |
|||
* 小程序AppId |
|||
*/ |
|||
private String appId; |
|||
|
|||
/** |
|||
* 用户微信code |
|||
*/ |
|||
private String wxCode; |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
package com.epmet.dto.result; |
|||
|
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author zhaoqifeng |
|||
* @dscription |
|||
* @date 2020/7/16 9:53 |
|||
*/ |
|||
@NoArgsConstructor |
|||
@Data |
|||
public class CodeHistoryResultDTO implements Serializable { |
|||
private static final long serialVersionUID = 6030280825893585115L; |
|||
/** |
|||
* 操作时间 |
|||
*/ |
|||
private String operationTime; |
|||
/** |
|||
* 版本 |
|||
*/ |
|||
private String version; |
|||
/** |
|||
* 操作 上传upload,审核audit,撤回undo,发布release |
|||
*/ |
|||
private String operation; |
|||
/** |
|||
* 描述 |
|||
*/ |
|||
private String describe; |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
package com.epmet.dto.result; |
|||
|
|||
import com.epmet.dto.PaCustomerAgencyDTO; |
|||
import com.epmet.dto.PaCustomerDTO; |
|||
import com.epmet.dto.PaUserDTO; |
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @Author sun |
|||
* @Description 运营端初始化客户信息-查询客户各项注册信息-接口返参 |
|||
*/ |
|||
@Data |
|||
public class InitCustomerResultDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 3253989119352850315L; |
|||
|
|||
/** |
|||
* 注册客户信息 |
|||
*/ |
|||
private PaCustomerDTO paCustomer; |
|||
/** |
|||
* 注册客户组织信息 |
|||
*/ |
|||
private PaCustomerAgencyDTO paAgency; |
|||
/** |
|||
* 注册客户管理员信息 |
|||
*/ |
|||
private PaUserDTO paUser; |
|||
|
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
package com.epmet.dto.result; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author zhaoqifeng |
|||
* @dscription |
|||
* @date 2020/7/17 11:17 |
|||
*/ |
|||
@Data |
|||
public class MediaUploadResultDTO implements Serializable { |
|||
private static final long serialVersionUID = -8462768939270515547L; |
|||
private String id; |
|||
private String name; |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
package com.epmet.dto.result; |
|||
|
|||
import com.epmet.dto.PaCustomerDTO; |
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* 根据appid查询公众号注册的客户信息 |
|||
* @Author sun |
|||
*/ |
|||
@Data |
|||
public class PublicCustomerResultDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 4642988014737245076L; |
|||
|
|||
/** |
|||
* 客户信息 |
|||
*/ |
|||
private PaCustomerDTO customer; |
|||
|
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
package com.epmet.dto.result; |
|||
|
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author zhaoqifeng |
|||
* @dscription |
|||
* @date 2020/7/16 9:46 |
|||
*/ |
|||
@NoArgsConstructor |
|||
@Data |
|||
public class QrCodeResultDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = -1145375851106140589L; |
|||
/** |
|||
* 二维码 |
|||
*/ |
|||
private Object qrcode; |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package com.epmet.dto.result; |
|||
|
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* @author zhaoqifeng |
|||
* @dscription |
|||
* @date 2020/7/16 9:28 |
|||
*/ |
|||
@NoArgsConstructor |
|||
@Data |
|||
public class ReasonResultDTO implements Serializable { |
|||
private static final long serialVersionUID = -1905907350492787127L; |
|||
/** |
|||
* 失败原因 |
|||
*/ |
|||
private String reason; |
|||
/** |
|||
* 失败的小程序截图url |
|||
*/ |
|||
private List<String> screenshotUrl; |
|||
} |
|||
@ -1,59 +0,0 @@ |
|||
package com.epmet.constant; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/8 17:59 |
|||
*/ |
|||
public interface ThirdApiConstant { |
|||
|
|||
/** |
|||
* 获取预授权码 |
|||
*/ |
|||
String API_CREATE_PREAUTHCODE_URL = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode"; |
|||
|
|||
/** |
|||
* 使用授权码获取授权信息请求地址 |
|||
*/ |
|||
String API_QUERY_AUTH_URL = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth"; |
|||
|
|||
/** |
|||
* 获取令牌请求地址 |
|||
*/ |
|||
String API_COMPONENT_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/component/api_component_token"; |
|||
|
|||
String API_AUTHORIZER_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token"; |
|||
|
|||
/** |
|||
* 授权回调url |
|||
*/ |
|||
String API_REDIRECT_URL = "https://epmet-dev.elinkservice.cn/api/third/redirectauthcode"; |
|||
|
|||
/** |
|||
* 反参授权回调url |
|||
*/ |
|||
String API_RETURN_REDIRECT_URL = "https://epmet-dev.elinkservice.cn/api/third/redirectauthcode?client=%s&customerId=%s"; |
|||
|
|||
/** |
|||
* 授权注册页面扫码授权 |
|||
* component_appid:第三方AppId |
|||
* pre_auth_code:预授权码 |
|||
* redirect_uri:回调url(获取授权码) |
|||
*/ |
|||
String API_AUTH_REGISTER_URL = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=%s&pre_auth_code=%s&redirect_uri=%s"; |
|||
|
|||
/** |
|||
* 创建开放平台帐号并绑定公众号/小程序 |
|||
*/ |
|||
String API_CREATE_OPEN = "https://api.weixin.qq.com/cgi-bin/open/create"; |
|||
|
|||
/** |
|||
* 公众号/小程序绑定到开放平台帐号下 |
|||
*/ |
|||
String API_BIND_OPEN = "https://api.weixin.qq.com/cgi-bin/open/bind?"; |
|||
|
|||
/** |
|||
* 获取授权方的帐号基本信息 |
|||
*/ |
|||
String API_GET_AUTHORIZER_INFO = "https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info"; |
|||
|
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
/** |
|||
* Copyright 2018 人人开源 https://www.renren.io
|
|||
* <p> |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* <p> |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* <p> |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.epmet.controller; |
|||
|
|||
import com.epmet.commons.tools.page.PageData; |
|||
import com.epmet.commons.tools.utils.Result; |
|||
import com.epmet.commons.tools.validator.AssertUtils; |
|||
import com.epmet.commons.tools.validator.ValidatorUtils; |
|||
import com.epmet.commons.tools.validator.group.AddGroup; |
|||
import com.epmet.commons.tools.validator.group.DefaultGroup; |
|||
import com.epmet.commons.tools.validator.group.UpdateGroup; |
|||
import com.epmet.dto.CodeExtDTO; |
|||
import com.epmet.service.CodeExtService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import java.util.Map; |
|||
|
|||
|
|||
/** |
|||
* 代码第三方配置 |
|||
* |
|||
* @author generator generator@elink-cn.com |
|||
* @since v1.0.0 2020-07-17 |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("codeext") |
|||
public class CodeExtController { |
|||
|
|||
@Autowired |
|||
private CodeExtService codeExtService; |
|||
|
|||
@GetMapping("page") |
|||
public Result<PageData<CodeExtDTO>> page(@RequestParam Map<String, Object> params){ |
|||
PageData<CodeExtDTO> page = codeExtService.page(params); |
|||
return new Result<PageData<CodeExtDTO>>().ok(page); |
|||
} |
|||
|
|||
@GetMapping("{id}") |
|||
public Result<CodeExtDTO> get(@PathVariable("id") String id){ |
|||
CodeExtDTO data = codeExtService.get(id); |
|||
return new Result<CodeExtDTO>().ok(data); |
|||
} |
|||
|
|||
@PostMapping |
|||
public Result save(@RequestBody CodeExtDTO dto){ |
|||
//效验数据
|
|||
ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); |
|||
codeExtService.save(dto); |
|||
return new Result(); |
|||
} |
|||
|
|||
@PutMapping |
|||
public Result update(@RequestBody CodeExtDTO dto){ |
|||
//效验数据
|
|||
ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); |
|||
codeExtService.update(dto); |
|||
return new Result(); |
|||
} |
|||
|
|||
@DeleteMapping |
|||
public Result delete(@RequestBody String[] ids){ |
|||
//效验数据
|
|||
AssertUtils.isArrayEmpty(ids, "id"); |
|||
codeExtService.delete(ids); |
|||
return new Result(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
/** |
|||
* Copyright 2018 人人开源 https://www.renren.io
|
|||
* <p> |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* <p> |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* <p> |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.epmet.dao; |
|||
|
|||
import com.epmet.commons.mybatis.dao.BaseDao; |
|||
import com.epmet.dto.CodeExtDTO; |
|||
import com.epmet.entity.CodeExtEntity; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
import org.apache.ibatis.annotations.Param; |
|||
|
|||
/** |
|||
* 代码第三方配置 |
|||
* |
|||
* @author generator generator@elink-cn.com |
|||
* @since v1.0.0 2020-07-17 |
|||
*/ |
|||
@Mapper |
|||
public interface CodeExtDao extends BaseDao<CodeExtEntity> { |
|||
|
|||
/** |
|||
* 获取第三方配置模板 |
|||
* @author zhaoqifeng |
|||
* @date 2020/7/17 15:25 |
|||
* @param |
|||
* @return java.lang.String |
|||
*/ |
|||
String selectExtTemplate(@Param("clientType") String clientType); |
|||
|
|||
/** |
|||
* 获取客户的第三方配置 |
|||
* @author zhaoqifeng |
|||
* @date 2020/7/17 15:26 |
|||
* @param customerId |
|||
* @param clientType |
|||
* @return java.lang.String |
|||
*/ |
|||
CodeExtDTO selectExtByCustomerId(@Param("customerId") String customerId, @Param("clientType") String clientType); |
|||
|
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright 2018 人人开源 https://www.renren.io
|
|||
* <p> |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* <p> |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* <p> |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.epmet.dao; |
|||
|
|||
import com.epmet.commons.mybatis.dao.BaseDao; |
|||
import com.epmet.dto.result.CodeHistoryResultDTO; |
|||
import com.epmet.entity.CodeOperationHistoryEntity; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
import org.apache.ibatis.annotations.Param; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 客户代码操作历史 |
|||
* |
|||
* @author generator generator@elink-cn.com |
|||
* @since v1.0.0 2020-07-09 |
|||
*/ |
|||
@Mapper |
|||
public interface CodeOperationHistoryDao extends BaseDao<CodeOperationHistoryEntity> { |
|||
|
|||
List<CodeHistoryResultDTO> selectHistoryList(@Param("customerId") String customerId, @Param("clientType") String clientType); |
|||
|
|||
/** |
|||
* 更新描述 |
|||
* @author zhaoqifeng |
|||
* @date 2020/7/16 16:11 |
|||
* @param codeId |
|||
* @param describe |
|||
* @return void |
|||
*/ |
|||
void updateDescribeByCodeId(@Param("codeId") String codeId, @Param("describe") String describe); |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
/** |
|||
* Copyright 2018 人人开源 https://www.renren.io
|
|||
* <p> |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* <p> |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* <p> |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.epmet.entity; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
|
|||
import com.epmet.commons.mybatis.entity.BaseEpmetEntity; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* 代码第三方配置 |
|||
* |
|||
* @author generator generator@elink-cn.com |
|||
* @since v1.0.0 2020-07-17 |
|||
*/ |
|||
@Data |
|||
@EqualsAndHashCode(callSuper=false) |
|||
@TableName("code_ext") |
|||
public class CodeExtEntity extends BaseEpmetEntity { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** |
|||
* 客户ID |
|||
*/ |
|||
private String customerId; |
|||
|
|||
/** |
|||
* 所属端 居民的:resi,工作端:work |
|||
*/ |
|||
private String clientType; |
|||
|
|||
/** |
|||
* APPID |
|||
*/ |
|||
private String appId; |
|||
|
|||
/** |
|||
* 自定义配置 |
|||
*/ |
|||
private String extJson; |
|||
|
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
package com.epmet.exception; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/6 10:03 |
|||
*/ |
|||
@SuppressWarnings("serial") |
|||
public class AesException extends Exception { |
|||
|
|||
public final static int OK = 0; |
|||
public final static int ValidateSignatureError = -40001; |
|||
public final static int ParseXmlError = -40002; |
|||
public final static int ComputeSignatureError = -40003; |
|||
public final static int IllegalAesKey = -40004; |
|||
public final static int ValidateCorpidError = -40005; |
|||
public final static int EncryptAESError = -40006; |
|||
public final static int DecryptAESError = -40007; |
|||
public final static int IllegalBuffer = -40008; |
|||
public final static int EncodeBase64Error = -40009; |
|||
public final static int DecodeBase64Error = -40010; |
|||
public final static int GenReturnXmlError = -40011; |
|||
|
|||
private int code; |
|||
|
|||
private static String getMessage(int code) { |
|||
switch (code) { |
|||
case ValidateSignatureError: |
|||
return "签名验证错误"; |
|||
case ParseXmlError: |
|||
return "xml解析失败"; |
|||
case ComputeSignatureError: |
|||
return "sha加密生成签名失败"; |
|||
case IllegalAesKey: |
|||
return "SymmetricKey非法"; |
|||
case ValidateCorpidError: |
|||
return "corpid校验失败"; |
|||
case EncryptAESError: |
|||
return "aes加密失败"; |
|||
case DecryptAESError: |
|||
return "aes解密失败"; |
|||
case IllegalBuffer: |
|||
return "解密后得到的buffer非法"; |
|||
case EncodeBase64Error: |
|||
return "base64加密错误"; |
|||
case DecodeBase64Error: |
|||
return "base64解密错误"; |
|||
case GenReturnXmlError: |
|||
return "xml生成失败"; |
|||
default: |
|||
return null; // cannot be
|
|||
} |
|||
} |
|||
|
|||
public int getCode() { |
|||
return code; |
|||
} |
|||
|
|||
public AesException(int code) { |
|||
super(getMessage(code)); |
|||
this.code = code; |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,96 @@ |
|||
/* |
|||
* Copyright (C) 2018 Baidu, Inc. All Rights Reserved. |
|||
*/ |
|||
package com.epmet.mpaes; |
|||
|
|||
import org.apache.commons.codec.binary.Base64; |
|||
|
|||
import javax.crypto.Cipher; |
|||
import javax.crypto.spec.IvParameterSpec; |
|||
import javax.crypto.spec.SecretKeySpec; |
|||
import java.nio.charset.Charset; |
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* JAVA AES 消息加解密 |
|||
*/ |
|||
public class AesDecryptUtil { |
|||
|
|||
private static Charset CHARSET = Charset.forName("utf-8"); |
|||
private Cipher decCipher; |
|||
|
|||
/** |
|||
* 构造函数 |
|||
* |
|||
* @param encodingAesKey encodingAESKey |
|||
* |
|||
* @throws Exception 异常错误信息 |
|||
*/ |
|||
public AesDecryptUtil(String encodingAesKey) throws Exception { |
|||
int encodingAesKeyLength = 43; |
|||
if (encodingAesKey.length() != encodingAesKeyLength) { |
|||
throw new Exception("ILLEGAL_AES_KEY_ERROR"); |
|||
} |
|||
byte[] aesKey = Base64.decodeBase64(encodingAesKey + "="); |
|||
|
|||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); |
|||
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); |
|||
Cipher encCipher = Cipher.getInstance("AES/CBC/NoPadding"); |
|||
encCipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); |
|||
|
|||
decCipher = Cipher.getInstance("AES/CBC/NoPadding"); |
|||
decCipher.init(Cipher.DECRYPT_MODE, keySpec, iv); |
|||
} |
|||
|
|||
/** |
|||
* 还原4个字节的网络字节序 |
|||
* |
|||
* @param orderBytes 字节码 |
|||
* |
|||
* @return sourceNumber |
|||
*/ |
|||
private int recoverNetworkBytesOrder(byte[] orderBytes) { |
|||
int sourceNumber = 0; |
|||
int length = 4; |
|||
int number = 8; |
|||
for (int i = 0; i < length; i++) { |
|||
sourceNumber <<= number; |
|||
sourceNumber |= orderBytes[i] & 0xff; |
|||
} |
|||
return sourceNumber; |
|||
} |
|||
|
|||
/** |
|||
* 对密文进行解密 |
|||
* |
|||
* @param text 需要解密的密文 |
|||
* |
|||
* @return 解密得到的明文 |
|||
* |
|||
* @throws Exception 异常错误信息 |
|||
*/ |
|||
public String decrypt(String text) |
|||
throws Exception { |
|||
byte[] original; |
|||
try { |
|||
|
|||
byte[] encrypted = Base64.decodeBase64(text); |
|||
original = decCipher.doFinal(encrypted); |
|||
} catch (Exception e) { |
|||
throw new Exception("DECRYPT_AES_ERROR"); |
|||
} |
|||
String xmlContent; |
|||
try { |
|||
// 去除补位字符
|
|||
byte[] bytes = PKCS7Encoder.decode(original); |
|||
// 分离16位随机字符串,网络字节序和ClientId
|
|||
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); |
|||
int xmlLength = recoverNetworkBytesOrder(networkOrder); |
|||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); |
|||
} catch (Exception e) { |
|||
throw new Exception("ILLEGAL_BUFFER_ERROR"); |
|||
} |
|||
return xmlContent; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
package com.epmet.mpaes; |
|||
|
|||
@SuppressWarnings("serial") |
|||
public class AesException extends Exception { |
|||
|
|||
/*public final static int OK = 0; |
|||
public final static int ValidateSignatureError = -40001; |
|||
public final static int ParseXmlError = -40002; |
|||
public final static int ComputeSignatureError = -40003; |
|||
public final static int IllegalAesKey = -40004; |
|||
public final static int ValidateAppidError = -40005; |
|||
public final static int EncryptAESError = -40006; |
|||
public final static int DecryptAESError = -40007; |
|||
public final static int IllegalBuffer = -40008; |
|||
//public final static int EncodeBase64Error = -40009;
|
|||
//public final static int DecodeBase64Error = -40010;
|
|||
//public final static int GenReturnXmlError = -40011;
|
|||
|
|||
private int code; |
|||
|
|||
private static String getMessage(int code) { |
|||
switch (code) { |
|||
case ValidateSignatureError: |
|||
return "签名验证错误"; |
|||
case ParseXmlError: |
|||
return "xml解析失败"; |
|||
case ComputeSignatureError: |
|||
return "sha加密生成签名失败"; |
|||
case IllegalAesKey: |
|||
return "SymmetricKey非法"; |
|||
case ValidateAppidError: |
|||
return "appid校验失败"; |
|||
case EncryptAESError: |
|||
return "aes加密失败"; |
|||
case DecryptAESError: |
|||
return "aes解密失败"; |
|||
case IllegalBuffer: |
|||
return "解密后得到的buffer非法"; |
|||
// case EncodeBase64Error:
|
|||
// return "base64加密错误";
|
|||
// case DecodeBase64Error:
|
|||
// return "base64解密错误";
|
|||
// case GenReturnXmlError:
|
|||
// return "xml生成失败";
|
|||
default: |
|||
return null; // cannot be
|
|||
} |
|||
} |
|||
|
|||
public int getCode() { |
|||
return code; |
|||
} |
|||
|
|||
AesException(int code) { |
|||
super(getMessage(code)); |
|||
this.code = code; |
|||
}*/ |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package com.epmet.mpaes; |
|||
|
|||
import java.util.ArrayList; |
|||
|
|||
class ByteGroup { |
|||
ArrayList<Byte> byteContainer = new ArrayList<Byte>(); |
|||
|
|||
public byte[] toBytes() { |
|||
byte[] bytes = new byte[byteContainer.size()]; |
|||
for (int i = 0; i < byteContainer.size(); i++) { |
|||
bytes[i] = byteContainer.get(i); |
|||
} |
|||
return bytes; |
|||
} |
|||
|
|||
public ByteGroup addBytes(byte[] bytes) { |
|||
for (byte b : bytes) { |
|||
byteContainer.add(b); |
|||
} |
|||
return this; |
|||
} |
|||
|
|||
public int size() { |
|||
return byteContainer.size(); |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
/** |
|||
* 对公众平台发送给公众账号的消息加解密示例代码. |
|||
* |
|||
* @copyright Copyright (c) 1998-2014 Tencent Inc. |
|||
*/ |
|||
|
|||
// ------------------------------------------------------------------------
|
|||
|
|||
package com.epmet.mpaes; |
|||
|
|||
import java.nio.charset.Charset; |
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* 提供基于PKCS7算法的加解密接口. |
|||
*/ |
|||
class PKCS7Encoder { |
|||
static Charset CHARSET = Charset.forName("utf-8"); |
|||
static int BLOCK_SIZE = 32; |
|||
|
|||
/** |
|||
* 获得对明文进行补位填充的字节. |
|||
* |
|||
* @param count 需要进行填充补位操作的明文字节个数 |
|||
* @return 补齐用的字节数组 |
|||
*/ |
|||
static byte[] encode(int count) { |
|||
// 计算需要填充的位数
|
|||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); |
|||
if (amountToPad == 0) { |
|||
amountToPad = BLOCK_SIZE; |
|||
} |
|||
// 获得补位所用的字符
|
|||
char padChr = chr(amountToPad); |
|||
String tmp = new String(); |
|||
for (int index = 0; index < amountToPad; index++) { |
|||
tmp += padChr; |
|||
} |
|||
return tmp.getBytes(CHARSET); |
|||
} |
|||
|
|||
/** |
|||
* 删除解密后明文的补位字符 |
|||
* |
|||
* @param decrypted 解密后的明文 |
|||
* @return 删除补位字符后的明文 |
|||
*/ |
|||
static byte[] decode(byte[] decrypted) { |
|||
int pad = (int) decrypted[decrypted.length - 1]; |
|||
if (pad < 1 || pad > 32) { |
|||
pad = 0; |
|||
} |
|||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); |
|||
} |
|||
|
|||
/** |
|||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码 |
|||
* |
|||
* @param a 需要转化的数字 |
|||
* @return 转化得到的字符 |
|||
*/ |
|||
static char chr(int a) { |
|||
byte target = (byte) (a & 0xFF); |
|||
return (char) target; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
/** |
|||
* 对公众平台发送给公众账号的消息加解密示例代码. |
|||
* |
|||
* @copyright Copyright (c) 1998-2014 Tencent Inc. |
|||
*/ |
|||
|
|||
// ------------------------------------------------------------------------
|
|||
|
|||
package com.epmet.mpaes; |
|||
|
|||
import com.epmet.commons.tools.exception.RenException; |
|||
import com.epmet.wxapi.enums.WxMaErrorMsgEnum; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.security.MessageDigest; |
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* SHA1 class |
|||
* |
|||
* 计算公众平台的消息签名接口. |
|||
*/ |
|||
@Component |
|||
public class SHA1 { |
|||
|
|||
/** |
|||
* 用SHA1算法生成安全签名 |
|||
* @param token 票据 |
|||
* @param timestamp 时间戳 |
|||
* @param nonce 随机字符串 |
|||
* @param encrypt 密文 |
|||
* @return 安全签名 |
|||
* @throws AesException |
|||
*/ |
|||
public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException { |
|||
try { |
|||
String[] array = new String[]{token, timestamp, nonce, encrypt}; |
|||
StringBuffer sb = new StringBuffer(); |
|||
// 字符串排序
|
|||
Arrays.sort(array); |
|||
for (int i = 0; i < 4; i++) { |
|||
sb.append(array[i]); |
|||
} |
|||
String str = sb.toString(); |
|||
// SHA1签名生成
|
|||
MessageDigest md = MessageDigest.getInstance("SHA-1"); |
|||
md.update(str.getBytes()); |
|||
byte[] digest = md.digest(); |
|||
|
|||
StringBuffer hexstr = new StringBuffer(); |
|||
String shaHex = ""; |
|||
for (int i = 0; i < digest.length; i++) { |
|||
shaHex = Integer.toHexString(digest[i] & 0xFF); |
|||
if (shaHex.length() < 2) { |
|||
hexstr.append(0); |
|||
} |
|||
hexstr.append(shaHex); |
|||
} |
|||
return hexstr.toString(); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40003.getMsg()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,283 @@ |
|||
/** |
|||
* 对公众平台发送给公众账号的消息加解密示例代码. |
|||
* |
|||
* @copyright Copyright (c) 1998-2014 Tencent Inc. |
|||
*/ |
|||
|
|||
// ------------------------------------------------------------------------
|
|||
|
|||
/** |
|||
* 针对org.apache.commons.codec.binary.Base64, |
|||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) |
|||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
|
|||
*/ |
|||
package com.epmet.mpaes; |
|||
|
|||
import com.epmet.commons.tools.exception.RenException; |
|||
import com.epmet.wxapi.enums.WxMaErrorMsgEnum; |
|||
import org.apache.commons.codec.binary.Base64; |
|||
|
|||
import javax.crypto.Cipher; |
|||
import javax.crypto.spec.IvParameterSpec; |
|||
import javax.crypto.spec.SecretKeySpec; |
|||
import java.nio.charset.Charset; |
|||
import java.util.Arrays; |
|||
import java.util.Random; |
|||
|
|||
/** |
|||
* 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串). |
|||
* <ol> |
|||
* <li>第三方回复加密消息给公众平台</li> |
|||
* <li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li> |
|||
* </ol> |
|||
* 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案 |
|||
* <ol> |
|||
* <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址: |
|||
* http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
|
|||
* <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li> |
|||
* <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li> |
|||
* <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li> |
|||
* </ol> |
|||
*/ |
|||
public class WXBizMsgCrypt { |
|||
static Charset CHARSET = Charset.forName("utf-8"); |
|||
Base64 base64 = new Base64(); |
|||
byte[] aesKey; |
|||
String token; |
|||
String appId; |
|||
|
|||
/** |
|||
* 构造函数 |
|||
* @param token 公众平台上,开发者设置的token |
|||
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey |
|||
* @param appId 公众平台appid |
|||
* |
|||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 |
|||
*/ |
|||
public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException { |
|||
if (encodingAesKey.length() != 43) { |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40004.getMsg()); |
|||
} |
|||
|
|||
this.token = token; |
|||
this.appId = appId; |
|||
aesKey = Base64.decodeBase64(encodingAesKey + "="); |
|||
} |
|||
|
|||
// 生成4个字节的网络字节序
|
|||
byte[] getNetworkBytesOrder(int sourceNumber) { |
|||
byte[] orderBytes = new byte[4]; |
|||
orderBytes[3] = (byte) (sourceNumber & 0xFF); |
|||
orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF); |
|||
orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF); |
|||
orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF); |
|||
return orderBytes; |
|||
} |
|||
|
|||
// 还原4个字节的网络字节序
|
|||
int recoverNetworkBytesOrder(byte[] orderBytes) { |
|||
int sourceNumber = 0; |
|||
for (int i = 0; i < 4; i++) { |
|||
sourceNumber <<= 8; |
|||
sourceNumber |= orderBytes[i] & 0xff; |
|||
} |
|||
return sourceNumber; |
|||
} |
|||
|
|||
// 随机生成16位字符串
|
|||
String getRandomStr() { |
|||
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; |
|||
Random random = new Random(); |
|||
StringBuffer sb = new StringBuffer(); |
|||
for (int i = 0; i < 16; i++) { |
|||
int number = random.nextInt(base.length()); |
|||
sb.append(base.charAt(number)); |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 对明文进行加密. |
|||
* |
|||
* @param text 需要加密的明文 |
|||
* @return 加密后base64编码的字符串 |
|||
* @throws AesException aes加密失败 |
|||
*/ |
|||
String encrypt(String randomStr, String text) throws AesException { |
|||
ByteGroup byteCollector = new ByteGroup(); |
|||
byte[] randomStrBytes = randomStr.getBytes(CHARSET); |
|||
byte[] textBytes = text.getBytes(CHARSET); |
|||
byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length); |
|||
byte[] appidBytes = appId.getBytes(CHARSET); |
|||
|
|||
// randomStr + networkBytesOrder + text + appid
|
|||
byteCollector.addBytes(randomStrBytes); |
|||
byteCollector.addBytes(networkBytesOrder); |
|||
byteCollector.addBytes(textBytes); |
|||
byteCollector.addBytes(appidBytes); |
|||
|
|||
// ... + pad: 使用自定义的填充方式对明文进行补位填充
|
|||
byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); |
|||
byteCollector.addBytes(padBytes); |
|||
|
|||
// 获得最终的字节流, 未加密
|
|||
byte[] unencrypted = byteCollector.toBytes(); |
|||
|
|||
try { |
|||
// 设置加密模式为AES的CBC模式
|
|||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); |
|||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); |
|||
IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16); |
|||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); |
|||
|
|||
// 加密
|
|||
byte[] encrypted = cipher.doFinal(unencrypted); |
|||
|
|||
// 使用BASE64对加密后的字符串进行编码
|
|||
String base64Encrypted = base64.encodeToString(encrypted); |
|||
|
|||
return base64Encrypted; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40006.getMsg()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 对密文进行解密. |
|||
* |
|||
* @param text 需要解密的密文 |
|||
* @return 解密得到的明文 |
|||
* @throws AesException aes解密失败 |
|||
*/ |
|||
String decrypt(String text) throws AesException { |
|||
byte[] original; |
|||
try { |
|||
// 设置解密模式为AES的CBC模式
|
|||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); |
|||
SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); |
|||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); |
|||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); |
|||
|
|||
// 使用BASE64对密文进行解码
|
|||
byte[] encrypted = Base64.decodeBase64(text); |
|||
|
|||
// 解密
|
|||
original = cipher.doFinal(encrypted); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40007.getMsg()); |
|||
} |
|||
|
|||
String xmlContent, from_appid; |
|||
try { |
|||
// 去除补位字符
|
|||
byte[] bytes = PKCS7Encoder.decode(original); |
|||
|
|||
// 分离16位随机字符串,网络字节序和AppId
|
|||
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); |
|||
|
|||
int xmlLength = recoverNetworkBytesOrder(networkOrder); |
|||
|
|||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); |
|||
from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), |
|||
CHARSET); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40008.getMsg()); |
|||
} |
|||
|
|||
// appid不相同的情况
|
|||
if (!from_appid.equals(appId)) { |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40005.getMsg()); |
|||
} |
|||
return xmlContent; |
|||
|
|||
} |
|||
|
|||
/** |
|||
* 将公众平台回复用户的消息加密打包. |
|||
* <ol> |
|||
* <li>对要发送的消息进行AES-CBC加密</li> |
|||
* <li>生成安全签名</li> |
|||
* <li>将消息密文和安全签名打包成xml格式</li> |
|||
* </ol> |
|||
* |
|||
* @param replyMsg 公众平台待回复用户的消息,xml格式的字符串 |
|||
* @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp |
|||
* @param nonce 随机串,可以自己生成,也可以用URL参数的nonce |
|||
* |
|||
* @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 |
|||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 |
|||
*/ |
|||
public String encryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException { |
|||
// 加密
|
|||
String encrypt = encrypt(getRandomStr(), replyMsg); |
|||
|
|||
// 生成安全签名
|
|||
if (timeStamp == "") { |
|||
timeStamp = Long.toString(System.currentTimeMillis()); |
|||
} |
|||
|
|||
String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt); |
|||
|
|||
// System.out.println("发送给平台的签名是: " + signature[1].toString());
|
|||
// 生成发送的xml
|
|||
String result = XMLParse.generate(encrypt, signature, timeStamp, nonce); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 检验消息的真实性,并且获取解密后的明文. |
|||
* <ol> |
|||
* <li>利用收到的密文生成安全签名,进行签名验证</li> |
|||
* <li>若验证通过,则提取xml中的加密消息</li> |
|||
* <li>对消息进行解密</li> |
|||
* </ol> |
|||
* |
|||
* @param msgSignature 签名串,对应URL参数的msg_signature |
|||
* @param timeStamp 时间戳,对应URL参数的timestamp |
|||
* @param nonce 随机串,对应URL参数的nonce |
|||
* @param postData 密文,对应POST请求的数据 |
|||
* |
|||
* @return 解密后的原文 |
|||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 |
|||
*/ |
|||
public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData) |
|||
throws AesException { |
|||
// 提取密文
|
|||
String encrypt = XMLParse.extract(postData); |
|||
|
|||
// 验证安全签名
|
|||
String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt); |
|||
if (!signature.equals(msgSignature)) { |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40001.getMsg()); |
|||
} |
|||
// 解密
|
|||
String result = decrypt(encrypt); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 验证URL |
|||
* @param msgSignature 签名串,对应URL参数的msg_signature |
|||
* @param timeStamp 时间戳,对应URL参数的timestamp |
|||
* @param nonce 随机串,对应URL参数的nonce |
|||
* @param echoStr 随机串,对应URL参数的echostr |
|||
* |
|||
* @return 解密之后的echostr |
|||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 |
|||
*/ |
|||
public String verifyUrl(String msgSignature, String timeStamp, String nonce, String echoStr) |
|||
throws AesException { |
|||
String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr); |
|||
|
|||
if (!signature.equals(msgSignature)) { |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40001.getMsg()); |
|||
} |
|||
|
|||
String result = decrypt(echoStr); |
|||
return result; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
/** |
|||
* 对公众平台发送给公众账号的消息加解密示例代码. |
|||
* |
|||
* @copyright Copyright (c) 1998-2014 Tencent Inc. |
|||
*/ |
|||
|
|||
// ------------------------------------------------------------------------
|
|||
|
|||
package com.epmet.mpaes; |
|||
|
|||
import com.epmet.commons.tools.exception.RenException; |
|||
import com.epmet.wxapi.enums.WxMaErrorMsgEnum; |
|||
import org.w3c.dom.Document; |
|||
import org.w3c.dom.Element; |
|||
import org.w3c.dom.NodeList; |
|||
import org.xml.sax.InputSource; |
|||
|
|||
import javax.xml.parsers.DocumentBuilder; |
|||
import javax.xml.parsers.DocumentBuilderFactory; |
|||
import java.io.StringReader; |
|||
|
|||
/** |
|||
* XMLParse class |
|||
* |
|||
* 提供提取消息格式中的密文及生成回复消息格式的接口. |
|||
*/ |
|||
public class XMLParse { |
|||
|
|||
/** |
|||
* 提取出xml数据包中的加密消息 |
|||
* @param xmltext 待提取的xml字符串 |
|||
* @return 提取出的加密消息字符串 |
|||
* @throws AesException |
|||
*/ |
|||
public static String extract(String xmltext) throws AesException { |
|||
// Object[] result = new Object[3];
|
|||
try { |
|||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
|||
DocumentBuilder db = dbf.newDocumentBuilder(); |
|||
StringReader sr = new StringReader(xmltext); |
|||
InputSource is = new InputSource(sr); |
|||
Document document = db.parse(is); |
|||
|
|||
Element root = document.getDocumentElement(); |
|||
NodeList nodelist1 = root.getElementsByTagName("Encrypt"); |
|||
NodeList nodelist2 = root.getElementsByTagName("ToUserName"); |
|||
// result[0] = 0;
|
|||
String result = nodelist1.item(0).getTextContent(); |
|||
// result[2] = nodelist2.item(0).getTextContent();
|
|||
return result; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new RenException(WxMaErrorMsgEnum.CODE_MINUS_40002.getMsg()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 生成xml消息 |
|||
* @param encrypt 加密后的消息密文 |
|||
* @param signature 安全签名 |
|||
* @param timestamp 时间戳 |
|||
* @param nonce 随机字符串 |
|||
* @return 生成的xml字符串 |
|||
*/ |
|||
public static String generate(String encrypt, String signature, String timestamp, String nonce) { |
|||
|
|||
String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" |
|||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" |
|||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" + "</xml>"; |
|||
return String.format(format, encrypt, signature, timestamp, nonce); |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,116 @@ |
|||
/** |
|||
* Copyright 2018 人人开源 https://www.renren.io
|
|||
* <p> |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* <p> |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* <p> |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.epmet.service; |
|||
|
|||
import com.epmet.commons.mybatis.service.BaseService; |
|||
import com.epmet.commons.tools.page.PageData; |
|||
import com.epmet.dto.CodeExtDTO; |
|||
import com.epmet.entity.CodeExtEntity; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 代码第三方配置 |
|||
* |
|||
* @author generator generator@elink-cn.com |
|||
* @since v1.0.0 2020-07-17 |
|||
*/ |
|||
public interface CodeExtService extends BaseService<CodeExtEntity> { |
|||
|
|||
/** |
|||
* 默认分页 |
|||
* |
|||
* @param params |
|||
* @return PageData<CodeExtDTO> |
|||
* @author generator |
|||
* @date 2020-07-17 |
|||
*/ |
|||
PageData<CodeExtDTO> page(Map<String, Object> params); |
|||
|
|||
/** |
|||
* 默认查询 |
|||
* |
|||
* @param params |
|||
* @return java.util.List<CodeExtDTO> |
|||
* @author generator |
|||
* @date 2020-07-17 |
|||
*/ |
|||
List<CodeExtDTO> list(Map<String, Object> params); |
|||
|
|||
/** |
|||
* 单条查询 |
|||
* |
|||
* @param id |
|||
* @return CodeExtDTO |
|||
* @author generator |
|||
* @date 2020-07-17 |
|||
*/ |
|||
CodeExtDTO get(String id); |
|||
|
|||
/** |
|||
* 默认保存 |
|||
* |
|||
* @param dto |
|||
* @return void |
|||
* @author generator |
|||
* @date 2020-07-17 |
|||
*/ |
|||
void save(CodeExtDTO dto); |
|||
|
|||
/** |
|||
* 默认更新 |
|||
* |
|||
* @param dto |
|||
* @return void |
|||
* @author generator |
|||
* @date 2020-07-17 |
|||
*/ |
|||
void update(CodeExtDTO dto); |
|||
|
|||
/** |
|||
* 批量删除 |
|||
* |
|||
* @param ids |
|||
* @return void |
|||
* @author generator |
|||
* @date 2020-07-17 |
|||
*/ |
|||
void delete(String[] ids); |
|||
|
|||
/** |
|||
* 获取配置模板 |
|||
* |
|||
* @param clientType |
|||
* @return java.lang.String |
|||
* @author zhaoqifeng |
|||
* @date 2020/7/17 15:29 |
|||
*/ |
|||
String getExtTemplate(String clientType); |
|||
|
|||
/** |
|||
* 获取客户第三方配置 |
|||
* |
|||
* @param customerId |
|||
* @param clientType |
|||
* @return java.lang.String |
|||
* @author zhaoqifeng |
|||
* @date 2020/7/17 15:29 |
|||
*/ |
|||
CodeExtDTO getExtByCustomer(String customerId, String clientType); |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
/** |
|||
* Copyright 2018 人人开源 https://www.renren.io
|
|||
* <p> |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU General Public License as published by |
|||
* the Free Software Foundation, either version 3 of the License, or |
|||
* (at your option) any later version. |
|||
* <p> |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* <p> |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
package com.epmet.service.impl; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.baomidou.mybatisplus.core.metadata.IPage; |
|||
import com.epmet.commons.mybatis.service.impl.BaseServiceImpl; |
|||
import com.epmet.commons.tools.page.PageData; |
|||
import com.epmet.commons.tools.utils.ConvertUtils; |
|||
import com.epmet.commons.tools.constant.FieldConstant; |
|||
import com.epmet.dao.CodeOperationHistoryDao; |
|||
import com.epmet.dto.CodeOperationHistoryDTO; |
|||
import com.epmet.dto.result.CodeHistoryResultDTO; |
|||
import com.epmet.entity.CodeOperationHistoryEntity; |
|||
import com.epmet.redis.CodeOperationHistoryRedis; |
|||
import com.epmet.service.CodeOperationHistoryService; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 客户代码操作历史 |
|||
* |
|||
* @author generator generator@elink-cn.com |
|||
* @since v1.0.0 2020-07-09 |
|||
*/ |
|||
@Service |
|||
public class CodeOperationHistoryServiceImpl extends BaseServiceImpl<CodeOperationHistoryDao, CodeOperationHistoryEntity> implements CodeOperationHistoryService { |
|||
|
|||
@Autowired |
|||
private CodeOperationHistoryRedis customerCodeOperationHistoryRedis; |
|||
|
|||
@Override |
|||
public PageData<CodeOperationHistoryDTO> page(Map<String, Object> params) { |
|||
IPage<CodeOperationHistoryEntity> page = baseDao.selectPage( |
|||
getPage(params, FieldConstant.CREATED_TIME, false), |
|||
getWrapper(params) |
|||
); |
|||
return getPageData(page, CodeOperationHistoryDTO.class); |
|||
} |
|||
|
|||
@Override |
|||
public List<CodeOperationHistoryDTO> list(Map<String, Object> params) { |
|||
List<CodeOperationHistoryEntity> entityList = baseDao.selectList(getWrapper(params)); |
|||
|
|||
return ConvertUtils.sourceToTarget(entityList, CodeOperationHistoryDTO.class); |
|||
} |
|||
|
|||
private QueryWrapper<CodeOperationHistoryEntity> getWrapper(Map<String, Object> params){ |
|||
String id = (String)params.get(FieldConstant.ID_HUMP); |
|||
|
|||
QueryWrapper<CodeOperationHistoryEntity> wrapper = new QueryWrapper<>(); |
|||
wrapper.eq(StringUtils.isNotBlank(id), FieldConstant.ID, id); |
|||
|
|||
return wrapper; |
|||
} |
|||
|
|||
@Override |
|||
public CodeOperationHistoryDTO get(String id) { |
|||
CodeOperationHistoryEntity entity = baseDao.selectById(id); |
|||
return ConvertUtils.sourceToTarget(entity, CodeOperationHistoryDTO.class); |
|||
} |
|||
|
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public void save(CodeOperationHistoryDTO dto) { |
|||
CodeOperationHistoryEntity entity = ConvertUtils.sourceToTarget(dto, CodeOperationHistoryEntity.class); |
|||
insert(entity); |
|||
} |
|||
|
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public void update(CodeOperationHistoryDTO dto) { |
|||
CodeOperationHistoryEntity entity = ConvertUtils.sourceToTarget(dto, CodeOperationHistoryEntity.class); |
|||
updateById(entity); |
|||
} |
|||
|
|||
@Override |
|||
@Transactional(rollbackFor = Exception.class) |
|||
public void delete(String[] ids) { |
|||
// 逻辑删除(@TableLogic 注解)
|
|||
baseDao.deleteBatchIds(Arrays.asList(ids)); |
|||
} |
|||
|
|||
@Override |
|||
public List<CodeHistoryResultDTO> getHistoryList(String customerId, String clientType) { |
|||
return baseDao.selectHistoryList(customerId, clientType); |
|||
} |
|||
|
|||
@Override |
|||
public void updateDescribe(String codeId, String describe) { |
|||
baseDao.updateDescribeByCodeId(codeId, describe); |
|||
} |
|||
|
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
package com.epmet.util; |
|||
|
|||
import okhttp3.*; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/7 17:39 |
|||
*/ |
|||
@Component |
|||
public class OkHttpHelper { |
|||
|
|||
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); |
|||
|
|||
OkHttpClient client = new OkHttpClient(); |
|||
|
|||
public String post(String url, String json) throws IOException { |
|||
RequestBody body = RequestBody.create(JSON, json); |
|||
Request request = new Request.Builder() |
|||
.url(url) |
|||
.post(body) |
|||
.build(); |
|||
try (Response response = client.newCall(request).execute()) { |
|||
return response.body().string(); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -1,63 +0,0 @@ |
|||
package com.epmet.util; |
|||
|
|||
import com.epmet.commons.tools.constant.NumConstant; |
|||
|
|||
import java.nio.charset.Charset; |
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/6 10:45 |
|||
*/ |
|||
public class PKCS7EncoderUtil { |
|||
|
|||
static Charset CHARSET = Charset.forName("utf-8"); |
|||
static int BLOCK_SIZE = 32; |
|||
|
|||
/** |
|||
* 获得对明文进行补位填充的字节. |
|||
* |
|||
* @param count 需要进行填充补位操作的明文字节个数 |
|||
* @return 补齐用的字节数组 |
|||
*/ |
|||
static byte[] encode(int count) { |
|||
// 计算需要填充的位数
|
|||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); |
|||
if (amountToPad == NumConstant.ZERO) { |
|||
amountToPad = BLOCK_SIZE; |
|||
} |
|||
// 获得补位所用的字符
|
|||
char padChr = chr(amountToPad); |
|||
String tmp = new String(); |
|||
for (int index = NumConstant.ZERO; index < amountToPad; index++) { |
|||
tmp += padChr; |
|||
} |
|||
return tmp.getBytes(CHARSET); |
|||
} |
|||
|
|||
/** |
|||
* 删除解密后明文的补位字符 |
|||
* |
|||
* @param decrypted 解密后的明文 |
|||
* @return 删除补位字符后的明文 |
|||
*/ |
|||
static byte[] decode(byte[] decrypted) { |
|||
int pad = (int) decrypted[decrypted.length - 1]; |
|||
if (pad < NumConstant.ONE || pad > 32) { |
|||
pad = NumConstant.ZERO; |
|||
} |
|||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); |
|||
} |
|||
|
|||
/** |
|||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码 |
|||
* |
|||
* @param a 需要转化的数字 |
|||
* @return 转化得到的字符 |
|||
*/ |
|||
static char chr(int a) { |
|||
byte target = (byte) (a & 0xFF); |
|||
return (char) target; |
|||
} |
|||
|
|||
} |
|||
@ -1,246 +0,0 @@ |
|||
package com.epmet.util; |
|||
|
|||
import com.epmet.exception.AesException; |
|||
import me.chanjar.weixin.common.util.crypto.PKCS7Encoder; |
|||
import org.apache.commons.codec.binary.Base64; |
|||
import org.w3c.dom.Document; |
|||
import org.w3c.dom.Element; |
|||
import org.w3c.dom.NodeList; |
|||
import org.xml.sax.InputSource; |
|||
|
|||
import java.io.StringReader; |
|||
import java.nio.charset.Charset; |
|||
import java.security.MessageDigest; |
|||
import java.util.Arrays; |
|||
|
|||
import javax.crypto.Cipher; |
|||
import javax.crypto.spec.IvParameterSpec; |
|||
import javax.crypto.spec.SecretKeySpec; |
|||
import javax.xml.parsers.DocumentBuilder; |
|||
import javax.xml.parsers.DocumentBuilderFactory; |
|||
|
|||
/** |
|||
* 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串). |
|||
* <ol> * <li>第三方回复加密消息给公众平台</li> * <li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li> |
|||
* </ol> |
|||
* 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案 |
|||
* <ol> |
|||
* <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址: * |
|||
* http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
|
|||
* <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li> |
|||
* <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件</li> |
|||
* <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件</li> |
|||
* |
|||
* </ol> |
|||
*/ |
|||
|
|||
/** |
|||
* @Author zxc |
|||
* @CreateTime 2020/7/6 9:51 |
|||
*/ |
|||
public class WXBizMsgCrypt { |
|||
static Charset CHARSET = Charset.forName("utf-8"); |
|||
Base64 base64 = new Base64(); |
|||
byte[] aesKey; |
|||
String token; |
|||
String appId; |
|||
|
|||
/** |
|||
* 构造函数 |
|||
* @param token 公众平台上,开发者设置的token |
|||
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey |
|||
* @param appId 公众平台appid |
|||
* |
|||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 |
|||
*/ |
|||
public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException { |
|||
if (encodingAesKey.length() != 43) { |
|||
throw new AesException(AesException.IllegalAesKey); |
|||
} |
|||
|
|||
this.token = token; |
|||
this.appId = appId; |
|||
aesKey = Base64.decodeBase64(encodingAesKey + "="); |
|||
} |
|||
|
|||
// 还原4个字节的网络字节序
|
|||
int recoverNetworkBytesOrder(byte[] orderBytes) { |
|||
int sourceNumber = 0; |
|||
for (int i = 0; i < 4; i++) { |
|||
sourceNumber <<= 8; |
|||
sourceNumber |= orderBytes[i] & 0xff; |
|||
} |
|||
return sourceNumber; |
|||
} |
|||
|
|||
/** |
|||
* 对密文进行解密. |
|||
* @param text 需要解密的密文 |
|||
* @return 解密得到的明文 |
|||
* @throws AesException aes解密失败 |
|||
*/ |
|||
String decrypt(String text) throws AesException { |
|||
byte[] original; |
|||
try { |
|||
// 设置解密模式为AES的CBC模式
|
|||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); |
|||
SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); |
|||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); |
|||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); |
|||
|
|||
// 使用BASE64对密文进行解码
|
|||
byte[] encrypted = Base64.decodeBase64(text); |
|||
|
|||
// 解密
|
|||
original = cipher.doFinal(encrypted); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new AesException(AesException.DecryptAESError); |
|||
} |
|||
|
|||
String xmlContent, from_appid; |
|||
try { |
|||
// 去除补位字符
|
|||
byte[] bytes = PKCS7Encoder.decode(original); |
|||
|
|||
// 分离16位随机字符串,网络字节序和AppId
|
|||
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); |
|||
|
|||
int xmlLength = recoverNetworkBytesOrder(networkOrder); |
|||
|
|||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); |
|||
from_appid = |
|||
new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new AesException(AesException.IllegalBuffer); |
|||
} |
|||
|
|||
// appid不相同的情况
|
|||
if (!from_appid.equals(appId)) { |
|||
throw new AesException(AesException.ValidateSignatureError); |
|||
} |
|||
return xmlContent; |
|||
|
|||
} |
|||
|
|||
/** |
|||
* * 检验消息的真实性,并且获取解密后的明文. |
|||
* <ol> |
|||
* <li>利用收到的密文生成安全签名,进行签名验证</li> |
|||
* <li>若验证通过,则提取xml中的加密消息</li> |
|||
* <li>对消息进行解密</li> |
|||
* </ol> |
|||
* |
|||
* @param msgSignature 签名串,对应URL参数的msg_signature |
|||
* @param timeStamp 时间戳,对应URL参数的timestamp |
|||
* @param nonce 随机串,对应URL参数的nonce |
|||
* @param postData 密文,对应POST请求的数据 |
|||
* @return 解密后的原文 |
|||
* @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息 |
|||
*/ |
|||
public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData) |
|||
throws AesException { |
|||
|
|||
// 密钥,公众账号的app secret
|
|||
// 提取密文
|
|||
Object[] encrypt = extract(postData); |
|||
|
|||
// 验证安全签名
|
|||
String signature = getSHA1(token, timeStamp, nonce, encrypt[1].toString()); |
|||
|
|||
// 和URL中的签名比较是否相等
|
|||
// System.out.println("第三方收到URL中的签名:" + msg_sign);
|
|||
// System.out.println("第三方校验签名:" + signature);
|
|||
if (!signature.equals(msgSignature)) { |
|||
throw new AesException(AesException.ValidateSignatureError); |
|||
} |
|||
|
|||
// 解密
|
|||
String result = decrypt(encrypt[1].toString()); |
|||
return result; |
|||
} |
|||
|
|||
/** |
|||
* 提取出xml数据包中的加密消息 |
|||
* @param xmltext 待提取的xml字符串 |
|||
* @return 提取出的加密消息字符串 |
|||
* @throws AesException |
|||
*/ |
|||
public static Object[] extract(String xmltext) throws AesException { |
|||
Object[] result = new Object[3]; |
|||
try { |
|||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
|||
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); |
|||
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); |
|||
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); |
|||
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); |
|||
dbf.setXIncludeAware(false); |
|||
dbf.setExpandEntityReferences(false); |
|||
DocumentBuilder db = dbf.newDocumentBuilder(); |
|||
StringReader sr = new StringReader(xmltext); |
|||
InputSource is = new InputSource(sr); |
|||
Document document = db.parse(is); |
|||
|
|||
Element root = document.getDocumentElement(); |
|||
NodeList nodelist1 = root.getElementsByTagName("Encrypt"); |
|||
NodeList nodelist2 = root.getElementsByTagName("ToUserName"); |
|||
result[0] = 0; |
|||
result[1] = nodelist1.item(0).getTextContent(); |
|||
|
|||
//注意这里,获取ticket中的xml里面没有ToUserName这个元素,官网原示例代码在这里会报空
|
|||
//空指针,所以需要处理一下
|
|||
if (nodelist2 != null) { |
|||
if (nodelist2.item(0) != null) { |
|||
result[2] = nodelist2.item(0).getTextContent(); |
|||
} |
|||
} |
|||
return result; |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new AesException(AesException.ParseXmlError); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 用SHA1算法生成安全签名 |
|||
* @param token 票据 |
|||
* @param timestamp 时间戳 |
|||
* @param nonce 随机字符串 |
|||
* @param encrypt 密文 |
|||
* @return 安全签名 |
|||
* @throws |
|||
* AesException |
|||
*/ |
|||
public static String getSHA1(String token, String timestamp, String nonce, String encrypt) |
|||
throws AesException { |
|||
try { |
|||
String[] array = new String[]{token, timestamp, nonce, encrypt}; |
|||
StringBuffer sb = new StringBuffer(); |
|||
// 字符串排序
|
|||
Arrays.sort(array); |
|||
for (int i = 0; i < 4; i++) { |
|||
sb.append(array[i]); |
|||
} |
|||
String str = sb.toString(); |
|||
// SHA1签名生成
|
|||
MessageDigest md = MessageDigest.getInstance("SHA-1"); |
|||
md.update(str.getBytes()); |
|||
byte[] digest = md.digest(); |
|||
|
|||
StringBuffer hexstr = new StringBuffer(); |
|||
String shaHex = ""; |
|||
for (int i = 0; i < digest.length; i++) { |
|||
shaHex = Integer.toHexString(digest[i] & 0xFF); |
|||
if (shaHex.length() < 2) { |
|||
hexstr.append(0); |
|||
} |
|||
hexstr.append(shaHex); |
|||
} |
|||
return hexstr.toString(); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
throw new AesException(AesException.ComputeSignatureError); |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue