SpringSecurity登录时报错栈溢出,StackOverflow,SpringSecurity多端登录实现方案
最近在用spring security做一套医疗项目,要求一套后端对应两套前端界面,用户(患者)和医生。
先写的用户登录界面,没有问题,再用同样方法写医生登录的时候报错栈溢出 stack overflow。
先看下spring security登录的时序图
1,把前端传过来的用户名和密码封装成UsernamePasswordAuthentication对象
2,调用DaoAuthenticationProvider的authenticate方法进行认证,我们需要写一个类实现springSecurity提供的UserDetailService接口并重写loadUserByUsername方法,
这样在调用authenticate方法时,就会自动调用loadUserByUsername方法,查询数据库与封装的UsernamePasswordAuthentication进行对比,封装用户权限。
debug调试,发现是在执行到这一行,调用loadUserByUsername方法时出现的错误
分析原因:我在写用户登录的时候,写了一个LoginUserDetail来实现UserDetailsService接口,并重写了loadUserByUsername方法,实现了用户登录。
之后写医生端的登录,以为是一样的套路,写了一个LoginDoctorDetail又一次实现了UserDetailsService接口,并重写了loadUserByUsername方法,在使用医生账号登录时报错stack overflow,网上查询原因,说springSecurity默认只能接受UserDetailService有一个实现类,否则DaoAuthenticProvider就会不知道调用哪个loadUserByUsername方法,会导致框架内部的方法持续递归调用,导致栈溢出 (个人感觉此处框架设计的不太合理,应该提示一下错误原因)
修改方法:既然不能有多个实现类,就干脆都放到一个实现类里,登录方式都是电话+密码,这样就先根据电话查用户表,查到了说明是用户登录,返回springSecurity要求的用户实体类,没查到再继续查医生表,返回医生实体类(医生需要权限控制)
package com.woniu.user.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.woniu.doctor.mapper.DoctorMapper; import com.woniu.entity.Doctor; import com.woniu.entity.LoginDoctor; import com.woniu.entity.LoginUser; import com.woniu.entity.User; import com.woniu.user.mapper.UserMapper; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Set; @Service public class LoginUserImpl implements UserDetailsService { @Resource private UserMapper userMapper; @Resource private DoctorMapper doctorMapper; @Override public UserDetails loadUserByUsername(String userTel) throws UsernameNotFoundException { //因为用户数量肯定远多于医生,所以先查询用户信息 QueryWrapper<User> queryWrapper=new QueryWrapper<>(); queryWrapper.eq("tel",userTel); //先根据手机号查user User user = userMapper.selectOne(queryWrapper); if (user != null){ return new LoginUser(user); } //没查到user,再查doctor QueryWrapper<Doctor> queryWrapper1=new QueryWrapper<>(); queryWrapper1.eq("tel",userTel); Doctor doctor = doctorMapper.selectOne(queryWrapper1); if (doctor != null){ //查询对应的医生信息和权限信息 Long doctorId = doctor.getId(); int count = doctorMapper.queryPermission(doctorId); Set<String> permissions; //count大于零说明医生是组长,获取组长权限 if (count > 0){ permissions = doctorMapper.selectLeaderPerms(); } else{ permissions = doctorMapper.selectDoctorPerms(); } return new LoginDoctor(doctor,permissions); } //TODO 如果user和doctor都没查到,再查admin return null; } }
总结:此方法实现起来较简单,但是算是投机取巧的一种方法把,在医生登录时需要多查一次用户表,并且需要要求一个手机号不能同时注册用户和医生端两个账号,这样肯定不是最优解,但是由于医生数量肯定远小于用户数量, 应该是可以接受的。
另外在解决问题时还发现了另一种方法可以满足实现多次UserDetailService方法实现多端登录,需要自定义authenticationFileter和authenticatProvider,得对源码有一定理解,虽然实现起来较麻烦,但是比较正规,具体可以参考如下
https://blog.csdn.net/weixin_43738764/article/details/122459141
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律