一、登录注册功能
1、登录短信验证码倒计时
2.登录短信验证码整合
注册功能写在认证服务中,用户前台点击获取验证码,需要远程调用第三方服务来执行短信验证码发送功能。 短信验证码主要使用阿里云短信验证码发送组件。用户前台点击获取验证码,向第三方服务发送请求,根据用户提供的手机号和验证码服务中配置的appcode等相关信息来请求阿里云验证码组件来发送验证码进行验证。
解决验证码防刷
验证码再次校验:将验证码存入redis中,
二、分布式Session
普通session共享存在的问题
2.1.Session的原理
- 浏览器第一次访问服务器进行登录,服务器将用户信息保存到session中,该session存在服务器内存sessionManager中
- 浏览器会让客户端保存一个jsessionid为指定值的cookie;
- 客户端下次访问服务器,需要带上jsessionId为指定值的cookie,识别到用户登录信息
- 浏览器关闭,清楚会话session;
- 下次访问重复步骤1-4
2.2 存在的问题
在分布式项目中,不同微服务的服务器不同,同一服务也可能被负载均衡到不同的服务器。
域名不同,这样跨域名session无法统一
在auth.gulimall.com域名下,令牌的Domain在本域下,gulimall.com域无法获取到该数据
2.3解决办法
session复制:将session复制到每一个服务器中,可以保证服务器中的session都是相同的。
session客户端存储:将sesison存储在客户端
hash一致性:
使用redis存储session:
2.4SpringSession解决子域共享问题
引入依赖
-
- <dependency>
- <groupId>org.springframework.session</groupId>
- <artifactId>spring-session-data-redis</artifactId>
- </dependency>
复制代码 配置文件中配置
- spring:
- session:
- store-type: redis
复制代码 主启动类中添加注解:@EnableRedisHttpSession
至此,可以保证session存入redis中。
然而,默认发的令牌。sessionid=abc的作用域是在当前域,需要解决子域session共享问题。
扩大子域范围
- @Configuration
- public class AchangmallSessionConfig {
- @Bean
- public CookieSerializer cookieSerializer() {
- DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
- //放大作用域
- cookieSerializer.setDomainName("gulimall.com");
- cookieSerializer.setCookieName("ACHANGSESSION");
- return cookieSerializer;
- }
- @Bean
- public RedisSerializer<Object> springSessionDefaultRedisSerializer() {//使用json存储redis,而不是默认的序列化存储
- return new GenericJackson2JsonRedisSerializer();
- }
- } Y
复制代码 原理
三、购物车
3.1ThreadLocal用户身份鉴别
用户身份鉴别流程:
- 获取购物车之前首先要判断是否登录,需要整合springsession来共享数据
- 第一次使用购物车功能,会给一个临时用户身份
浏览器有一个cookie:use-key:标识用户身份,一个月后过期
浏览器以后保存,每次访问都会带上这个cookie
登录:session中有相关信息
未登录:按照cookie中的user-key
第一次使用:没有临时用户就要创建一个临时用户。
这里采用拦截器来实现
具体实现:
拦截器配置
拦截器的注册:
- @Configuration
- public class GulimallWebConfig implements WebMvcConfigurer {
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(new CartInterceptor())//注册拦截器
- .addPathPatterns("/**");
- }
- }
复制代码 购物车的拦截器实现spring中的HandlerInterceptor接口
- public class CartInterceptor implements HandlerInterceptor {
- public static ThreadLocal<UserInfoTo> toThreadLocal = new ThreadLocal<>();
- /***
- * 目标方法执行之前
- * @param request
- * @param response
- * @param handler
- * @return
- * @throws Exception
- */
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- UserInfoTo userInfoTo = new UserInfoTo();
- HttpSession session = request.getSession();
- //获得当前登录用户的信息
- MemberResponseVo memberResponseVo = (MemberResponseVo) session.getAttribute(LOGIN_USER);
- if (memberResponseVo != null) {
- //用户登录了
- userInfoTo.setUserId(memberResponseVo.getId());
- }
- Cookie[] cookies = request.getCookies();
- if (cookies != null && cookies.length > 0) {
- for (Cookie cookie : cookies) {
- //user-key
- String name = cookie.getName();
- if (name.equals(TEMP_USER_COOKIE_NAME)) {
- userInfoTo.setUserKey(cookie.getValue());
- //标记为已是临时用户
- userInfoTo.setTempUser(true);
- }
- }
- }
- //如果没有临时用户一定分配一个临时用户
- if (StringUtils.isEmpty(userInfoTo.getUserKey())) {
- String uuid = UUID.randomUUID().toString();
- userInfoTo.setUserKey(uuid);
- }
- //目标方法执行之前
- toThreadLocal.set(userInfoTo);
- return true;
- }
- /**
- * 业务执行之后,分配临时用户来浏览器保存
- * @param request
- * @param response
- * @param handler
- * @param modelAndView
- * @throws Exception
- */
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- //获取当前用户的值
- UserInfoTo userInfoTo = toThreadLocal.get();
- //如果没有临时用户一定保存一个临时用户
- if (!userInfoTo.getTempUser()) {
- //创建一个cookie
- Cookie cookie = new Cookie(TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());
- //扩大作用域
- cookie.setDomain("gulimall.com");
- //设置过期时间
- cookie.setMaxAge(TEMP_USER_COOKIE_TIMEOUT);
- response.addCookie(cookie);
- }
- }
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- }
- }
复制代码 面试问题:你是如何在项目中使用ThreadLocal的?
ThreadLocal来共享同一个线程中的数据。每一个请求,tomcat会开辟一个线程来处理请求,从拦截器的执行,到调用controller、service、dao,一直到请求结束返回,都是在同一个线程中实现的。
在购物车项目中,我们需要一个拦截器来进行用户身份的鉴别,拦截器中创建一个ThreadLocal来放用户登录信息,然后在执行其他操作时,需要登录信息时,只需要get到即可。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |