SpringSecurity多表多端賬戶登錄的實現(xiàn)
需求:針對公司員工,普通用戶等各類型用戶,將其分別存儲在不同的用戶表中,基于SpeingSecurity實現(xiàn)用戶認(rèn)證,也就是登錄功能
流程
- 首先做數(shù)據(jù)庫設(shè)計
- 基于SpringBoot創(chuàng)建一個項目
- 項目中做相關(guān)的實現(xiàn)
- 通過apifox接口測試工具進行測試
- 分別測試不同用戶的登錄方法,是否調(diào)用了對應(yīng)的登錄邏輯【登錄也稱為認(rèn)證】
注意:權(quán)限這塊并沒有涉及,僅僅是用戶數(shù)據(jù)這塊
數(shù)據(jù)表設(shè)計
本文先不涉及權(quán)限,表設(shè)計就是兩張用戶表
員工表
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 '帳號狀態(tài)(0正常 1停用)', `creator` bigint DEFAULT '1' COMMENT '創(chuàng)建者', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時間', `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 '帳號狀態(tài)(0正常 1停用)', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時間', `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='外部用戶表';
創(chuàng)建項目
登錄功能,使用非常簡單的三層架構(gòu),技術(shù)選型有:
- SpringBoot 3.1.X
- SpringSecurity 6.1.X
- Mybatis Plus
- lombok【簡化實體類】,可以通過注解生成getter、setter方法,構(gòu)造方法,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.Driver url: jdbc:mysql://localhost:3306/spring-security?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: stt123456
創(chuàng)建三層架構(gòu)
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; } /** * 后端管理系統(tǒng)登錄 * 返回值: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實現(xiàn)的,我們就是去告訴SpringSecurity現(xiàn)在要登錄 * SpringSecirity登錄是通過 AuthticationManager 實現(xiàn)的 * 將AuthticationManager引入到service中,調(diào)用他的認(rèn)證方法就可以了 * @param loginParam * @return */ @Override public String sysLogin(LoginParam loginParam) { // 通過authenticationManager 的認(rèn)證方法實現(xiàn)登錄,該方法需要傳入 Authentication 對象 就是一個認(rèn)證對象 // Authenticationl里邊存儲的就是用戶的認(rèn)證信息,權(quán)限,用戶名,密碼的等信息,其實就是loadUserByUsername方法返回的UserDetails UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginParam.getUsername(), loginParam.getPassword()); Authentication authenticate = authenticationManager.authenticate(authenticationToken); // 獲取用戶信息 SysUser sysUser = (SysUser) authenticate.getPrincipal(); log.info("sysUser==========》{}",sysUser); // 返回的是token return sysUser.getUsername(); } }
SiteUserServiceImpl
@Service @Slf4j public class SiteUserServiceImpl extends ServiceImpl<SiteUserMapper, SiteUser> implements ISiteUserService { /** * 將AuthenticationManager注入 */ @Autowired @Qualifier("siteUserAuthenticationManager") private AuthenticationManager authenticationManager; @Override public String siteLogin(LoginParam loginParam) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginParam.getMobile(), loginParam.getPassword()); Authentication authenticate = authenticationManager.authenticate(authenticationToken); // 強轉(zhuǎn)為用戶類型 SiteUser siteUser = (SiteUser) authenticate.getPrincipal(); log.info("siteUser===========>{}",siteUser); return siteUser.getUsername(); } }
實現(xiàn)login功能
項目中引入SpringSecurity,SpringSecurity在實現(xiàn)用戶登錄【認(rèn)證】時需要使用到兩個接口
- UserDetailsService:是一個接口 ,提供了一個方法loadUserByUsername();
- UserDetails:是一個接口,用來存儲用戶權(quán)限,狀態(tài)【是否禁用,超時等】
通過UserDetailsService查詢用戶,將用戶信息放到UserDetails中,剩下的就交給SpringSecurity的AuthenticationManager做判斷,判斷用戶是否允許登錄
分兩步走
創(chuàng)建UserDetailsService接口實現(xiàn)類
查詢用戶,分別為客戶和用后臺系統(tǒng)用戶創(chuàng)建對應(yīng)的查詢用戶的實現(xiàn)類
// 系統(tǒng)用戶的DetailsService @Service public class SysUserDetailsService implements UserDetailsService { private final SysUserMapper sysUserMapper; public SysUserDetailsService(SysUserMapper sysUserMapper) { this.sysUserMapper = sysUserMapper; } /** * 此方法從數(shù)據(jù)庫中查詢用戶 * 返回一個 UserDetails */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { log.info("后臺系統(tǒng)用戶登錄=============》"); // 根據(jù)用戶名查詢用戶 SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username)); // 有權(quán)限的話,需要查詢該用戶對應(yīng)的權(quán)限 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; } @Override public 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; } }
創(chuàng)建UserDetails接口實現(xiàn)類
存儲用戶信息,同樣的創(chuàng)建兩個實現(xiàn)類,存儲不同的用戶信息,再實體類上直接修改
// 后臺管理系統(tǒng)用戶類 @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; @JsonIgnore private String password; private Integer status; private Long creator; private Long updater; private String remark; @TableLogic private Integer deleted; private LocalDateTime createTime; private LocalDateTime updateTime; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public 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; @JsonIgnore private String password; private Integer status; private Long updater; private String remark; @TableLogic private Integer deleted; private LocalDateTime createTime; private LocalDateTime updateTime; /** * 權(quán)限?,F(xiàn)在并沒有查詢權(quán)限 * @return */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
關(guān)聯(lián)
將SpringSecurity的AuthenticationManager 【認(rèn)證管理器,管登錄的組件】,與我們寫的登錄邏輯關(guān)聯(lián)起來【loadUserByUsername方法】,實現(xiàn)方式就是在SpringSecurity的配置類中實現(xiàn)
/** * 現(xiàn)在使用的是SpringSecurity 6.1.5版本,開啟SpringSecurity的自定義配置, * 需要使用 @EnableWebSecurity注解,而不再是繼承Adpater */ @Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private SysUserDetailsService sysUserDetailsService; @Autowired private SiteUserDetailsService siteUserDetailsService; // 配置SpringSecurity的過濾器鏈 @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 設(shè)置登錄接口放行 http.authorizeHttpRequests(auth -> auth.requestMatchers("/auth/sys_login","/auth/site_login").permitAll().anyRequest().authenticated()); // 關(guān)閉csrf http.csrf(csrf -> csrf.disable()); return http.build(); } // 配置AuthenticationManager,配置兩個。一個管理后臺用戶 @Primary @Bean("sysUserAuthenticationManager") public AuthenticationManager sysUserAuthenticationManager(PasswordEncoder passwordEncoder) { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); // 關(guān)聯(lián)UserDetailsService authenticationProvider.setUserDetailsService(sysUserDetailsService); // 關(guān)聯(lián)密碼管理器 authenticationProvider.setPasswordEncoder(passwordEncoder); return new ProviderManager(authenticationProvider); } // 配置AuthenticationManager,管理APP用戶 @Bean("siteUserAuthenticationManager") public AuthenticationManager siteUserAuthenticationManager(PasswordEncoder passwordEncoder) { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); // 關(guān)聯(lián)UserDetailsService authenticationProvider.setUserDetailsService(siteUserDetailsService); // 關(guān)聯(lián)密碼管理器 authenticationProvider.setPasswordEncoder(passwordEncoder); return new ProviderManager(authenticationProvider); } /** * 密碼管理器,會將明文密碼轉(zhuǎn)換成密文,加密,而且不能解碼 * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
密碼
需要對數(shù)據(jù)庫中存儲的密碼進行編碼,因為SpringSecurity進行密碼匹配時,會對用戶輸入的密碼先編碼,再驗證,先通過PasswordEncoder生成加密后的密碼
@SpringBootTest public class MyTestApplication { @Autowired private PasswordEncoder passwordEncoder; @Test public void test() { // 密碼加密 String encode = passwordEncoder.encode("123456"); System.out.println(encode); } }
SpringSecurity配置
創(chuàng)建兩個AuthenticationManager,一定要設(shè)置一個主AuthenticationManager
否則將會報錯,即在任意一個Bean上添加@Primary
注解標(biāo)記
@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; }
數(shù)據(jù)庫配置
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springsecurity?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: stt123456
AuthenticationManager
AuthenticationManager用于定義SpringSecurity如何進行身份認(rèn)證,之后將認(rèn)證信息封裝在Authentication對象上,設(shè)置到SecurityContextHolder上,AuthenticationManager常用的實現(xiàn)是ProviderManager,你也可以對其做自定義實現(xiàn)。
到此這篇關(guān)于SpringSecurity多表多端賬戶登錄的實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringSecurity多表多端賬戶登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringSecurity集成第三方登錄過程詳解(最新推薦)
- springsecurity實現(xiàn)用戶登錄認(rèn)證快速使用示例代碼(前后端分離項目)
- SpringSecurity自動登錄流程與實現(xiàn)詳解
- SpringSecurity6自定義JSON登錄的實現(xiàn)
- SpringSecurity6.x多種登錄方式配置小結(jié)
- 如何使用JWT的SpringSecurity實現(xiàn)前后端分離
- SpringSecurity+Redis+Jwt實現(xiàn)用戶認(rèn)證授權(quán)
- SpringSecurity角色權(quán)限控制(SpringBoot+SpringSecurity+JWT)
- SpringBoot3.0+SpringSecurity6.0+JWT的實現(xiàn)
- springSecurity之如何添加自定義過濾器
- springSecurity自定義登錄接口和JWT認(rèn)證過濾器的流程
相關(guān)文章
關(guān)于@Transactional事務(wù)表被鎖的問題及解決
這篇文章主要介紹了關(guān)于@Transactional事務(wù)表被鎖的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01解決SpringBoot啟動過后不能訪問jsp頁面的問題(超詳細(xì))
這篇文章主要介紹了解決SpringBoot啟動過后不能訪問jsp頁面的問題,文中通過示例代碼介紹的非常詳細(xì),有需要的朋友可以參考一下,希望對你有所幫助。2020-05-05Springboot中spring-boot-starter-quartz的使用及說明
這篇文章主要介紹了Springboot中spring-boot-starter-quartz的使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12