一、两次MD5
1. 用户端: PASS = MD5( 明文 + 固定 Salt)
2. 服务端: PASS = MD5( 用户输入 + 随机 Salt)
pom添加依赖
commons-codec
commons-codec
1.11
org.apache.commons
commons-lang3
3.6
添加MD5工具类
package com.imooc.miaosha.util;
import org.springframework.util.DigestUtils;
public class Md5Util {
private static final String SALT = "1a2b3c4d";
public static String md5(String src){
return DigestUtils.md5Hex(src);
}
public static String inputPass2FormPass(String inputPass){
String src = "" + SALT.charAt(0) + SALT.charAt(2)+ inputPass + SALT.charAt(5)+ SALT.charAt(4);
return md5(src);
}
public static String formPass2DbPass(String formPass, String salt){
String src = "" + salt.charAt(0) + salt.charAt(2)+ formPass + salt.charAt(5)+ salt.charAt(4);
return md5(src);
}
public static String inputPass2DbPass(String inputPass, String salt){
String formPass = inputPass2FormPass(inputPass);
String dbPass = formPass2DbPass(formPass, salt);
return dbPass;
}
public static void main(String[] args) {
String inputPass = "13632481101";
String salt = "mysalt";
String formPass = inputPass2FormPass(inputPass);
String dbPass1 = formPass2DbPass(formPass, salt);
String dbPass2 = inputPass2DbPass(inputPass, salt);
System.out.println(formPass);
System.out.println(dbPass1);
System.out.println(dbPass2);
}
}
添加电话号码校验类
package com.imooc.miaosha.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.alibaba.druid.util.StringUtils;
public class ValidatorUtil {
private static Pattern MOBILE_PATTERN = Pattern.compile("1\\d{10}");
public static boolean isMobile(String mobile){
if(StringUtils.isEmpty(mobile)){
return false;
}
Matcher matcher = MOBILE_PATTERN.matcher(mobile);
return matcher.matches();
}
public static void main(String[] args) {
boolean result1 = isMobile("13632481101");
boolean result2 = isMobile("1363248110");
System.out.println(result1);
System.out.println(result2);
}
}
resources文件下添加js/common.js,js/jquery.min.js,js/md5.min.js,templates/login.html
新增登录页面login.html
登录用户登录
请输入手机号码
请输入密码
重置
登录
function login(){
$("#loginForm").validate({
submitHandler:function(form){
doLogin();
}
});
}
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();
if(data.code == 0){
layer.msg("成功");
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.closeAll();
}
});
}
common.js
//展示loading
function g_showLoading(){
var idx = layer.msg('处理中...', {icon: 16,shade: [0.5, '#f5f5f5'],scrollbar: false,offset: '0px', time:100000}) ;
return idx;
}
//salt
var g_passsword_salt="1a2b3c4d"
二.JSR303参数检验+全局异常处理器
pom添加依赖
org.springframework.boot
spring-boot-starter-validation
新建包package com/imooc/miaosha/validator
自定义参数注解
package com.imooc.miaosha.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@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 {};
}
package com.imooc.miaosha.validator;
import com.imooc.miaosha.util.ValidatorUtil;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class IsMobileValidator implements ConstraintValidator {
private boolean required;
@Override
public void initialize(IsMobile isMobile) {
required = isMobile.required();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (!required && StringUtils.isEmpty(value)) {
return true;
}
return ValidatorUtil.isMobile(value);
}
}
在LoginVo中使用注解
package com.imooc.miaosha.vo;
import javax.validation.constraints.NotNull;
import com.imooc.miaosha.validator.IsMobile;
import org.hibernate.validator.constraints.Length;
public class LoginVo {
@NotNull
@IsMobile
private String mobile;
@NotNull
@Length(min = 32)
private String password;
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "LoginVo [mobile=" + mobile + ", password=" + password + "]";
}
}
方法中添加参数校验
@RequestMapping("/do_login")
@ResponseBody
public Result doLogin(@Valid LoginVo loginVo) {
log.info(loginVo.toString());
seckillUserService.login(loginVo);
return Result.success(CodeMsg.SUCCESS);
}
service的login方法
public boolean login(LoginVo loginVo){
if(loginVo == null){
//return CodeMsg.SERVER_ERROR;
throw new GlobalException(CodeMsg.SERVER_ERROR);
}
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
/*
if(StringUtils.isEmpty(mobile)){
return CodeMsg.MOBILE_EMPTY;
}
if(StringUtils.isEmpty(password)){
return CodeMsg.PASSWORD_EMPTY;
}
if(!ValidatorUtil.isMobile(mobile)){
return CodeMsg.MOBILE_ERROR;
}
*/
SeckillUser user = seckillUserDao.getById(Long.parseLong(mobile));
if(user == null){
//return CodeMsg.MOBILE_NOT_EXIST;
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}
String salt = user.getSalt();
String dbPass = user.getPassword();
String md5Pass = Md5Util.formPass2DbPass(password, salt);
if(!dbPass.equals(md5Pass)){
//return CodeMsg.PASSWORD_ERROR;
throw new GlobalException(CodeMsg.PASSWORD_ERROR);
}
return true;
}
新建包package com/imooc/miaosha/exception
添加全局异常类GlobalException和全局异常处理器GlobalExceptionHandler
package com.imooc.miaosha.exception;
import com.imooc.miaosha.result.CodeMsg;
public class GlobalException extends RuntimeException{
private static final long serialVersionUID = 31665074385012932L;
private CodeMsg cm;
public GlobalException(CodeMsg cm){
this.cm = cm;
}
public CodeMsg getCm() {
return cm;
}
}
package com.imooc.miaosha.exception;
import com.imooc.miaosha.result.CodeMsg;
import com.imooc.miaosha.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.validation.BindException;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public Result handleException(HttpServletRequest request, Exception ex){
ex.printStackTrace();
if(ex instanceof GlobalException){
GlobalException gex = (GlobalException)ex;
return Result.error(gex.getCm());
} else if(ex instanceof BindException){
BindException bex = (BindException)ex;
String message = bex.getAllErrors().get(0).getDefaultMessage();
return Result.error(CodeMsg.BIND_ERROR.fillMsg(message));
} else {
return Result.error(CodeMsg.SERVER_ERROR);
}
}
}
三.分布式session
添加工具类UUIDUtil
package com.imooc.miaosha.util;
import java.util.UUID;
public class UUIDUtil {
public static String uuid(){
return UUID.randomUUID().toString().replace("-", "");
}
}
userservice的login添加代码
String token = UUIDUtil.uuid();
redisService.set(SeckillUserKey.token, token, user);
Cookie cookie = new Cookie(COOKIE_TOKEN_NAME, token);
cookie.setMaxAge(SeckillUserKey.token.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
userservice使用token获取user对象
public User getByToke(String token) {
if(StringUtils.isEmpty(token)){
return null;
}
return redisService.get(UserKey.token, token, User.class);
}
GoodsController
public SeckillUser getByToke(String token) {
if(StringUtils.isEmpty(token)){
return null;
}
return redisService.get(SeckillUserKey.token, token, SeckillUser.class);
}
四.优化
使用SpringMVC中WebMvcConfigurerAdapter的addArgumentResolvers,向需要user对象的方法进行入参注入。
新建包package com/imooc/miaosha/exception
新建类UserArgumentResolver和WebConfig
package com.imooc.miaosha.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private UserArgumentResolver userArgumentResolver;
@Override
public void addArgumentResolvers(List argumentResolvers) {
argumentResolvers.add(userArgumentResolver);
}
}
package com.imooc.miaosha.config;
import com.imooc.miaosha.domain.User;
import com.imooc.miaosha.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Autowired
private UserService UserService;
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class> clazz = parameter.getParameterType();
return clazz == User.class;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
String paramToken = request.getParameter(UserService.COOKIE_TOKEN_NAME);
String cookieToken = getCookieValue(request, UserService.COOKIE_TOKEN_NAME);
if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)){
return null;
}
String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;
return UserService.getByToke(token, response);
}
private String getCookieValue(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if(cookies != null){
for(Cookie cookie : cookies){
if(cookie.getName().equals(cookieName)){
return cookie.getValue();
}
}
}
return null;
}
}
优化后userservice类
package com.imooc.miaosha.service;
import com.imooc.miaosha.dao.UserDao;
import com.imooc.miaosha.domain.User;
import com.imooc.miaosha.exception.GlobalException;
import com.imooc.miaosha.redis.RedisService;
import com.imooc.miaosha.redis.UserKey;
import com.imooc.miaosha.result.CodeMsg;
import com.imooc.miaosha.util.Md5Util;
import com.imooc.miaosha.util.UUIDUtil;
import com.imooc.miaosha.vo.LoginVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@Service
public class UserService {
public static final String COOKIE_TOKEN_NAME = "token";
@Autowired
UserDao UserDao;
@Autowired
RedisService redisService;
public User getById(long id){
return UserDao.getById(id);
}
public boolean login(HttpServletResponse response, LoginVo loginVo){
if(loginVo == null){
throw new GlobalException(CodeMsg.SERVER_ERROR);
}
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
User user = UserDao.getById(Long.parseLong(mobile));
if(user == null){
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}
String salt = user.getSalt();
String dbPass = user.getPassword();
String md5Pass = Md5Util.formPass2DbPass(password, salt);
if(!dbPass.equals(md5Pass)){
throw new GlobalException(CodeMsg.PASSWORD_ERROR);
}
String token = UUIDUtil.uuid();
addCookie(token, response, user);
return true;
}
public User getByToke(String token, HttpServletResponse response) {
if(StringUtils.isEmpty(token)){
return null;
}
// 延长有效期
User user = redisService.get(UserKey.token, token, User.class);
if(user != null){
addCookie(token, response, user);
}
return user;
}
private void addCookie(String token, HttpServletResponse response, User user){
redisService.set(UserKey.token, token, user);
Cookie cookie = new Cookie(COOKIE_TOKEN_NAME, token);
cookie.setMaxAge(UserKey.token.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
}
}
controller类
package com.imooc.miaosha.controller;
import com.imooc.miaosha.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/goods")
public class GoodsController {
@RequestMapping("/to_list")
public String toList(Model model, User User) {
model.addAttribute("user", User);
return "goods_list";
}
}