高并发秒杀项目——实现登录
数据库设计
明文两次MD5加密
用户端:PASS=MD5(明文+固定Salt)
服务端:PASS=MD5(用户输入+随机Salt)
1、引入依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
2、MD5Util工具类
两次MD5加密,首先写一个固定的salt。
第一次MD5,将salt与用户输入的密码做一个拼装,然后将拼装后的密码传入服务端。
第二次MD5,生成一个随机的salt,与经过第一次MD5加密后的密码进行拼装
JSR303参数校验+全局异常处理器
系统在登录的时候做了一个参数校验,也就是说每一个方法的开头都要去做一个校验,为了避免反复做校验,使用JSR 303 参数校验。
1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2、在需要校验的参数前加注解@NotNull、@Length(min=32)进行验证(不能为空、密码最小值为32)
3、自定义@IsMobile验证器
新建注解类IsMobile,引入相应的规则,新建一个IsMobileValidator类,继承ConstraintValidator校验器,首先initialize初始化方法,初始化的时候可以拿到IsMobile注解,然后查看值是否为必须的,如果是必须的,那么isValid会进行验证逻辑。
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if (required){//查看值是否为必须
return ValidatorUtil.isMobile(value);
}else {
if (StringUtils.isEmpty(value)){
return true;
}else {
return ValidatorUtil.isMobile(value);
}
}
}
使用JSP303校验器后,输入有错误,抛出异常,为使异常在客户端显示的友好些,使用全局异常处理。全集异常处理可以实现对项目中所有产生的异常进行拦截,在同一个类中实现统一处理。避免异常漏处理的情况。
分布式Session
用户登录成功之后,给这个用户生成一个session Id(用token来标识这个用户),写到cookie中,传递给客户端。然后客户端在随后的访问中,都在cookie中上传这个token,然后服务端拿到这个token之后,就根据这个token来取得对应的session信息。
UUID生成token
public class UUIDUtil {
public static String uuid(){
return UUID.randomUUID().toString().replace("-", "");
}
}
在MiaoshaUserService类中定义一个Cookie的(name、value),设置有效期、网站的根目录。然后使用response.addCookie,将Cookie写入到客户端。
public void addCookie(HttpServletResponse response, String token, MiaoshaUser user){
redisService.set(MiaoshaUserKey.token, token, user);
Cookie cookie = new Cookie(COOKIE_NAME_TOKEN, token);
cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
}
在MiaoshaUserKey类中 ,设置有效期
public class MiaoshaUserKey extends BasePrefix{
public static final int TOKEN_EXPIRE=3600*24*2;//2天
public MiaoshaUserKey(int expireSeconds,String prefix) {
super(expireSeconds,prefix);
}
public static MiaoshaUserKey token=new MiaoshaUserKey(TOKEN_EXPIRE,"tk");
//对象缓存一般没有有效期,永久有效
public static MiaoshaUserKey getById=new MiaoshaUserKey(0,"id");
}