文章只展示部分核心代码,完整代码请参考源码
一、秒杀接口地址隐藏
思路:秒杀开始前,先请求接口获取秒杀地址
1.接口改造,带上PathVariable参数
前端页面请求:
function getMiaoshaPath() {var goodsId = $("#goodsId").val();$.ajax({url:"/miaosha/path",type:"GET",data:{goodsId:goodsId},success:function(data){if(data.code == 0){var path = data.data;doMiaosha(path);}else{layer.msg(data.msg);}},error:function(){layer.msg("客户端请求有误");}});}
2.添加生成地址的接口
Controller 层:创建一个临时path值,保存到redis缓存中,并设置过期时间
@RequestMapping(value="/path", method=RequestMethod.GET)@ResponseBodypublic Result<String> getMiaoshaPath(Model model, MiaoshaUser user,@RequestParam("goodsId")long goodsId) {model.addAttribute("user", user);if (user == null) {return Result.error(CodeMsg.SESSION_ERROR);}String path = miaoshaService.createMiaoshaPath(user,goodsId);return Result.success(path);}//daopublic String createMiaoshaPath(MiaoshaUser user, long goodsId) {String str = MD5Util.md5(UUIDUtil.uuid()+"123456");redisService.set(MiaoshaKey.getMiaoshaPath,""+user.getId()+"_"+goodsId,str);return str;}
3.接口收到请求,先验证PathVariable
秒杀前先验证path,从redis缓存从取出
public boolean checkPath(MiaoshaUser user, long goodsId, String path) {
if (user == null || path == null){
return false;
}
String pathOld = redisService.get(MiaoshaKey.getMiaoshaPath, "" + user.getId() + "_" + goodsId, String.class);
return path.equals(pathOld);
}
二、数学公式验证码
- 生成验证码
```java public BufferedImage createVerifyCode(MiaoshaUser user, long goodsId) { if (user == null || goodsId <= 0) {@RequestMapping(value="/verifyCode", method=RequestMethod.GET) @ResponseBody public Result<String> getMiaoshaVerifyCode(HttpServletResponse response,Model model, MiaoshaUser user, @RequestParam("goodsId")long goodsId) { model.addAttribute("user", user); if (user == null) { return Result.error(CodeMsg.SESSION_ERROR); } BufferedImage image = miaoshaService.createVerifyCode(user,goodsId); try { OutputStream out = response.getOutputStream(); ImageIO.write(image,"JPEG",out); out.flush(); out.close(); return null; }catch (Exception e){ return Result.error(CodeMsg.MIAOSHA_FAIL); } }
} int width = 80; int height = 32; //create the image BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); // set the background color g.setColor(new Color(0xDCDCDC)); g.fillRect(0, 0, width, height); // draw the border g.setColor(Color.black); g.drawRect(0, 0, width - 1, height - 1); // create a random instance to generate the codes Random rdm = new Random(); // make some confusion for (int i = 0; i < 50; i++) {return null;
} // generate a random code String verifyCode = generateVerifyCode(rdm); g.setColor(new Color(0, 100, 0)); g.setFont(new Font(“Candara”, Font.BOLD, 24)); g.drawString(verifyCode, 8, 24); g.dispose(); //把验证码存到redis中 int rnd = calc(verifyCode); redisService.set(MiaoshaKey.getMiaoshaVerifyCode, user.getId() + “,” + goodsId, rnd); //输出图片 return image; }int x = rdm.nextInt(width); int y = rdm.nextInt(height); g.drawOval(x, y, 0, 0);
private int calc(String exp) { try{ ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName(“JavaScript”); return (Integer)engine.eval(exp); }catch (Exception e){ e.printStackTrace(); return 0; } }
private static char[] ops = new char[]{‘+’, ‘-‘, ‘*’};
private String generateVerifyCode(Random rdm) { int num1 = rdm.nextInt(10); int num2 = rdm.nextInt(10); int num3 = rdm.nextInt(10); char op1 = ops[rdm.nextInt(3)]; char op2 = ops[rdm.nextInt(3)]; String exp = “” + num1 + op1 + num2 + op2 + num3; return exp; }
- 校验验证码
```java
public boolean checkVerifyCode(MiaoshaUser user, long goodsId, int verifyCode) {
if (user ==null || goodsId <= 0){
return false;
}
Integer codeOld = redisService.get(MiaoshaKey.getMiaoshaVerifyCode, user.getId() + "," + goodsId, Integer.class);
if (codeOld == null || codeOld-verifyCode != 0){
return false;
}
redisService.delete(MiaoshaKey.getMiaoshaVerifyCode,user.getId() + "," + goodsId);
return true;
}
前端发起请求
var verifyCode = $("#verifyCode").val(); $.ajax({ url:"/miaosha/path", type:"GET", data:{ goodsId:goodsId, verifyCode:verifyCode },三、接口限流防刷
自定义一个注解实现对接口方法进行拦截
@AccessLimit(seconds=10,maxCount=5,needLogin=true)具体实现步骤
@Retention(RUNTIME) @Target(METHOD) public @interface AccessLimit { int seconds(); int maxCount(); boolean needLogin() default true; }```java @Service public class AccessInterceptor extends HandlerInterceptorAdapter {
@Autowired MiaoshaUserService userService;
@Autowired RedisService redisService;
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod){ MiaoshaUser user = getUser(request, response); UserContext.setUser(user); HandlerMethod hm = (HandlerMethod) handler; AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); if (accessLimit == null){ return true; } int seconds = accessLimit.seconds(); int macCount = accessLimit.maxCount(); boolean needLogin = accessLimit.needLogin(); String key = request.getRequestURI(); if (needLogin) { if (user == null){ render(response,CodeMsg.SESSION_ERROR); return false; } key += "_"+user.getId(); }else { //do nothing } AccessKey accessKey = AccessKey.withExpire(seconds); Integer count = redisService.get(accessKey, key, Integer.class); if (count == null){ redisService.set(accessKey,key,1); }else if (count < macCount){ redisService.incr(accessKey,key); }else { render(response,CodeMsg.ACCESS_LIMIT_REACHED); return false; } } return true;}
private void render(HttpServletResponse response, CodeMsg codeMsg) throws Exception {
response.setContentType("application/json;charset=UTF-8"); OutputStream out = response.getOutputStream(); String str = JSON.toJSONString(Result.error(codeMsg)); out.write(str.getBytes("UTF-8")); out.flush(); out.close();}
private MiaoshaUser getUser(HttpServletRequest request, HttpServletResponse response){
String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN); String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN); if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) { return null; } String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken; return userService.getByToken(response, token);}
private String getCookieValue(HttpServletRequest request, String cookiName) {
Cookie[] cookies = request.getCookies();
if(cookies == null || cookies.length <= 0){
return null;
}
for(Cookie cookie : cookies) {
if(cookie.getName().equals(cookiName)) {
return cookie.getValue();
}
}
return null;
}
}
- 用 ThreadLocal 保存 user
```java
public class UserContext {
private static ThreadLocal<MiaoshaUser> userHolder = new ThreadLocal<MiaoshaUser>();
public static void setUser(MiaoshaUser user){
userHolder.set(user);
}
public static MiaoshaUser getUser(){
return userHolder.get();
}
}
- 在 WebConfig 中将接口注册
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(accessInterceptor); }
