SpringSecurity多表,多端账户登录

2024-05-14 04:52

本文主要是介绍SpringSecurity多表,多端账户登录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文章对应视频SpringSecurity6多端账号登录,可无限扩展教程,记得三连哦,这对我很重要呢!
温馨提示:视频与文章相辅相成,结合学习效果更强哦!更多视频教程可移步B站【石添的编程哲学】

SpringSecurity实现多表账户登录

需求:针对公司员工,普通用户等各类型用户,将其分别存储在不同的用户表中,基于SpeingSecurity实现用户认证,也就是登录功能

流程

  • 首先做数据库设计
  • 基于SpringBoot创建一个项目
  • 项目中做相关的实现
  • 通过apifox接口测试工具进行测试
  • 分别测试不同用户的登录方法,是否调用了对应的登录逻辑【登录也称为认证】

注意:权限这块并没有涉及,仅仅是用户数据这块

数据表设计

本文先不涉及权限,表设计就是两张用户表

员工表

CREATE TABLE `ums_sys_user` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户账号',`nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户昵称',`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '用户邮箱',`mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '手机号码',`sex` int DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',`avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '头像地址',`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '密码',`status` int DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',`creator` bigint DEFAULT '1' COMMENT '创建者',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`updater` bigint DEFAULT '1' COMMENT '更新者',`update_time` datetime DEFAULT NULL COMMENT '更新时间',`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注',`deleted` tinyint DEFAULT '0',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='后台用户表';

客户表

CREATE TABLE `ums_site_user` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户账号',`nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户昵称',`openid` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '微信openid',`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '用户邮箱',`mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '手机号码',`sex` int DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',`avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '头像地址',`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '密码',`status` int DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`updater` bigint DEFAULT '1' COMMENT '更新者',`update_time` datetime DEFAULT NULL COMMENT '更新时间',`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注',`deleted` tinyint DEFAULT '0',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='外部用户表';

创建项目

登录功能,使用非常简单的三层架构,技术选型有:

  • SpringBoot 3.1.X
  • SpringSecurity 6.1.X
  • Mybatis Plus
  • lombok【简化实体类】,可以通过注解生成getter、setter方法,构造方法,toString方法等
  • maven

pom文件

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
</dependencies>

application.yml文件配置

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/spring-security?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: stt123456

创建三层架构

Controller

@RestController
@RequestMapping("/auth")
public class AuthController {private final ISysUserService sysUserService;private final ISiteUserService siteUserService;public AuthController(ISysUserService sysUserService, ISiteUserService siteUserService) {this.sysUserService = sysUserService;this.siteUserService = siteUserService;}/*** 后端管理系统登录* 返回值:token*/@PostMapping("sys_login")public String sysLogin(@RequestBody LoginParam loginParam) {return "后台用户登录======》" +sysUserService.sysLogin(loginParam);}@PostMapping("site_login")public String siteLogin(@RequestBody LoginParam loginParam) {return "APP用户登录======》" + siteUserService.siteLogin(loginParam);}}

SysUserServiceImpl

@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {@Autowired@Qualifier("sysUserAuthenticationManager")private AuthenticationManager authenticationManager;/*** 登录是SpringSecurity实现的,我们就是去告诉SpringSecurity现在要登录* SpringSecirity登录是通过 AuthticationManager 实现的* 将AuthticationManager引入到service中,调用他的认证方法就可以了* @param loginParam* @return*/@Overridepublic String sysLogin(LoginParam loginParam) {// 通过authenticationManager 的认证方法实现登录,该方法需要传入 Authentication 对象 就是一个认证对象// Authenticationl里边存储的就是用户的认证信息,权限,用户名,密码的等信息,其实就是loadUserByUsername方法返回的UserDetailsUsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginParam.getUsername(), loginParam.getPassword());Authentication authenticate = authenticationManager.authenticate(authenticationToken);// 获取用户信息SysUser sysUser = (SysUser) authenticate.getPrincipal();log.info("sysUser==========》{}",sysUser);// 返回的是tokenreturn sysUser.getUsername();}
}

SiteUserServiceImpl

@Service
@Slf4j
public class SiteUserServiceImpl extends ServiceImpl<SiteUserMapper, SiteUser> implements ISiteUserService {/*** 将AuthenticationManager注入*/@Autowired@Qualifier("siteUserAuthenticationManager")private AuthenticationManager authenticationManager;@Overridepublic String siteLogin(LoginParam loginParam) {UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginParam.getMobile(), loginParam.getPassword());Authentication authenticate = authenticationManager.authenticate(authenticationToken);// 强转为用户类型SiteUser siteUser = (SiteUser) authenticate.getPrincipal();log.info("siteUser===========>{}",siteUser);return siteUser.getUsername();}
}

实现login功能

项目中引入SpringSecurity,SpringSecurity在实现用户登录【认证】时需要使用到两个接口

  • UserDetailsService:是一个接口 ,提供了一个方法loadUserByUsername();
  • UserDetails:是一个接口,用来存储用户权限,状态【是否禁用,超时等】

通过UserDetailsService查询用户,将用户信息放到UserDetails中,剩下的就交给SpringSecurity的AuthenticationManager做判断,判断用户是否允许登录

分两步走

创建UserDetailsService接口实现类

查询用户,分别为客户和用后台系统用户创建对应的查询用户的实现类

// 系统用户的DetailsService
@Service
public class SysUserDetailsService implements UserDetailsService {private final SysUserMapper sysUserMapper;public SysUserDetailsService(SysUserMapper sysUserMapper) {this.sysUserMapper = sysUserMapper;}/*** 此方法从数据库中查询用户* 返回一个 UserDetails*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {log.info("后台系统用户登录=============》");// 根据用户名查询用户SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username));// 有权限的话,需要查询该用户对应的权限if(sysUser == null) {throw new UsernameNotFoundException("用户或密码不正确");}return sysUser;}
}
// APP用户的DetailsService
@Slf4j
public class SiteUserDetailsService implements UserDetailsService {private final SiteUserMapper siteUserMapper;public SiteUserDetailsService(SiteUserMapper siteUserMapper) {this.siteUserMapper = siteUserMapper;}@Overridepublic UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {log.info("APP用户登录===================》");SiteUser siteUser = siteUserMapper.selectOne(new LambdaQueryWrapper<SiteUser>().eq(SiteUser::getMobile, mobile));if(siteUser == null) {throw new UsernameNotFoundException("用户名或密码错误!");}return siteUser;}
}

创建UserDetails接口实现类

存储用户信息,同样的创建两个实现类,存储不同的用户信息,再实体类上直接修改

// 后台管理系统用户类
@TableName("ums_sys_user")
@Data
public class SysUser implements Serializable, UserDetails {private Long id;private String username;private String nickname;private String email;private String mobile;private Integer sex;private String avatar;@JsonIgnoreprivate String password;private Integer status;private Long creator;private Long updater;private String remark;@TableLogicprivate Integer deleted;private LocalDateTime createTime;private LocalDateTime updateTime;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
// APP用户实体类
@Data
@TableName("ums_site_user")
public class SiteUser implements Serializable, UserDetails {private Long id;private String username;private String nickname;private String openid;private String email;private String mobile;private Integer sex;private String avatar;@JsonIgnoreprivate String password;private Integer status;private Long updater;private String remark;@TableLogicprivate Integer deleted;private LocalDateTime createTime;private LocalDateTime updateTime;/*** 权限。现在并没有查询权限* @return*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

关联

将SpringSecurity的AuthenticationManager 【认证管理器,管登录的组件】,与我们写的登录逻辑关联起来【loadUserByUsername方法】,实现方式就是在SpringSecurity的配置类中实现

/*** 现在使用的是SpringSecurity 6.1.5版本,开启SpringSecurity的自定义配置,* 需要使用 @EnableWebSecurity注解,而不再是继承Adpater*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Autowiredprivate SysUserDetailsService sysUserDetailsService;@Autowiredprivate SiteUserDetailsService siteUserDetailsService;// 配置SpringSecurity的过滤器链@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {// 设置登录接口放行http.authorizeHttpRequests(auth -> auth.requestMatchers("/auth/sys_login","/auth/site_login").permitAll().anyRequest().authenticated());// 关闭csrfhttp.csrf(csrf -> csrf.disable());return http.build();}// 配置AuthenticationManager,配置两个。一个管理后台用户@Primary@Bean("sysUserAuthenticationManager")public AuthenticationManager sysUserAuthenticationManager(PasswordEncoder passwordEncoder) {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();// 关联UserDetailsServiceauthenticationProvider.setUserDetailsService(sysUserDetailsService);// 关联密码管理器authenticationProvider.setPasswordEncoder(passwordEncoder);return new ProviderManager(authenticationProvider);}// 配置AuthenticationManager,管理APP用户@Bean("siteUserAuthenticationManager")public AuthenticationManager siteUserAuthenticationManager(PasswordEncoder passwordEncoder) {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();// 关联UserDetailsServiceauthenticationProvider.setUserDetailsService(siteUserDetailsService);// 关联密码管理器authenticationProvider.setPasswordEncoder(passwordEncoder);return new ProviderManager(authenticationProvider);}/*** 密码管理器,会将明文密码转换成密文,加密,而且不能解码* @return*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

密码

需要对数据库中存储的密码进行编码,因为SpringSecurity进行密码匹配时,会对用户输入的密码先编码,再验证,先通过PasswordEncoder生成加密后的密码

@SpringBootTest
public class MyTestApplication {@Autowiredprivate PasswordEncoder passwordEncoder;@Testpublic void test() {// 密码加密String encode = passwordEncoder.encode("123456");System.out.println(encode);}
}

SpringSecurity配置

创建两个AuthenticationManager,一定要设置一个主AuthenticationManager否则将会报错,即在任意一个Bean上添加@Primary注解标记

@Primary
@Bean("sysAuthenticationManager")
public AuthenticationManager sysAuthenticationManager(PasswordEncoder passwordEncoder) {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();authenticationProvider.setUserDetailsService(sysUserDetailsService);authenticationProvider.setPasswordEncoder(passwordEncoder);ProviderManager providerManager = new ProviderManager(authenticationProvider);providerManager.setEraseCredentialsAfterAuthentication(false);return providerManager;
}/*** 外部用户验证管理器* @param passwordEncoder* @return*/
@Bean("siteAuthenticationManager")
public AuthenticationManager siteAuthenticationManager(PasswordEncoder passwordEncoder) {DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();authenticationProvider.setUserDetailsService(siteUserDetailsService);authenticationProvider.setPasswordEncoder(passwordEncoder);ProviderManager providerManager = new ProviderManager(authenticationProvider);providerManager.setEraseCredentialsAfterAuthentication(false);return providerManager;
}

数据库配置

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/springsecurity?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: stt123456

AuthenticationManager

AuthenticationManager用于定义SpringSecurity如何进行身份认证,之后将认证信息封装在Authentication对象上,设置到SecurityContextHolder上,AuthenticationManager常用的实现是ProviderManager,你也可以对其做自定义实现。

这篇关于SpringSecurity多表,多端账户登录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/987803

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听