前言
最近接触到尚医通这个项目,其实这个项目确实有一些让人诟病的部分,所以我在看课程学习项目的同时也对自己感觉可以优化的部分进行优化,后面也会出一些关于这个项目的问题解决以及自己的解决方案,本篇博客仅是对签名校验的实现进行自己的思路重构,欢迎读者指正~
问题提出
原本的项目代码真的可以说是很冗余了,在每一个接口方法前面都需要经过签名校验,即业务逻辑代码200行,校验就单独做了100行,那么如何避免这种尴尬的情况呢?我不禁想到AOP(面向切面编程)就可以解决此类问题
原代码
@RestController
@RequestMapping("/api/hosp")
public class ApiController {
@Autowired
private HospitalService hospitalService;
@Autowired
private HospitalSetService hospitalSetService;
@Autowired
private DepartmentService departmentService;
@Autowired
private ScheduleService scheduleService;
//上传医院接口
@PostMapping("saveHospital")
public Result saveHosp(HttpServletRequest request){
//获取医院传递过来的信息
Map<String, String[]> parameterMap = request.getParameterMap();
//为避免后面遍历,将map中的String[]转换成Object
Map<String, Object> parampMap = HttpRequestHelper.switchMap(parameterMap);
//核验签名是否一致
//1.获取医院系统传递过来的签名
String hospSign = (String) parampMap.get("sign");
//2.根据传递过来的医院编码,查询数据库,查询签名
String hoscode = (String) parampMap.get("hoscode");
String signKey = hospitalSetService.getSignKey(hoscode);
//3.把查询出来的签名进行MD5加密
String signKeyMD5 = MD5.encrypt(signKey);
//4.判断签名是否一致
if(!hospSign.equals(signKeyMD5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
//图片数据采取base64工具类传输,在传输过程中“+”转换为了“ ”,因此我们要转换回来
String logoData = (String) parampMap.get("logoData");
logoData = logoData.replace(" ","+");
parampMap.put("logoData",logoData);
//调用service的方法
hospitalService.save(parampMap);
return Result.ok();
}
//查询医院接口
@PostMapping("hospital/show")
public Result getHospital(HttpServletRequest request){
//获取医院传递过来的信息
Map<String, String[]> parameterMap = request.getParameterMap();
//为避免后面遍历,将map中的String[]转换成Object
Map<String, Object> parampMap = HttpRequestHelper.switchMap(parameterMap);
//获取传递过来的医院编号
String hoscode = (String) parampMap.get("hoscode");
//签名校验
String hospSign = (String) parampMap.get("sign");
String signKey = hospitalSetService.getSignKey(hoscode);
String signKeyMD5 = MD5.encrypt(signKey);
if(!hospSign.equals(signKeyMD5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
//调用service方法
Hospital hospital = hospitalService.getByHoscode(hoscode);
return Result.ok(hospital);
}
//上传科室接口
@PostMapping("saveDepartment")
public Result saveDepartment(HttpServletRequest request){
//获取传递过来的科室信息
Map<String, String[]> parameterMap = request.getParameterMap();
Map<String, Object> parampMap = HttpRequestHelper.switchMap(parameterMap);
//核验签名是否一致
String hospSign = (String) parampMap.get("sign");
String hoscode = (String) parampMap.get("hoscode");
String signKey = hospitalSetService.getSignKey(hoscode);
String signKeyMD5 = MD5.encrypt(signKey);
if(!hospSign.equals(signKeyMD5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
//调用service方法
departmentService.save(parampMap);
return Result.ok();
}
//科室查询接口
@PostMapping("department/list")
public Result findDepartment(HttpServletRequest request){
//获取传递过来的科室信息
Map<String, String[]> parameterMap = request.getParameterMap();
Map<String, Object> parampMap = HttpRequestHelper.switchMap(parameterMap);
//医院编号
String hoscode = (String) parampMap.get("hoscode");
//当前页
int page = Integer.parseInt((String) parampMap.get("page"));
if(StringUtils.isEmpty(page)){
page = 1;
}
//每页显示记录数
int limit = Integer.parseInt((String) parampMap.get("limit"));
if(StringUtils.isEmpty(limit)){
limit = 1;
}
//签名校验
String hospSign = (String) parampMap.get("sign");
String signKey = hospitalSetService.getSignKey(hoscode);
String signKeyMD5 = MD5.encrypt(signKey);
if(!hospSign.equals(signKeyMD5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
//调用service查询
//查询条件的值封装到departmentQueryVo中
DepartmentQueryVo departmentQueryVo = new DepartmentQueryVo();
departmentQueryVo.setHoscode(hoscode);
Page<Department> pageModel = departmentService.finPageDepartment(page,limit,departmentQueryVo);
return Result.ok(pageModel);
}
//删除科室接口
@PostMapping("department/remove")
public Result removeDepartment(HttpServletRequest request){
//获取传递过来的科室信息
Map<String, String[]> parameterMap = request.getParameterMap();
Map<String, Object> parampMap = HttpRequestHelper.switchMap(parameterMap);
//获取医院编号和科室编号
String hoscode = (String) parampMap.get("hoscode");
String depcode = (String) parampMap.get("depcode");
//签名校验
String hospSign = (String) parampMap.get("sign");
String signKey = hospitalSetService.getSignKey(hoscode);
String signKeyMD5 = MD5.encrypt(signKey);
if(!hospSign.equals(signKeyMD5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
departmentService.remove(hoscode,depcode);
return Result.ok();
}
//上传排班
@PostMapping("saveSchedule")
public Result saveSchedule(HttpServletRequest request){
//获取传递过来的科室信息
Map<String, String[]> parameterMap = request.getParameterMap();
Map<String, Object> parampMap = HttpRequestHelper.switchMap(parameterMap);
//签名校验
String hospSign = (String) parampMap.get("sign");
String hoscode = (String) parampMap.get("hoscode");
String signKey = hospitalSetService.getSignKey(hoscode);
String signKeyMD5 = MD5.encrypt(signKey);
if(!hospSign.equals(signKeyMD5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
//调用service方法
scheduleService.save(parampMap);
return Result.ok();
}
//查询排班
@PostMapping("schedule/list")
public Result findSchedule(HttpServletRequest request) {
//获取传递过来的科室信息
Map<String, String[]> parameterMap = request.getParameterMap();
Map<String, Object> parampMap = HttpRequestHelper.switchMap(parameterMap);
//医院编号
String hoscode = (String) parampMap.get("hoscode");
//科室编号
String depcode = (String) parampMap.get("depcode");
//当前页
int page = Integer.parseInt((String) parampMap.get("page"));
if (StringUtils.isEmpty(page)) {
page = 1;
}
//每页显示记录数
int limit = Integer.parseInt((String) parampMap.get("limit"));
if (StringUtils.isEmpty(limit)) {
limit = 1;
}
//签名校验
String hospSign = (String) parampMap.get("sign");
String signKey = hospitalSetService.getSignKey(hoscode);
String signKeyMD5 = MD5.encrypt(signKey);
if (!hospSign.equals(signKeyMD5)) {
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
//调用service查询
//查询条件的值封装到departmentQueryVo中
ScheduleQueryVo scheduleQueryVo = new ScheduleQueryVo();
scheduleQueryVo.setHoscode(hoscode);
scheduleQueryVo.setDepcode(depcode);
Page<Schedule> pageModel = scheduleService.finPageSchedule(page, limit, scheduleQueryVo);
return Result.ok(pageModel);
}
//删除排班
@PostMapping("schedule/remove")
public Result remove(HttpServletRequest request){
//获取传递过来的科室信息
Map<String, String[]> parameterMap = request.getParameterMap();
Map<String, Object> parampMap = HttpRequestHelper.switchMap(parameterMap);
//获取医院编号和排班编号
String hoscode = (String) parampMap.get("hoscode");
String hosScheduleId = (String) parampMap.get("hosScheduleId");
//签名校验
String hospSign = (String) parampMap.get("sign");
String signKey = hospitalSetService.getSignKey(hoscode);
String signKeyMD5 = MD5.encrypt(signKey);
if(!hospSign.equals(signKeyMD5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
scheduleService.remove(hoscode,hosScheduleId);
return Result.ok();
}
}
具体实现
通过看上面的代码可以说是非常冗余了,于是我通过注解切面的形式做了一个小优化
实现步骤也非常简单:
- 定义签名校验注解
- 定义切面,并且做切入点的系统服务
- 在需要校验的方法上添加注解即可
- 自定义注解
//自定义注解:签名校验
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) //运行时生效
public @interface SignatureValidation {
}
- 定义切面
@Aspect
@Component
public class SignatureValidation {
@Autowired
private HospitalSetService hospitalSetService;
//切入点
@Pointcut("@annotation(com.hang.aspectj.annotation.SignatureValidation)")
public void signPointcut(){
}
//签名校验
@Before("signPointcut()")
public void doBefore(){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
Map<String, Object> paramMap = HttpRequestHelper.switchMap(request.getParameterMap());
//必须参数校验
String hoscode = (String) paramMap.get("hoscode");
if(StringUtils.isEmpty(hoscode)) {
throw new YyghException(ResultCodeEnum.PARAM_ERROR);
}
//进行签名校验
String signKey = hospitalSetService.getSignKey(hoscode);
//对数据库中取出的签名进行加密
String signKeyMd5 = MD5.encrypt(signKey);
String hosSign=(String) paramMap.get("sign");
//判断签名是否一致
if(!hosSign.equals(signKeyMd5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
}
}
- 添加注解实现(实现后的代码)
@RestController
@RequestMapping("/api/hosp")
public class ApiController {
@Autowired
private HospitalService hospitalService;
@Autowired
private HospitalSetService hospitalSetService;
@Autowired
private DepartmentService departmentService;
@Autowired
private ScheduleService scheduleService;
//上传医院接口
@PostMapping("saveHospital")
public Result saveHospital(HttpServletRequest request){
//首先获取传递过来的信息
//将传递的String[]信息转为Object
Map<String, Object> parameterMap = HttpRequestHelper.switchMap(request.getParameterMap());
//图片数据采取base64工具类传输,在传输过程中"+"转换为了" ",因此我们要转换回来
String logoData = (String) parameterMap.get("logoData");
logoData = logoData.replace(" ","+");
parameterMap.put("logoData",logoData);
//调用service方法进行保存
hospitalService.save(parameterMap);
//返回结果
return Result.ok();
}
@PostMapping("hospital/show")
@SignatureValidation
public Result hospital(HttpServletRequest request) {
Map<String, Object> paramMap = HttpRequestHelper.switchMap(request.getParameterMap());
//将request过来的参数进行解析获取
return Result.ok(hospitalService.getByHoscode((String)paramMap.get("hoscode")));
}
//上传科室接口
@PostMapping("saveDepartment")
@SignatureValidation
public Result saveDepartment(HttpServletRequest request) {
Map<String, Object> paramMap = HttpRequestHelper.switchMap(request.getParameterMap());
departmentService.save(paramMap);
return Result.ok();
}
//查询科室接口
@PostMapping("department/list")
@SignatureValidation
public Result department(HttpServletRequest request) {
Map<String, Object> paramMap = HttpRequestHelper.switchMap(request.getParameterMap());
//必须参数校验
String hoscode = (String)paramMap.get("hoscode");
//非必填
String depcode = (String)paramMap.get("depcode");
int page = StringUtils.isEmpty(paramMap.get("page")) ? 1 : Integer.parseInt((String)paramMap.get("page"));
int limit = StringUtils.isEmpty(paramMap.get("limit")) ? 10 : Integer.parseInt((String)paramMap.get("limit"));
DepartmentQueryVo departmentQueryVo = new DepartmentQueryVo();
departmentQueryVo.setHoscode(hoscode);
departmentQueryVo.setDepcode(depcode);
Page<Department> pageModel = departmentService.selectPage(page, limit, departmentQueryVo);
return Result.ok(pageModel);
}
//删除科室接口
@PostMapping("department/remove")
@SignatureValidation
public Result removeDepartment(HttpServletRequest request) {
Map<String, Object> paramMap = HttpRequestHelper.switchMap(request.getParameterMap());
//必须参数校验
String hoscode = (String) paramMap.get("hoscode");
//必填
String depcode = (String) paramMap.get("depcode");
departmentService.remove(hoscode, depcode);
return Result.ok();
}
//上传排班信息
@PostMapping("saveSchedule")
@SignatureValidation
public Result saveSchedule(HttpServletRequest request) {
Map<String, Object> paramMap = HttpRequestHelper.switchMap(request.getParameterMap());
scheduleService.save(paramMap);
return Result.ok();
}
//查询排版信息接口
@PostMapping("schedule/list")
@SignatureValidation
public Result getSchedules(HttpServletRequest request){
//查询参数
Map<String, Object> paramMap = HttpRequestHelper.switchMap(request.getParameterMap());
String hoscode = (String) paramMap.get("hoscode");
//分页查询数据
String depcode=(String) paramMap.get("depcode");
//非必填数据
int page = StringUtils.isEmpty(paramMap.get("page")) ? 1 : Integer.parseInt((String)paramMap.get("page"));
int limit = StringUtils.isEmpty(paramMap.get("limit")) ? 10 : Integer.parseInt((String)paramMap.get("limit"));
ScheduleQueryVo scheduleQueryVo = new ScheduleQueryVo();
scheduleQueryVo.setHoscode(hoscode);
scheduleQueryVo.setDepcode(depcode);
Page<Schedule> pageModel = scheduleService.selectPage(page, limit, scheduleQueryVo);
return Result.ok(pageModel);
}
//删除排班接口:由于排班号并不唯一,因此不需要进行额外的唯一校验,因此仅需要进行签名校验
@PostMapping("schedule/remove")
public Result removeSchedule(HttpServletRequest request) {
Map<String, Object> paramMap = HttpRequestHelper.switchMap(request.getParameterMap());
String hoscode = (String)paramMap.get("hoscode");
//必填
String hosScheduleId = (String)paramMap.get("hosScheduleId");
//签名校验
String hospSign = (String) paramMap.get("sign");
String signKey = hospitalSetService.getSignKey(hoscode);
String signKeyMD5 = MD5.encrypt(signKey);
if(!hospSign.equals(signKeyMD5)){
throw new YyghException(ResultCodeEnum.SIGN_ERROR);
}
scheduleService.remove(hoscode, hosScheduleId);
return Result.ok();
}
}
代码整体减少了50~100行,而且逻辑也清晰了许多
总结
在学习的过程中不断总结,不断进步,学会将知识进行活学活用,才能立足~