怎么使用Redis的共享session实现短信登录
导读:本文共6386.5字符,通常情况下阅读需要21分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要:");}//3.符合,生成验证码(设置生成6位)Stringcode=RandomUtil.randomNumbers(6);//4.保存验证码到sessionsession.setAttribute("code",code);//5.发送验证码(这里并未实现,通过日志记录)log.debug("发送短信验证码成功,验证码:{}",c... ...
目录
(为您整理了一些要点),点击可以直达。");
}
//3.符合,生成验证码(设置生成6位)
Stringcode=RandomUtil.randomNumbers(6);
//4.保存验证码到session
session.setAttribute("code",code);
//5.发送验证码(这里并未实现,通过日志记录)
log.debug("发送短信验证码成功,验证码:{}",code);
//返回ok
returnResult.ok();
}
}
1.3 实现短信验证码登录、注册
前端请求说明
后端接口实现:
@Slf4j
@Service
publicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{@Override
publicResultlogin(LoginFormDTOloginForm,HttpSessionsession){
//1.校验手机号
Stringphone=loginForm.getPhone();
if(RegexUtils.isPhoneInvalid(phone)){
//不一致,返回错误信息
returnResult.fail("手机号格式错误!");
}
//2.校验验证码
StringcacheCode=(String)session.getAttribute("code");
Stringcode=loginForm.getCode();
if(cacheCode==null||!cacheCode.equals(cacheCode)){
//不一致,返回错误信息
returnResult.fail("验证码错误!");
}
//4.一致,根据手机号查询用户(这里使用的mybatis-plus)
Useruser=query().eq("phone",phone).one();
//5.判断用户是否存在
if(user==null){
//6.不存在,创建新用户并保存
user=createUserWithPhone(phone);
}
//7.保存用户信息到session中(通过BeanUtil.copyProperties方法将user中的信息过滤到UserDTO上,即用来隐藏部分信息)
session.setAttribute("user",BeanUtil.copyProperties(user,UserDTO.class));
returnResult.ok();
}privateUsercreateUserWithPhone(Stringphone){
//1.创建用户
Useruser=newUser();
user.setPhone(phone);
user.setNickName("user_"+RandomUtil.randomString(10));
//2.保存用户(这里使用mybatis-plus)
save(user);
returnuser;
}
}
1.4 实现登录校验拦截器
登录校验拦截器实现:
publicclassLoginInterceptorimplementsHandlerInterceptor{
@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{
//1.获取session
HttpSessionsession=request.getSession();
//2.获取session中的用户
UserDTOuser=(UserDTO)session.getAttribute("user");
//3.判断用户是否存在
if(user==null){
//4.不存在,拦截,返回401未授权
response.setStatus(401);
returnfalse;
}
//5.存在,保存用户信息到ThreadLocal
UserHolder.saveUser(user);
//6.放行
returntrue;
}@Override
publicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{
//移除用户,避免内存泄露
UserHolder.removeUser();
}
}
UserHolder 类的实现: 该类中定义了一个静态的 ThreadLocal
publicclassUserHolder{
privatestaticfinalThreadLocal<UserDTO>tl=newThreadLocal<>();publicstaticvoidsaveUser(UserDTOuser){
tl.set(user);
}publicstaticUserDTOgetUser(){
returntl.get();
}publicstaticvoidremoveUser(){
tl.remove();
}
}
配置拦截器:
@Configuration
publicclassMvcConfigimplementsWebMvcConfigurer{@Override
publicvoidaddInterceptors(InterceptorRegistryregistry){
registry.addInterceptor(newLoginInterceptor())
.excludePathPatterns(
"/user/login",
"/user/code"
);
}
}
前端请求说明:
后端接口实现:
@Slf4j
@Service
publicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{@Override
publicResultme(){
UserDTOuser=UserHolder.getUser();
returnResult.ok(user);
}
}
2. 集群的 session 共享问题
session 共享问题:
多台 tomcat 并不共享 session 存储空间,当请求切换到不同 tomcat 服务时会导致数据丢失的问题。
session 的替代方案应该满足以下条件:
数据共享(不同的 tomcat 都能够访问 Redis 中的数据)
内存存储(Redis 通过内存存储)
key、value 结构(Redis 是 key-value 结构)
3. 基于 Redis 实现共享 session 登录
3.1 Redis 实现共享 session 登录流程图
3.2 实现发送短信验证码
前端请求说明:
后端接口实现:
@Slf4j
@Service
publicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{@Resource
privateStringRedisTemplatestringRedisTemplate;@Override
publicResultsendCode(Stringphone,HttpSessionsession){
//1.校验手机号
if(RegexUtils.isPhoneInvalid(phone)){
//2.如果不符合,返回错误信息
returnResult.fail("手机号格式错误!");
}
//3.符合,生成验证码(设置生成6位)
Stringcode=RandomUtil.randomNumbers(6);
//4.保存验证码到Redis(以手机号为key,设置有效期为2min)
stringRedisTemplate.opsForValue().set("login:code:"+phone,code,2,TimeUnit.MINUTES);
//5.发送验证码(这里并未实现,通过日志记录)
log.debug("发送短信验证码成功,验证码:{}",code);
//返回ok
returnResult.ok();
}
}
3.3 实现短信验证码登录、注册
前端请求说明:
后端接口实现:
@Slf4j
@Service
publicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{@Override
publicResultlogin(LoginFormDTOloginForm,HttpSessionsession){
//1.校验手机号
Stringphone=loginForm.getPhone();
if(RegexUtils.isPhoneInvalid(phone)){
//不一致,返回错误信息
returnResult.fail("手机号格式错误!");
}
//2.校验验证码
StringcacheCode=(String)session.getAttribute("code");
Stringcode=loginForm.getCode();
if(cacheCode==null||!cacheCode.equals(cacheCode)){
//不一致,返回错误信息
returnResult.fail("验证码错误!");
}
//4.一致,根据手机号查询用户(这里使用的mybatis-plus)
Useruser=query().eq("phone",phone).one();
//5.判断用户是否存在
if(user==null){
//6.不存在,创建新用户并保存
user=createUserWithPhone(phone);
}
//7.保存用户信息到session中(通过BeanUtil.copyProperties方法将user中的信息过滤到UserDTO上,即用来隐藏部分信息)
session.setAttribute("user",BeanUtil.copyProperties(user,UserDTO.class));
returnResult.ok();
}privateUsercreateUserWithPhone(Stringphone){
//1.创建用户
Useruser=newUser();
user.setPhone(phone);
user.setNickName("user_"+RandomUtil.randomString(10));
//2.保存用户(这里使用mybatis-plus)
save(user);
returnuser;
}
}
3.4 实现登录校验拦截器
这里将原有的一个拦截器分成两个拦截器,第一个拦截器对所有的请求进行拦截,每次拦截刷新 token 的有效期,并将能查询到的用户信息保存到 ThreadLocal 中。第二个拦截器则进行拦截功能,对需要登录的路径进行拦截。
刷新 token 拦截器实现:
publicclassRefreshTokenInterceptorimplementsHandlerInterceptor{privateStringRedisTemplatestringRedisTemplate;
publicRefreshTokenInterceptor(StringRedisTemplatestringRedisTemplate){
this.stringRedisTemplate=stringRedisTemplate;
}@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{
//1.获取请求头中的token
Stringtoken=request.getHeader("authorization");
if(StrUtil.isBlank(token)){
returntrue;
}
//2.基于token获取redis中的用户
StringtokenKey="login:token:"+token;
Map<Object,Object>userMap=stringRedisTemplate.opsForHash().entries(tokenKey);
//3.判断用户是否存在
if(userMap.isEmpty()){
returntrue;
}
//5.将查询到的Hash数据转为UserDTO对象
UserDTOuser=BeanUtil.fillBeanWithMap(userMap,newUserDTO(),false);
//6.存在,保存用户信息到ThreadLocal
UserHolder.saveUser(user);
//7.刷新token有效期30min
stringRedisTemplate.expire(tokenKey,30,TimeUnit.MINUTES);
//8.放行
returntrue;
}
}
登录校验拦截器实现:
publicclassLoginInterceptorimplementsHandlerInterceptor{
@Override
publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{
//1.获取session
HttpSessionsession=request.getSession();
//2.获取session中的用户
UserDTOuser=(UserDTO)session.getAttribute("user");
//3.判断用户是否存在
if(user==null){
//4.不存在,拦截,返回401未授权
response.setStatus(401);
returnfalse;
}
//5.存在,保存用户信息到ThreadLocal
UserHolder.saveUser(user);
//6.放行
returntrue;
}@Override
publicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{
//移除用户,避免内存泄露
UserHolder.removeUser();
}
}
UserHolder 类的实现: 该类中定义了一个静态的 ThreadLocal
publicclassUserHolder{
privatestaticfinalThreadLocal<UserDTO>tl=newThreadLocal<>();publicstaticvoidsaveUser(UserDTOuser){
tl.set(user);
}publicstaticUserDTOgetUser(){
returntl.get();
}publicstaticvoidremoveUser(){
tl.remove();
}
}
配置拦截器:
@Configuration
publicclassMvcConfigimplementsWebMvcConfigurer{@Resource
privateStringRedisTemplatestringRedisTemplate;@Override
publicvoidaddInterceptors(InterceptorRegistryregistry){
registry.addInterceptor(newRefreshTokenInterceptor(stringRedisTemplate))
.addPathPatterns("/**").order(0);
registry.addInterceptor(newLoginInterceptor())
.excludePathPatterns(
"/user/login",
"/user/code"
).order(1);
}
}
前端请求说明:
后端接口实现:
@Slf4j
@Service
publicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsIUserService{@Override
publicResultme(){
UserDTOuser=UserHolder.getUser();
returnResult.ok(user);
}
}
读到这里,这篇“怎么使用Redis的共享session实现短信登录”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。
怎么使用Redis的共享session实现短信登录的详细内容,希望对您有所帮助,信息来源于网络。