Java秒杀系统方案优化 高性能高并发实战项目笔记四——结合MD5实现登录+JSR303参数校验+全局异常处理

在写本篇博客的时候,是在学完了分布式Session之后开始学的,在学的过程中也遇到了一点点阻力,我发现我平时代码的写法和这个老师讲的不一样的地方挺多的,比如现在引进了全局异常处理,我之前是不写异常处理类,直接return一个结果回来,看了好多视频都是用异常类处理的,可能这种方式真的比较好吧

本篇博客分三方面讲述:

  1. 登录模块
  2. JSR303参数校验
  3. 全局异常处理

登录模块:

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位
配置方式:
  1. 先写出ValidatorUtil类,写出要判断bean中数据的规范,比如下面说的是手机号1开头且长度为11位的
  2. 写出适配器,由两个类组成,分别是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把错误抛出来
实现这个方式的步骤主要为两步:

  1. 定义一个继承自RuntimeException的全局异常子类GlobalException
  2. 拦截异常类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);
        }

写到这里差不多把这个章节总结的比较清晰了,一天写了好几篇博客,如果大家觉得不错,多点点关注哦!
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值