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

一、登录注册功能

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

2.登录短信验证码整合

注册功能写在认证服务中,用户前台点击获取验证码,需要远程调用第三方服务来执行短信验证码发送功能。 短信验证码主要使用阿里云短信验证码发送组件。用户前台点击获取验证码,向第三方服务发送请求,根据用户提供的手机号和验证码服务中配置的appcode等相关信息来请求阿里云验证码组件来发送验证码进行验证。

解决验证码防刷
验证码再次校验:将验证码存入redis中,

二、分布式Session

普通session共享存在的问题

2.1.Session的原理

  1. 浏览器第一次访问服务器进行登录,服务器将用户信息保存到session中,该session存在服务器内存sessionManager中
  2. 浏览器会让客户端保存一个jsessionid为指定值的cookie;
  3. 客户端下次访问服务器,需要带上jsessionId为指定值的cookie,识别到用户登录信息
  4. 浏览器关闭,清楚会话session;
  5. 下次访问重复步骤1-4
    在这里插入图片描述

2.2 存在的问题

在分布式项目中,不同微服务的服务器不同,同一服务也可能被负载均衡到不同的服务器。
域名不同,这样跨域名session无法统一

在auth.gulimall.com域名下,令牌的Domain在本域下,gulimall.com域无法获取到该数据
在这里插入图片描述

2.3解决办法

session复制:将session复制到每一个服务器中,可以保证服务器中的session都是相同的。
在这里插入图片描述
session客户端存储:将sesison存储在客户端
在这里插入图片描述
hash一致性
在这里插入图片描述
使用redis存储session
在这里插入图片描述

2.4SpringSession解决子域共享问题

引入依赖

	<!-- 整合 spring session 实现 session 共享-->
<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用户身份鉴别

用户身份鉴别流程:

  1. 获取购物车之前首先要判断是否登录,需要整合springsession来共享数据
  2. 第一次使用购物车功能,会给一个临时用户身份
    浏览器有一个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到即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值