DROP TABLE IF EXISTS `miaosha_user`;
CREATE TABLE `miaosha_user` (
`id` BIGINT(20) NOT NULL,
`nickname` VARCHAR(255) NOT NULL,
`pwd` VARCHAR(32) DEFAULT NULL,
`salt` VARCHAR(10) DEFAULT NULL, //password的盐
`head` VARCHAR(128) DEFAULT NULL, //头像
`register_date` DATETIME DEFAULT NULL, //注册时间
`last_login_date` DATETIME DEFAULT NULL, //上次登录时间
`login_count` INT(11) DEFAULT '0', //登录次数
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
public class MiaoshaUser {
private Long id;
private String nickname;
private String pwd;
private String salt;
private String head;
private Date registerDate;
private Date lastLoginDate;
private Integer loginCount;
.....
}
@Mapper
public interface MiaoshaUserDao {
@Select("select * from miaosha_user where id=#{id}") //这里#{id}通过后面参数来为其赋值
public MiaoshaUser getById(@Param("id") long id); //绑定
//绑定在对象上面了----@Param("id")long id,@Param("pwd")long pwd 效果一致
@Update("update miaosha_user set pwd=#{pwd} where id=#{id}")
public void update(MiaoshaUser toupdateuser);
//public boolean update(@Param("id")long id); //绑定
}
http在网络上是明文传输的。所以要加密
第一次密码与固定的salt拼接进行MD5加密得到的结果是str1,再对str1与随机的salt进行MD5加密得到真正要存到数据库的密码。并将2次MD5之后的密码和随机的salt存到数据库中。使用两次MD5的原因是,如果只有一次MD5的话,容易被彩虹表破解,两次MD5再加上随机的salt,破解密码的难度大大增大。
commons-codec
commons-codec
1.9
org.apache.commons
commons-lang3
3.6
//form表单登录后拿到的经过一次md5的密码和mobile
public class LoginEntity{
private String mobile;
private String password;
......
}
- 登录
form表单点击登录按钮,得到一个LoginEntity对象,LoginEntity对象有两个参数,一个是经过一次MD5后的密码,一个是手机号。该数据传递到后台服务器。- 普通验证
验证手机号,密码是否为空,再验证手机号的格式是否正确(手机号的长度,开头是否为1),验证密码的格式(密码的长度是32位)。- 进一步验证:根据手机号得到对应的MiaoshaUser对象,判断该对象是否存在,即是不是已注册对象,判断的思路是:先从缓存中找,找到了返回,再从MiaoshaUser表中去找,找到了返回,找不到返回null
- 2次MD5密码验证,将从LoginEntity对象得到的密码进行一次MD5,(salt是从数据库取出的),与得到的MiaoshaUser对象的密码进行验证,返回验证结果。
对应前端代码:
$.validator.setDefaults( {
submitHandler: function () {
var pass=$("#password").val();
//pass='111111';
var salt='1a2b3c4d';
var str=""+salt.charAt(0)+salt.charAt(2)+pass+salt.charAt(5)+salt.charAt(4);
var password=md5(str);
//alert(salt);
//alert(pass);
//alert(password);
//与后台Md5规则一致
//var str=""+salt.charAt(0)+salt.charAt(2)+formPass+salt.charAt(5)+salt.charAt(4);
$.ajax({
url:"/login/do_login",
type:"POST",
data:{
mobile:$("#phone").val(),
password:password,
},
success:function(data){
if(data.code==0){
alert("success");
//成功后跳转
window.location.href="/goods/to_list";
}else{
alert(data.msg);
}
},
error:function(data){
alert("error");
//alert(data.msg);
}
});
负责登录的验证
@Controller
public class LoginController {
@Autowired
MiaoShaUserService miaoShaUserService;
@RequestMapping("/login/to_login")
public String doLogin(){
return "login";
}
@RequestMapping("/login/do_login")
@ResponseBody
public Result judge_Login(HttpServletResponse response, LoginEntity loginEntity){
Result login = miaoShaUserService.Login(response, loginEntity);
return login;
}
}
jQuery.validator 验证规则详解
public Result doLogin(HttpServletResponse response,@Valid LoginVo loginVo)
public class LoginVo {
private String mobile;
private String password;
@NotNull
@IsMobile
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
@NotNull
@Length(min=32)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { IsMobileValidator.class })//继承校验器
public @interface IsMobile {
boolean required() default true; //默认手机号有没有
String message() default "手机号码格式有误!";//不通过给出的信息
Class>[] groups() default { };
Class extends Payload>[] payload() default { };
}
public class IsMobileValidator implements ConstraintValidator{
private boolean required=false;
//是否可以为空
public void initialize(IsMobile constraintAnnotation) {
constraintAnnotation.required();
}
public boolean isValid(String value, ConstraintValidatorContext context) {
if(required) {//查看值是否是必须的
return ValidatorUtil.isMobile(value);
}else {
if(StringUtils.isEmpty(value)) {//required
return true;
}else {
return ValidatorUtil.isMobile(value);
}
}
}
}
它能够让你统一在一个地方处理应用程序所发生的所有异常。
而不是是在单个的控制器中分别处理。
这个注解的功能是:自动捕获controller层出现的指定类型异常,并对该异常进行相应的异常处理.
if(e instanceof BindException) {//是绑定异常的情况
//强转
BindException ex=(BindException) e;
//获取错误信息
List 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);
}
public class GlobalException extends RuntimeException{
private static final long serialVersionUID = 1L;
private CodeMsg cm;
public GlobalException(CodeMsg cm){
super(cm.toString());
this.cm=cm;
}
public CodeMsg getCm() {
return cm;
}
public void setCm(CodeMsg cm) {
this.cm = cm;
}
}
有异常直接抛出异常,全局处理器拦截到异常会去处理异常
MiaoshaUser user=getById(id);
if(user==null) {
throw new GlobalException(CodeMsg.MOBILE_NOTEXIST);
}
用户的第一个请求落到了第一个服务器,用户的第二个请求落到了第二个服务器,用户的session信息就会丢失
登录成功后,生成token,与对应的user存到redis中,再把token添加进cookie中。
- 通过cookie拿到sessionID,根据sessiodID在redis中取到对应的MiaoshaUser
public Result Login(HttpServletResponse response, LoginEntity loginEntity) {
.................
//添加sessionID信息
public void addCookie(HttpServletResponse response, MiaoshaUser miaoshaUser, String token) {
service.set(MiaoshaUserKey.token, token, miaoshaUser);
Cookie cookie = new Cookie(COOKIE_NAME, token);
cookie.setMaxAge(MiaoshaUserKey.token.getExpireTime());
cookie.setPath("/");
response.addCookie(cookie);
}
//根据sessionID从数据库中取出对应的MiaoshaUSser
public MiaoshaUser getByToken(String token, HttpServletResponse response) {
//token的有效期是最后一次登录加上token的寿命
if (!StringUtils.isEmpty(token)) {
return null;
}
MiaoshaUser miaoshaUser = service.get(MiaoshaUserKey.token, token, MiaoshaUser.class);
//重回设置token的有效期
if (miaoshaUser != null) {
addCookie(response, miaoshaUser, token);
}
return miaoshaUser;
}
先创建:UserArgumentResolver 类
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Autowired //既然能注入service,那么可以用来容器来管理,将其放在容器中
MiaoShaUserService miaoShaUserService;
public Object resolveArgument(MethodParameter arg0, ModelAndViewContainer arg1, NativeWebRequest webRequest,
WebDataBinderFactory arg3) throws Exception {
HttpServletRequest request=webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response=webRequest.getNativeResponse(HttpServletResponse.class);
String paramToken=request.getParameter(MiaoShaUserService.COOKIE_NAME);
System.out.println("@UserArgumentResolver-resolveArgument paramToken:"+paramToken);
//获取cookie
String cookieToken=getCookieValue(request,MiaoShaUserService.COOKIE_NAME);
System.out.println("@UserArgumentResolver-resolveArgument cookieToken:"+cookieToken);
if(StringUtils.isEmpty(cookieToken)&& StringUtils.isEmpty(paramToken))
{
return null;
}
String token=StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
//System.out.println("goods-token:"+token);
//System.out.println("goods-cookieToken:"+cookieToken);
MiaoshaUser user=miaoShaUserService.getByToken(token,response);
System.out.println("@UserArgumentResolver--------user:"+user);
//去取得已经保存的user,因为在用户登录的时候,user已经保存到threadLocal里面了,因为拦截器首先执行,然后才是取得参数
//MiaoshaUser user=UserContext.getUser();
return user;
}
public String getCookieValue(HttpServletRequest request, String cookie1NameToken) {//COOKIE1_NAME_TOKEN-->"token"
//遍历request里面所有的cookie
Cookie[] cookies=request.getCookies();
if(cookies!=null) {
for(Cookie cookie :cookies) {
if(cookie.getName().equals(cookie1NameToken)) {
System.out.println("getCookieValue:"+cookie.getValue());
return cookie.getValue();
}
}
}
System.out.println("No getCookieValue!");
return null;
}
public boolean supportsParameter(MethodParameter parameter) {
//返回参数的类型
Class> clazz=parameter.getParameterType();
return clazz==MiaoshaUser.class;
}
}
再配置mvc:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
UserArgumentResolver userArgumentResolver;
/*@Autowired
AccessInterceptor accessInterceptor;
*/
/**
* 设置一个MiaoshaUser参数给,toList使用
*/
@Override
public void addArgumentResolvers(List argumentResolvers) {
//将UserArgumentResolver注册到config里面去
argumentResolvers.add(userArgumentResolver);
}
/**
* 注册拦截器
*/
/*@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册
//registry.addInterceptor(accessInterceptor);
super.addInterceptors(registry);
}*/
}
前端代码:
AA
后端代码:
@Controller
@RequestMapping("/goods")
public class GoodsControler {
@Autowired
MiaoShaUserService miaoShaUserService;
//这里防止通过商品叶页面的url,进入商品页面
@RequestMapping("/to_list")
public String to_login(HttpServletResponse response, Model model/* @CookieValue(name = MiaoShaUserService.COOKIE_NAME, required = false)String token,
@PathVariable(name = MiaoShaUserService.COOKIE_NAME,required = false)String token1*/,MiaoshaUser user){
//得不到任何的cookie信息,则说明未登陆过,返回到登录界面
/* if(StringUtils.isEmpty(token)&&StringUtils.isEmpty(token1)){
return "login";
}
String realToken = StringUtils.isEmpty(token1) ? token : token1;
MiaoshaUser byToken = miaoShaUserService.getByToken(token, response);
System.out.println("拿到token对应的user");*/
model.addAttribute("user",user);
return "user_judge";
}
}