本文摘要
先简单列出用session方式。然后提出session方式的问题,并简单换为用Redis的方式。最后通过优化来解决一些小问题。
基于session实现短信登陆的简单流程
发送验证码
前端把手机号传给服务端,后端经过校验后,生成验证码并存入到session中,并通过第三方平台给用户手机发短信验证码。
登陆/注册
前端把登陆用的手机号和刚才接收的验证码传给服务端,后端经过校验后,若没毛病就用手机号去查用户表,没有用户的话给他注册,若有用户,就算是登陆成功。注册/登陆成功后,把用户的部分信息(除去敏感信息)放到session中。
1)校验手机号:是否规范,是否是刚才收到验证码的那个手机号。
2)校验验证码:放入session的验证码与前台传入的验证码比较。
查看用户登陆状态
前台调用服务端提供的相关API,因为请求会带上cookie,cookie里面有sessionid,后端通过sessionID取出相关用户信息。
用拦截器实现
一般来说,这个逻辑写在拦截器。但controller的有些部分可能会也要用到结果,所以有必要把拦截器的结果传给controller里,但要注意线程的安全,所以用ThreadLocal解决,即拦截器里搞到用户信息之后,可以把他保存在ThreadLocal,因为ThreadLocal是线程域对象,每一个进入Tomcat的请求都是一个独立的线程,所以ThreadLocal会在线程内开辟一个内存空间,去保存对应的用户,这样的话每个线程相互不干扰。所以到了controller后从ThreadLocal里取出用户即可。
代码示例:
LoginInterceptor.java
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(request,response,) {
// 进入controller之前处理
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if(user == null) {
// session中用户信息不存在,返回401状态码
response.setStatus(401);
return false;
}
// 如果session中存在用户信息,用户信息保存到ThreadLocal。由于是保存在当前线程里面的,所以不需要key。
UserHolder.saveUser((User)user);
return true;
}
@Override
public void afterCompletion(request,response,) {
// 业务执行完毕后,销毁用户信息,避免内存泄露
UserHolder.removeUser();
}
}
UserHolder.java
public class UserHolder {
pviate static final ThreadLocal<User> tl = new ThreadLocal<>();
public static void saveUser(User user) {
t1.set(user);
}
public static User getUser() {
returun tl.get();
}
public static void removeUser() {
tl.remove();
}
}
MvcConfig.java
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/user/login");
}
}
网友之言
网友1:为啥要使用ThreadLocal?
网友2:方便同一个线程可以重复使用user。
集群的session共享问题以及对应的解决方案
像上面的那样玩,在集群时会出现问题。因为多台Tomcat并不共享session存储空间,当请求切换到不同的Tomcat服务时导致数据丢失的问题。
session的替代方案需要满足的条件
- 数据共享,这是最重要的,就是因为不共享才导致了刚才的问题。
- 内存存储,因为session是内存存储,所以读写效率非常高。
- key、value结构
解决方案 - 使用Redis
因为Redis是Tomcat以外的存取方案,所以任何一台Tomcat都能访问到Redis,所以就能实现数据共享
。而且Redis是内存存储
,性能非常强,读写延时基本都是微妙级别。Red