目前,程序存在的问题主要有两个:
-
录入的用户名已存,抛出的异常后没有处理
-
新增员工时,创建人id和修改人id设置为固定值
接下来,我们对上述两个问题依次进行分析和解决。
1.4.1 问题一
描述:录入的用户名已存,抛出的异常后没有处理
分析:
新增username=zhangsan的用户,若employee表中之前已存在。
后台报错信息:
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'zhangsan' for key 'employee.idx_username'
查看employee表结构:
发现,username已经添加了唯一约束,不能重复。
解决:
通过全局异常处理器来处理。
进入到sky-server模块,com.sky.hander包下,GlobalExceptionHandler.java添加方法
/**
* 处理SQL异常
* @param ex
* @return
*/
@ExceptionHandler
public Result exceptionHandler(SQLIntegrityConstraintViolationException ex){
//Duplicate entry 'zhangsan' for key 'employee.idx_username'
String message = ex.getMessage();
if(message.contains("Duplicate entry")){
String[] split = message.split(" ");
String username = split[2];
String msg = username + MessageConstant.ALREADY_EXISTS;
return Result.error(msg);
}else{
return Result.error(MessageConstant.UNKNOWN_ERROR);
}
}
/** * 处理SQL异常 * * @param ex * @return contains:包含 * Duplicate entry:重复的健值对 */ @ExceptionHandler public Result exceptionHandler(SQLIntegrityConstraintViolationException ex) { //Duplicate entry 'zhangsan' for key 'employee.idx_username' //获取异常信息 String message = ex.getMessage(); /** * 如果包含Duplicate entry * 就可以确认输出的日志信息就是Duplicate entry 'zhangsan' for key 'employee.idx_username' * 这个时候 我们希望给前端一个提示 * 提示zhangsan用户名已经存在 应该返回一个提示信息 * 所以我们这个就要动态的把'zhangsan'提出出来 * 我们观察这个信息Duplicate entry 'zhangsan' for key 'employee.idx_username'是通过空格进行分隔 分隔完之后 我们发现zhangsan是在第三个 */ //判断是否包含Duplicate entry if (message.contains("Duplicate entry")) { //根据空格"" 进行分隔 得到一个数组对象 String[] split = message.split(""); //zhangsan是在数组对象里面的第三个 所以就取到用户名 String username = split[2]; //用户名已经存地 String msg = username + MessageConstant.ALREADY_EXISTS; //把msg 用户名已存在封装到Result返回 return Result.error(msg); }else { return Result.error(MessageConstant.UNKNOWN_ERROR); } }
进入到sky-common模块,在MessageConstant.java添加
public static final String ALREADY_EXISTS = "已存在";
再次,接口测试:
控制台就不会有异常信息
因为数据库抛出异常之后 被我们的异常处理方法给捕获了 捕获之后 经过正常的处理
给前端返回一个提示“用户名已存在”数据
1.4.2 问题二
描述:新增员工时,创建人id和修改人id设置为固定值
分析:
/**
* 新增员工
*
* @param employeeDTO
*/
public void save(EmployeeDTO employeeDTO) {
Employee employee = new Employee();
//................
//当前设置的id为固定值10//
employee.setCreateUser(10L);
employee.setUpdateUser(10L);
//
//.................................employeeMapper.insert(employee);//后续步骤定义
}
解决:
通过某种方式动态获取当前登录员工的id。
员工登录成功后会生成JWT令牌并响应给前端:
前端发起请求 把用户名和密码发给后端 后端就可以进行校验
比如进行密码的比对 假设比对成功了 这个时候就会生成一个令牌
也就是JWT的一个token ,然后把token返回给我们的客户端
客户端就会把token保存下来 ,后续的请求都会在请求头携带token
请求过来之后 首先被我们的拦截器 也就是我们进行token校验的一个拦截器
给拦截到 然后在这个地方来检查token
检查之后 来嘛通过 要嘛不通过
如果通过 就放行 去正常执行 我们的业务逻辑 如果不通过 我们程序就会返回错误信息
直接返回一个401的状态码,401的状态码就是没有授权,然后把状态码返回给我们前端
前端在去展示相对应的错误信息 并且跳转到登录页面
在sky-server模块
package com.sky.controller.admin;
/**
* 员工管理
*/
@RestController
@RequestMapping("/admin/employee")
@Slf4j
@Api(tags = "员工相关接口")
public class EmployeeController {@Autowired
private EmployeeService employeeService;
@Autowired
private JwtProperties jwtProperties;/**
* 登录
*
* @param employeeLoginDTO
* @return
*/
@PostMapping("/login")
@ApiOperation(value = "员工登录")
public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
//.........//登录成功后,生成jwt令牌
Map<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
String token = JwtUtil.createJWT(
jwtProperties.getAdminSecretKey(),
jwtProperties.getAdminTtl(),
claims);//............
return Result.success(employeeLoginVO);
}}
后续请求中,前端会携带JWT令牌,通过JWT令牌可以解析出当前登录员工id:
JwtTokenAdminInterceptor.java
package com.sky.interceptor;
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {@Autowired
private JwtProperties jwtProperties;/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//..............
//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getAdminTokenName());//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
log.info("当前员工id:", empId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}
思考:解析出登录员工id后,如何传递给Service的save方法?
通过ThreadLocal进行传递。
一次请求一个线程
客户端每一次发请的请求都是单独的线程