您的位置:首页 > 文旅 > 美景 > 深圳华强北手表批发市场在哪里_网店运营推广职业技能等级证书_优化网站快速排名软件_宁波seo优化定制

深圳华强北手表批发市场在哪里_网店运营推广职业技能等级证书_优化网站快速排名软件_宁波seo优化定制

2024/12/26 3:38:58 来源:https://blog.csdn.net/qq_63981644/article/details/143603712  浏览:    关键词:深圳华强北手表批发市场在哪里_网店运营推广职业技能等级证书_优化网站快速排名软件_宁波seo优化定制
深圳华强北手表批发市场在哪里_网店运营推广职业技能等级证书_优化网站快速排名软件_宁波seo优化定制

欢迎来到“雪碧聊技术”CSDN博客!

在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目心得。

让我们一同在Java的广阔天地中遨游,携手提升技术能力,共创美好未来!感谢您的关注与支持,期待在“雪碧聊技术”与您共同成长!

目录

一、短信登录3-集群的session共享问题

1、什么是集群的session共享问题?

2、如何解决上述问题?答:使用redis代替session。

二、短信登录4-基于Redis实现共享session登录

1、使用Redis,存储验证码

①用户输入手机号,请求获取验证码

②用户输入手机号、验证码,请求短信验证登录。后端从redis中取出正确的验证码,与用户输入的验证码核对。

2、如何将用户信息存入Redis中(关键※)

①如果验证该手机号、验证码都没毛病,证明应该让人家登录。此时就需要为该用户生成一个token(唯一的),并以token为key,用户信息为value,存入redis中。

②最关键的一步:把上面生成的token,返回到客户端,客户端再将该token保存到本地,作为以后登录、访问其他后端接口的令牌!

③以后客户端访问任何后端接口,都要携带token,经过后端拦截器的许可后,才能得到后端服务。

3、根据1和2的逻辑,完成具体的代码开发

①完成验证码存储到redis中

②使用redis,完成登录验证功能

③如果验证码一致,则校验登录成功。下面就需要为用户生成jwt令牌(此处用UUID来生成),然后将jwt令牌(作为键)和用户信息(作为值)存入redis中。

④别忘了将该token返回给客户端,让客户端存起来,以后每次请求都要携带该token,来通过后端拦截器的拦截,从而获取后端服务。

⑤重新编写拦截器,从redis中获取token,看看前端是否拥有token令牌,从而决定是否应该拦截该请求

注意:在拦截器中,无法注入stringRedisTemplate对象,因为这个拦截器类是我们自定义的,因此无法交给Spring管理,因此只能通过构造器的方式来获取stringRedisTemplate对象。那么谁来给这个拦截器类的构造器传入一个stringRedisTemplate对象呢?那就是配置类,因为配置类需要使用拦截器对象,如下:

⑥重启后端项目,查看运行效果

⑦解决stringRedisTemplate的类型转换异常

⑧解决上述异常后,再次启动项目,查看运行效果:

4、优化登录状态刷新问题

①什么是登录状态刷新?

②那目前的登录状态刷新有什么缺点?

③如何优化这个问题?

④重新启动项目,查看运行效果


一、短信登录3-集群的session共享问题

1、什么是集群的session共享问题?

当项目足够大时,一台Tomcat服务器的压力比较大,因此就会水平拓展出很多台Tomcat服务器,然后nginx服务器再进行负载均衡,将前端的请求合理地分配给后端的多台Tomcat服务器。

此时就会出现Session共享问题:多台Tomcat服务器之间,并不共享session存储空间,当前端请求被nginx服务器分配到不同的tomcat服务器时会导致数据丢失。

举例:当我填写手机号,要求后端生成一个验证码时,此时后端就会生成一个验证码,并存放到session中。

然后用户的手机短信,会收到刚刚后端生成的验证码“043018”,然后会输入到页面上,在请求验证码登录:

然后后端会拿出刚刚在session中存入的验证码,和用户现在输入的验证码进行对比,如果一致,说明确实是该手机号的主人,如果不一致,则说明不是本人。

那此时如果该请求被nginx服务器,分配到了其他Tomcat服务器,此时由于不同的Tomcat服务器之间是不共享session的,所以后端此时就无法拿到刚刚存入session中的正确验证码,因此就无法验证登录。这就叫“集群的session共享问题”

2、如何解决上述问题?答:使用redis代替session。

此时我们用redis来存储用户信息,如:验证码。

此时就能解决不同的Tomcat的session共享问题了。

二、短信登录4-基于Redis实现共享session登录

1、使用Redis,存储验证码

①用户输入手机号,请求获取验证码

然后后端将该验证码,存入redis中,以手机号为键,验证码为值,如下:

②用户输入手机号、验证码,请求短信验证登录。后端从redis中取出正确的验证码,与用户输入的验证码核对。

2、如何将用户信息存入Redis中(关键※)

思路:

①如果验证该手机号、验证码都没毛病,证明应该让人家登录。此时就需要为该用户生成一个token(唯一的),并以token为key,用户信息为value,存入redis中。

②最关键的一步:把上面生成的token,返回到客户端,客户端再将该token保存到本地,作为以后登录、访问其他后端接口的令牌!

举例:

③以后客户端访问任何后端接口,都要携带token,经过后端拦截器的许可后,才能得到后端服务。

3、根据1和2的逻辑,完成具体的代码开发

①完成验证码存储到redis中

验证功能:

后端生成验证码947864

去redis中查看,是否存入了该验证码?

此时就完成了验证码存储到redis中。

②使用redis,完成登录验证功能

③如果验证码一致,则校验登录成功。下面就需要为用户生成jwt令牌(此处用UUID来生成),然后将jwt令牌(作为键)和用户信息(作为值)存入redis中。

上面设置了token的有效期,时长为30分钟。也就是说,你登录成功并进入内部页面,30分钟回来以后,会显示你登录过期,请重新登陆,很多应用都是这么做的。

④别忘了将该token返回给客户端,让客户端存起来,以后每次请求都要携带该token,来通过后端拦截器的拦截,从而获取后端服务。

⑤重新编写拦截器,从redis中获取token,看看前端是否拥有token令牌,从而决定是否应该拦截该请求

public class LoginInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public LoginInterceptor(StringRedisTemplate stringRedisTemplate){this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1、获取session(废弃)//HttpSession session = request.getSession();//1、获取请求头中的token(前端携带过来的)String token = request.getHeader("authorization");if(StrUtil.isBlank(token)){//判断token是否为空//token为空,拦截,返回401状态码response.setStatus(401);}//2、根据该token,获取redis中的用户String key = RedisConstants.LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);//3、判断用户是否为空if(userMap.isEmpty()){//4、不存在,拦截,返回401状态码response.setStatus(401);return false;}//5、将从redis查询到的hash数据,转为UserDTO对象UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//6、存在,保存用户信息到ThreadLocalUserHolder.saveUser(userDTO);//7、刷新token的有效期stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);//8、放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//从ThreadLocal中,移除用户//这里是避免内存泄漏:防止ThreadLocal数据明明不再使用,却一直占用JVM内存,这就叫内存泄露UserHolder.removeUser();}
}
注意:在拦截器中,无法注入stringRedisTemplate对象,因为这个拦截器类是我们自定义的,因此无法交给Spring管理,因此只能通过构造器的方式来获取stringRedisTemplate对象。那么谁来给这个拦截器类的构造器传入一个stringRedisTemplate对象呢?那就是配置类,因为配置类需要使用拦截器对象,如下:

⑥重启后端项目,查看运行效果

查看后端报错:

⑦解决stringRedisTemplate的类型转换异常

这里的userMap在本质上,就是一个userDTO对象。

查看UserDTO类的构成:

因为我们的StringRedisTemplate有一个特点,就是往redis中存数据时,要求key和value都得是String类型的。下面的源码:

可见上述就是Long类型的id,转为String类型的过程中出现了异常。

解决方案:若还想继续使用StringRedisTemplate,就必须自己手动转,如下:

⑧解决上述异常后,再次启动项目,查看运行效果:

可见此时就登入进来了,下面查看一下redis中是否有该用户的token和用户信息:

4、优化登录状态刷新问题

①什么是登录状态刷新?

就是每次请求时,都要刷新redis中的token的有效期。

如果不刷新,那么redis中的有效期,就会一直是在登录时给的那30分钟,若这期间我经常访问该项目,证明我一直在,那么就应该刷新这个token的有效期。

②那目前的登录状态刷新有什么缺点?

没有被拦截的请求路径,如下:

此时这些请求路径,由于根本不执行拦截器的代码,因此根本没机会执行拦截器中刷新redis的token有效期的代码,因此是不合理的。

比如:我一直在访问首页,而这个首页就不是拦截器拦截的范围,因此不走拦截器,于是就不会刷新token的有效期。

③如何优化这个问题?

在原有的拦截器前面,再加一个拦截器,专门用来拦截全部的请求,并刷新token的有效期,此时就完美了,如下:

具体操作如下:

编写代码:

public class RefreshTokenInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate){this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1、获取session(废弃)//HttpSession session = request.getSession();//1、获取请求头中的token(前端携带过来的)String token = request.getHeader("authorization");if(StrUtil.isBlank(token)){//判断token是否为空return true;//此处不拦截,第二个拦截器也会拦截下来,因为还没往ThreadLocal中存用户信息呢!}//2、根据该token,获取redis中的用户String key = RedisConstants.LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);//3、判断用户是否为空if(userMap.isEmpty()){return true;//此处不拦截,第二个拦截器也会拦截下来,因为还没往ThreadLocal中存用户信息呢!}//5、将从redis查询到的hash数据,转为UserDTO对象UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//6、存在,保存用户信息到ThreadLocalUserHolder.saveUser(userDTO);//7、刷新token的有效期stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);//8、放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//从ThreadLocal中,移除用户//这里是避免内存泄漏:防止ThreadLocal数据明明不再使用,却一直占用JVM内存,这就叫内存泄露UserHolder.removeUser();}
}

修改原来的拦截器:

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1、判断是否需要拦截(依据是ThreadLocal中是否有用户信息)if(UserHolder.getUser() == null){//ThreadLocal中没有用户信息,需要拦截response.setStatus(401);return false;}//由用户,则放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//从ThreadLocal中,移除用户//这里是避免内存泄漏:防止ThreadLocal数据明明不再使用,却一直占用JVM内存,这就叫内存泄露UserHolder.removeUser();}
}

然后再修改下配置类,配置一下这两个拦截器的拦截范围、执行先后顺序等:

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//登录拦截器(后执行)registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(//访问下面声明的接口前,都不走拦截器,用的排除法"/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login").order(1);//用于token刷新的拦截器(先执行)registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);}
}

其中的order,用来指定该拦截器的执行顺序,数字越小,越先执行。

addPathPatterns("/**")方法,表示新添加的拦截器,要拦截全部的请求,从而进行redis中的token有效期的刷新操作。

④重新启动项目,查看运行效果

我们登录成功后,查看此时redis中的token有效期是多少:

下面我们访问一下前端那些没有被拦截的路径,如:首页,看看这个token的有效期会不会刷新:

此时,我们的登录状态刷新就得到了优化,即:访问原先没有被拦截的请求路径,也会刷新redis中的token有效期。

以上就是本篇文章的全部内容,如果感兴趣,请关注本博主吧~~

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com