Spring Boot多角色对象的数据库设计与认证逻辑处理咨询
解决方案:多角色用户的数据表设计与Spring Boot登录实现
一、数据表设计方案(主从表结构)
采用公共认证信息主表+角色专属信息子表的结构,既保证认证逻辑统一,又能灵活存储各角色的专属数据,避免单表冗余。
1. 主表:user(存储所有角色的公共认证信息)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | BIGINT | 主键,自增 |
| VARCHAR(100) | 登录账号,唯一非空 | |
| password | VARCHAR(255) | 加密后的密码,非空 |
| otp | VARCHAR(10) | 一次性验证码(可选) |
| role | VARCHAR(20) | 角色枚举(PATIENT/DOCTOR/LAB_TECH/RECEPTIONIST/CASHIER/ADMIN) |
| enabled | BOOLEAN | 账号是否启用,默认true |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
2. 角色子表(存储各角色专属信息)
每个角色对应一张子表,通过id与user表主键一对一关联(也可单独用user_id作为外键):
patient表:id(外键关联user.id)、patient_no(患者编号)、medical_history(病史)、allergy_info(过敏史)等doctor表:id、doctor_no(医生编号)、specialty(专业方向)、license_no(执业证号)等lab_tech/receptionist/cashier表:同理,添加各自业务所需的专属字段
二、Spring Boot实体类与Repository设计
1. 角色枚举类
public enum Role { PATIENT, DOCTOR, LAB_TECH, RECEPTIONIST, CASHIER, ADMIN }
2. 主实体User
@Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String email; @Column(nullable = false) private String password; private String otp; @Enumerated(EnumType.STRING) private Role role; private boolean enabled; @Column(name = "create_time") private LocalDateTime createTime; @Column(name = "update_time") private LocalDateTime updateTime; // 一对一关联各角色子表(按需添加) @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Patient patient; @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Doctor doctor; // Getter、Setter、构造方法 }
3. 角色子实体(以Patient为例)
@Entity @Table(name = "patient") public class Patient { @Id private Long id; // 与User表id完全一致 @OneToOne @MapsId // 映射User的主键作为当前实体主键 @JoinColumn(name = "id") private User user; @Column(name = "patient_no") private String patientNo; @Column(name = "medical_history") private String medicalHistory; @Column(name = "allergy_info") private String allergyInfo; // Getter、Setter }
4. Repository层
- 公共
UserRepository:负责登录时的用户查询
public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByEmail(String email); }
- 角色专属Repository(以
PatientRepository为例):
public interface PatientRepository extends JpaRepository<Patient, Long> { }
其他角色的Repository同理创建即可。
三、登录流程与角色识别实现
1. 核心登录逻辑(AuthService)
@Service public class AuthService { @Autowired private UserRepository userRepository; @Autowired private PatientRepository patientRepository; @Autowired private DoctorRepository doctorRepository; @Autowired private PasswordEncoder passwordEncoder; @Autowired private TokenRepository tokenRepository; public AuthResponse login(LoginRequest request) { // 1. 根据邮箱查询用户 User user = userRepository.findByEmail(request.getEmail()) .orElseThrow(() -> new RuntimeException("账号不存在")); // 2. 验证密码 if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { throw new RuntimeException("密码错误"); } // 3. 验证OTP(如果业务需要) if (StringUtils.hasText(user.getOtp()) && !user.getOtp().equals(request.getOtp())) { throw new RuntimeException("OTP验证码错误"); } // 4. 根据角色获取专属信息 Object roleDetail = null; switch (user.getRole()) { case PATIENT: roleDetail = patientRepository.findById(user.getId()).orElse(null); break; case DOCTOR: roleDetail = doctorRepository.findById(user.getId()).orElse(null); break; case LAB_TECH: // 调用LabTechRepository查询 break; case RECEPTIONIST: // 调用ReceptionistRepository查询 break; case CASHIER: // 调用CashierRepository查询 break; case ADMIN: // 管理员无专属子表则无需查询 break; } // 5. 生成登录Token(以JWT为例) String token = JwtUtils.generateToken(user.getId(), user.getRole().name()); // 6. 保存Token到token表(会话管理) Token tokenEntity = new Token(); tokenEntity.setToken(token); tokenEntity.setUserId(user.getId()); tokenEntity.setExpireTime(LocalDateTime.now().plusHours(24)); tokenEntity.setValid(true); tokenRepository.save(tokenEntity); // 7. 返回登录响应 return new AuthResponse(user.getId(), user.getEmail(), user.getRole(), roleDetail, token); } }
2. Spring Security整合(可选)
如果用Spring Security做认证,自定义UserDetailsService实现角色加载:
@Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { User user = userRepository.findByEmail(email) .orElseThrow(() -> new UsernameNotFoundException("账号不存在")); return User.builder() .username(user.getEmail()) .password(user.getPassword()) .roles(user.getRole().name()) .accountEnabled(user.isEnabled()) .build(); } }
四、方案优势
- 逻辑统一:所有角色的认证流程复用
user表,无需重复开发登录逻辑 - 扩展性强:新增角色只需添加对应子表和实体,不影响现有结构
- 数据整洁:避免单表冗余字段,各角色专属数据独立存储
- 性能友好:登录时仅查询
user表,专属数据按需加载,减少IO开销
内容的提问来源于stack exchange,提问作者TunazZz
相关产品推荐
相关产品推荐

