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

 

posted @   wwwwwwwty  阅读(1057)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示