伤心客 发表于 2022-6-25 16:17:59

Java打怪之路----谷粒商场认证服务

一、登录注册功能

1、登录短信验证码倒计时

2.登录短信验证码整合

注册功能写在认证服务中,用户前台点击获取验证码,需要远程调用第三方服务来执行短信验证码发送功能。 短信验证码主要使用阿里云短信验证码发送组件。用户前台点击获取验证码,向第三方服务发送请求,根据用户提供的手机号和验证码服务中配置的appcode等相关信息来请求阿里云验证码组件来发送验证码进行验证。
解决验证码防刷
验证码再次校验:将验证码存入redis中,
二、分布式Session

普通session共享存在的问题
2.1.Session的原理


[*]浏览器第一次访问服务器进行登录,服务器将用户信息保存到session中,该session存在服务器内存sessionManager中
[*]浏览器会让客户端保存一个jsessionid为指定值的cookie;
[*]客户端下次访问服务器,需要带上jsessionId为指定值的cookie,识别到用户登录信息
[*]浏览器关闭,清楚会话session;
[*]下次访问重复步骤1-4
https://img-blog.csdnimg.cn/51613036ddc84b16a08e1e99ac672a83.png
2.2 存在的问题

在分布式项目中,不同微服务的服务器不同,同一服务也可能被负载均衡到不同的服务器。
域名不同,这样跨域名session无法统一
在auth.gulimall.com域名下,令牌的Domain在本域下,gulimall.com域无法获取到该数据
https://img-blog.csdnimg.cn/24470efbfbb341038ee3788f36cc0307.png
2.3解决办法

session复制:将session复制到每一个服务器中,可以保证服务器中的session都是相同的。
https://img-blog.csdnimg.cn/662b9e62c447429e9ee1009b7d75b1e4.png
session客户端存储:将sesison存储在客户端
https://img-blog.csdnimg.cn/7b249ed1a7e349d2a998a6d4d2629488.png
hash一致性:
https://img-blog.csdnimg.cn/9ca1e0d900af46db8f090617b291720b.png
使用redis存储session:
https://img-blog.csdnimg.cn/655c1aab6db04001a819879de01c8cd5.png
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
第一次使用:没有临时用户就要创建一个临时用户。
这里采用拦截器来实现
具体实现:
拦截器配置
https://img-blog.csdnimg.cn/1a58b5d5ac84448e83d750f651600902.png
拦截器的注册:
@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到即可。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Java打怪之路----谷粒商场认证服务