java|秒杀项目(二)之验证功能&&全局共享&&参数解析器

目录
一、验证功能
1、将原有方法调用request以及response类
?2、导入cookie帮助类
3、UserServiceImpl类编写
4、路径控制类的编写
二、全局共享
【java|秒杀项目(二)之验证功能&&全局共享&&参数解析器】1、为什么要使用全局共享
2、导入全局共享依赖
3、配置redis配置
4、全局共享示意图
三、自定义redis完成全局共享
1、定义一个redisconfig类,用来连接redis
2、编写service层,以及实现类
3、用可视化工具连接redis
4、进行测试,实现redis进行数据共享

四、使用参数解析器
1、为什么会使用参数解析器
2、编写webconfig类
3、配置UserArgumentResolvers类


一、验证功能

在上次做的登录功能中有很过纰漏,并且有好多功能没有完善,比A登录进去之后,并且进行了商品的购买,后者有用户B登录进去了,那么A用户购买的商品就会进入到B用户中,这样的逻辑是行不通的,所以就要实现验证功能!验证功能使用的ticket,用cookie来进行实现
1、将原有方法调用request以及response类
IUserService:

package com.zj.seckill.service; import com.zj.seckill.pojo.User; import com.baomidou.mybatisplus.extension.service.IService; import com.zj.seckill.util.response.ResponseResult; import com.zj.seckill.vo.UserVo; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * * 用户信息表 服务类 *
* * @author zj * @since 2022-03-15 */ public interface IUserService extends IService {ResponseResult findByAccount(UserVo userVo, HttpServletRequest request, HttpServletResponse response); }

UserServiceImpl:

java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片

UserController:

java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片
2、导入cookie帮助类
CookieUtils:

package com.zj.seckill.util; import lombok.extern.slf4j.Slf4j; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; @Slf4j @SuppressWarnings("all") public class CookieUtils {/** * @Description: 得到Cookie的值, 不编码 */ public static String getCookieValue(HttpServletRequest request, String cookieName) { return getCookieValue(request, cookieName, false); }/** * @Description: 得到Cookie的值 */ public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) { Cookie[] cookieList = request.getCookies(); if (cookieList == null || cookieName == null) { return null; } String retValue = https://www.it610.com/article/null; try { for (int i = 0; i < cookieList.length; i++) { if (cookieList[i].getName().equals(cookieName)) { if (isDecoder) { retValue = URLDecoder.decode(cookieList[i].getValue(),"UTF-8"); } else { retValue = https://www.it610.com/article/cookieList[i].getValue(); } break; } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return retValue; }/** * @Description: 得到Cookie的值 */ public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) { Cookie[] cookieList = request.getCookies(); if (cookieList == null || cookieName == null) { return null; } String retValue = null; try { for (int i = 0; i < cookieList.length; i++) { if (cookieList[i].getName().equals(cookieName)) { retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString); break; } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return retValue; }/** * @Description: 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码 */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue) { setCookie(request, response, cookieName, cookieValue, -1); }/** * @Description: 设置Cookie的值 在指定时间内生效,但不编码 */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage) { setCookie(request, response, cookieName, cookieValue, cookieMaxage, false); }/** * @Description: 设置Cookie的值 不设置生效时间,但编码 * 在服务器被创建,返回给客户端,并且保存客户端 * 如果设置了SETMAXAGE(int seconds),会把cookie保存在客户端的硬盘中 * 如果没有设置,会默认把cookie保存在浏览器的内存中 * 一旦设置setPath():只能通过设置的路径才能获取到当前的cookie信息 */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, boolean isEncode) { setCookie(request, response, cookieName, cookieValue, -1, isEncode); }/** * @Description: 设置Cookie的值 在指定时间内生效, 编码参数 */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) { doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode); }/** * @Description: 设置Cookie的值 在指定时间内生效, 编码参数(指定编码) */ public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, String encodeString) { doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString); }/** * @Description: 删除Cookie带cookie域名 */ public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) { doSetCookie(request, response, cookieName, null, -1, false); }/** * @Description: 设置Cookie的值,并使其在指定时间内生效 */ private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) { try { if (cookieValue == null) { cookieValue =""; } else if (isEncode) { cookieValue = https://www.it610.com/article/URLEncoder.encode(cookieValue,"utf-8"); } Cookie cookie = new Cookie(cookieName, cookieValue); if (cookieMaxage > 0) cookie.setMaxAge(cookieMaxage); if (null != request) {// 设置域名的cookie String domainName = getDomainName(request); log.info("========== domainName: {} ==========", domainName); if (!"localhost".equals(domainName)) { cookie.setDomain(domainName); } } cookie.setPath("/"); response.addCookie(cookie); } catch (Exception e) { e.printStackTrace(); } }/** * @Description: 设置Cookie的值,并使其在指定时间内生效 */ private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, String encodeString) { try { if (cookieValue =https://www.it610.com/article/= null) { cookieValue =""; } else { cookieValue = https://www.it610.com/article/URLEncoder.encode(cookieValue, encodeString); } Cookie cookie = new Cookie(cookieName, cookieValue); if (cookieMaxage> 0) cookie.setMaxAge(cookieMaxage); if (null != request) {// 设置域名的cookie String domainName = getDomainName(request); log.info("========== domainName: {} ==========", domainName); if (!"localhost".equals(domainName)) { cookie.setDomain(domainName); } } cookie.setPath("/"); response.addCookie(cookie); } catch (Exception e) { e.printStackTrace(); } }/** * @Description: 得到cookie的域名 */ private static final String getDomainName(HttpServletRequest request) { String domainName = null; String serverName = request.getRequestURL().toString(); if (serverName == null || serverName.equals("")) { domainName = ""; } else { serverName = serverName.toLowerCase(); serverName = serverName.substring(7); final int end = serverName.indexOf("/"); serverName = serverName.substring(0, end); if (serverName.indexOf(":") > 0) { String[] ary = serverName.split("\\:"); serverName = ary[0]; }final String[] domains = serverName.split("\\."); int len = domains.length; if (len > 3 && !isIp(serverName)) { // www.xxx.com.cn domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1]; } else if (len <= 3 && len > 1) { // xxx.com or xxx.cn domainName = "." + domains[len - 2] + "." + domains[len - 1]; } else { domainName = serverName; } } return domainName; }public static String trimSpaces(String IP) {//去掉IP字符串前后所有的空格 while (IP.startsWith(" ")) { IP = IP.substring(1, IP.length()).trim(); } while (IP.endsWith(" ")) { IP = IP.substring(0, IP.length() - 1).trim(); } return IP; }public static boolean isIp(String IP) {//判断是否是一个IP boolean b = false; IP = trimSpaces(IP); if (IP.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")) { String s[] = IP.split("\\."); if (Integer.parseInt(s[0]) < 255) if (Integer.parseInt(s[1]) < 255) if (Integer.parseInt(s[2]) < 255) if (Integer.parseInt(s[3]) < 255) b = true; } return b; } }

3、UserServiceImpl类编写
String ticket = UUID.randomUUID().toString().replace("-", ""); request.getSession().setAttribute(ticket,user); CookieUtils.setCookie(request,response,"ticket",ticket);

4、路径控制类的编写
package com.zj.seckill.controller; import com.zj.seckill.exception.BusinessException; import com.zj.seckill.util.CookieUtils; import com.zj.seckill.util.response.ResponseResultCode; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; @Controller public class PathController {@RequestMapping("/") public String toPath() { return "login"; }@RequestMapping("/{dir}/{path}") public String toPath(@PathVariable("dir") String dir, @PathVariable("path") String path,HttpServletRequest request) { String ticket= CookieUtils.getCookieValue(request,"ticket"); if(ticket==null){ throw new BusinessException(ResponseResultCode.TICKET_ERROR); } //去session中取到ticket对应的用户,判断是否有值 Object obj = request.getSession().getAttribute(ticket); if(obj==null){ throw new BusinessException(ResponseResultCode.TICKET_ERROR); } return dir + "/" + path; } }

效果:
java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片



二、全局共享 1、为什么要使用全局共享
因为在我们在进行秒杀抢购,一台服务器支撑不了整个秒杀活动,所以就要进行全局共享,提供出第三者,来分担整个秒杀活动的压力
2、导入全局共享依赖
org.apache.commons commons-pool2 org.springframework.session spring-session-data-redis

3、配置redis配置
redis: host: 127.0.0.1 #password: 123456 database: 0 port: 6379

4、全局共享示意图 java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片





三、自定义redis完成全局共享 1、定义一个redisconfig类,用来连接redis
package com.zj.seckill.config; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @SuppressWarnings("all") public class RedisConfig {@Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory){ //新建一个 RedisTemplate redisTemplate = new RedisTemplate(); //设置一下使用连接工厂 redisTemplate.setConnectionFactory(factory); //额外设置(string) redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //额外设置(hash 就是 map 集合) redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); //让设置生效 redisTemplate.afterPropertiesSet(); return redisTemplate; }}

2、编写service层,以及实现类
IRedisService:

在service中编写两个方法,一个是增加,另一个是得到
package com.zj.seckill.service; import com.zj.seckill.pojo.User; @SuppressWarnings("all") public interface IRedisService {void putUserByTicket(String ticket, User user); User getUserByTicket(String ticket); }

RedisServiceImpl:

package com.zj.seckill.service.impl; import com.zj.seckill.pojo.User; import com.zj.seckill.service.IRedisService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service @SuppressWarnings("all") public class RedisServiceImpl implements IRedisService {@Autowired private RedisTemplate redisTemplate; @Override public void putUserByTicket(String ticket, User user) { redisTemplate.opsForValue().set("user:"+ticket,user,2L, TimeUnit.HOURS); }@Override public User getUserByTicket(String ticket) { Object obj = redisTemplate.opsForValue().get("user:"+ticket); if(obj==null|| !(obj instanceof User)){ return null; } return (User) obj; }}

3、用可视化工具连接redis java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片

4、进行测试,实现redis进行数据共享 java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片

java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片
因为在代码中打了"user:",所以会创建一个文件夹
redisTemplate.opsForValue().set("user:"+ticket,user,2L, TimeUnit.HOURS);




四、使用参数解析器 1、为什么会使用参数解析器
因为在一个大型秒杀系统中,用户每进一个页面就会实现用户验证功能,所以就会使用到参数解析器
2、编写webconfig类
package com.zj.seckill.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.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration @EnableWebMvc @SuppressWarnings("all") public class WebConfig implements WebMvcConfigurer {@Autowired private UserArgumentResolvers userArgumentsResolvers; /** * 參數解析器 * Argument參數 * HandlerMethodArgumentResolver 处理方法中参数的解析器 * @param resolvers */ @Override public void addArgumentResolvers(List resolvers) { resolvers.add(userArgumentsResolvers); }/** * 让js文件继续生效 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //静态资源访问映射映射路径 -> 本地资源路径 registry.addResourceHandler("/static/**") .addResourceLocations("classpath:/static/"); }}

3、配置UserArgumentResolvers类
这个类两个方法是相关联的,进入第一个方法后,如果通过就会进入到第二个方法中
package com.zj.seckill.config; import com.zj.seckill.exception.BusinessException; import com.zj.seckill.pojo.User; import com.zj.seckill.service.IRedisService; import com.zj.seckill.util.CookieUtils; import com.zj.seckill.util.response.ResponseResultCode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; 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.HttpServletRequest; /** * 声明为一个组件 */ @Component @SuppressWarnings("all") public class UserArgumentResolvers implements HandlerMethodArgumentResolver {//supportsParameter决定了resolveArgument是否运行//此方法会进入三次,有三个参数 //xd(int OrderId,double OrderSum,User user)@Autowired private IRedisService redisService; /** * 针对user参数进行解析 * supportsParameter决定了resolveArgument是否会运行 * getParameterType 获得参数的类型 * @param methodParameter * @return */ @Override public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.getParameterType() == User.class; }/** * 解析参数 * @param methodParameter * @param modelAndViewContainer * @param nativeWebRequest * @param webDataBinderFactory * @return * @throws Exception */ @Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { //参数解析User 因为很多地方需要做登录验证 HttpServletRequest request=(HttpServletRequest)nativeWebRequest.getNativeRequest(); //获取用户的ticket String ticket= CookieUtils.getCookieValue(request, "ticket"); if(ticket == null){ throw new BusinessException(ResponseResultCode.TICKET_ERROR); } //去session中取到ticket对应的用户 判断是否有值 //Object obj = request.getSession().getAttribute(ticket); //去缓存中拿 User user = redisService.getUserByTicket(ticket); if(user==null){ throw new BusinessException(ResponseResultCode.TICKET_ERROR); } return user; //经过了参数解析之后参数会变成你这个地方返回的值 } }

java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片

java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片

java|秒杀项目(二)之验证功能&&全局共享&&参数解析器
文章图片
成功!

    推荐阅读