在写本篇博客的时候,是在学完了分布式Session之后开始学的,在学的过程中也遇到了一点点阻力,我发现我平时代码的写法和这个老师讲的不一样的地方挺多的,比如现在引进了全局异常处理,我之前是不写异常处理类,直接return一个结果回来,看了好多视频都是用异常类处理的,可能这种方式真的比较好吧
本篇博客分三方面讲述:
- 登录模块
- JSR303参数校验
- 全局异常处理
登录模块:
MD5处理逻辑:
前端先从表单获取密码值,用JS的MD5封装一次得到formPass,讲formPass传到服务器端,服务器端再进行一次MD5处理,与数据库查出来的进行对比。
前端JS代码:
function doLogin(){
g_showLoading();
var inputPass = $("#password").val();
var salt = g_passsword_salt;
var str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);
var password = md5(str);
$.ajax({
url: "/login/do_login",
type: "POST",
data:{
mobile:$("#mobile").val(),
password: password
},
success:function(data){
layer.closeAll();
// console.log(data);
if(data.code == 0){
layer.msg("登录成功");
window.location.href="/goods/to_list";
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.closeAll();
}
});
}
Controller层:(优化了参数校验方式,这边最原先的参数判断方式先注释掉)
@Controller
@RequestMapping("/login")
public class LoginController {
private static Logger logger = LoggerFactory.getLogger(LoginController.class);
@Autowired
MiaoshaUserService miaoshaUserService;
@RequestMapping("/to_login")
public String toLogin() {
return "login";
}
@RequestMapping("/do_login")
@ResponseBody
public Result<Boolean> doLogin(HttpServletResponse response, @Valid LoginVo loginVo) {
logger.info(loginVo.toString());
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
// if(StringUtils.isEmpty(mobile)) {
// return Result.error(CodeMsg.MOBILE_EMPTY);
// }
//
// if(StringUtils.isEmpty(password)) {
// return Result.error(CodeMsg.PASSWORD_EMPTY);
// }
//
// if(!ValidatorUtil.isMobile(mobile)) {
// return Result.error(CodeMsg.MOBILE_ERROR);
// }
//登录
// CodeMsg codeMsg = miaoshaUserService.login(loginVo);
miaoshaUserService.login(response,loginVo);
return Result.success(true);
}
}
Service层:
public boolean login(HttpServletResponse response,LoginVo loginVo) {
if(loginVo == null) {
throw new GlobalException(CodeMsg.SERVER_ERROR);
}
String mobile = loginVo.getMobile();
String fromPass = loginVo.getPassword();
long ll = Long.parseLong(mobile);
// 判断手机号(用户)是否存在
MiaoshaUser miaoshaUser = miaoshaUserDao.getById(ll);
if(miaoshaUser == null) {
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}
// 验证密码
// 数据库里的密码
String dbPass = miaoshaUser.getPassword();
// 数据库里的固态salt值
String dbSalt = miaoshaUser.getSalt();
// 计算出来的pass
String calcPass = MD5Util.formPassToDBPass(fromPass, dbSalt);
if(!calcPass.equals(dbPass)) {
throw new GlobalException(CodeMsg.PASSWORD_ERROR);
}
addCookie(response,miaoshaUser);
// 成功
return true;
}
JSR303参数校验
参考自:JSR303参数校验
作用: JSR303方便了前端向后端传递参数的过程中,校验前端的参数是否符合规则,改变了传统的在Controller层出现的大量判断参数的情况
登录中出现的参数校验:其中@IsMobile是自定义校验,需要自己配置,下面是登录Bean:
@NotNull
@IsMobile
private String mobile;
@NotNull
@Length(max = 32)
private String password;
下面是常用的一些JSR303校验注解:
参考自:JSR 303校验常用注解
下面介绍@IsMobile自定义注解的配置,主要验证手机号是不是11位
配置方式:
- 先写出ValidatorUtil类,写出要判断bean中数据的规范,比如下面说的是手机号1开头且长度为11位的
- 写出适配器,由两个类组成,分别是IsMobile类和IsMobileValidator类,两个一个都不能少
ValidatorUtil类:
public class ValidatorUtil {
private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}");
public static boolean isMobile(String src) {
if(StringUtils.isEmpty(src)) {
return false;
}
Matcher m = mobile_pattern.matcher(src);
return m.matches();
}
public static void main(String[] args) {
System.out.println(isMobile("18912341234"));
System.out.println(isMobile("1891234123"));
}
}
IsMobile类:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {IsMobileValidator.class}
)
public @interface IsMobile {
/**
* 默认是必须要填的
*
* @return
*/
boolean required() default true;
String message() default "手机号码格式有误";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
IsMobileValidator类:
public class IsMobileValidator implements ConstraintValidator<IsMobile,String> {
private boolean required = false;
@Override
public void initialize(IsMobile constraintAnnotation) {
required = constraintAnnotation.required();
}
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
// 如果是必须的
if(required) {
return ValidatorUtil.isMobile(s);
} else {
//如果不是必须的
if(StringUtils.isEmpty(s)) {
return true;
} else {
return ValidatorUtil.isMobile(s);
}
}
}
}
全局异常处理
这种异常的写法我之前是没有使用过的,如果哪一个不符合条件,间接地通过错误码CodeMsg把错误抛出来
实现这个方式的步骤主要为两步:
- 定义一个继承自RuntimeException的全局异常子类GlobalException
- 拦截异常类GlobalExceptionHandle
GlobalException类:
public class GlobalException extends RuntimeException {
private static final long serialVersionUID = 1L;
private CodeMsg cm;
public GlobalException(CodeMsg cm) {
this.cm = cm;
}
public CodeMsg getCm() {
return cm;
}
}
GlobalExceptionHandle类:
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandle {
/**
* 拦截任何的异常
*
* @return
*/
@ExceptionHandler(value = Exception.class)
public Result<String> exceptionHandle(HttpServletRequest request, Exception e) {
e.printStackTrace();
if(e instanceof GlobalException) {
GlobalException ex = (GlobalException)e;
return Result.error(ex.getCm());
}else if(e instanceof BindException) {
BindException ex = (BindException)e;
// 参数错误
List<ObjectError> errors = ex.getAllErrors();
ObjectError error = errors.get(0);
String msg = error.getDefaultMessage();
return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg));
}else {
return Result.error(CodeMsg.SERVER_ERROR);
}
}
}
比如说在业务层不满足条件就抛出异常:
if(loginVo == null) {
throw new GlobalException(CodeMsg.SERVER_ERROR);
}
String mobile = loginVo.getMobile();
String fromPass = loginVo.getPassword();
long ll = Long.parseLong(mobile);
// 判断手机号(用户)是否存在
MiaoshaUser miaoshaUser = miaoshaUserDao.getById(ll);
if(miaoshaUser == null) {
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}