SpringSecurity多表多端賬戶登錄的實(shí)現(xiàn)
需求:針對公司員工,普通用戶等各類型用戶,將其分別存儲(chǔ)在不同的用戶表中,基于SpeingSecurity實(shí)現(xiàn)用戶認(rèn)證,也就是登錄功能
流程
- 首先做數(shù)據(jù)庫設(shè)計(jì)
- 基于SpringBoot創(chuàng)建一個(gè)項(xiàng)目
- 項(xiàng)目中做相關(guān)的實(shí)現(xiàn)
- 通過apifox接口測試工具進(jìn)行測試
- 分別測試不同用戶的登錄方法,是否調(diào)用了對應(yīng)的登錄邏輯【登錄也稱為認(rèn)證】
注意:權(quán)限這塊并沒有涉及,僅僅是用戶數(shù)據(jù)這塊
數(shù)據(jù)表設(shè)計(jì)
本文先不涉及權(quán)限,表設(shè)計(jì)就是兩張用戶表
員工表
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 '手機(jī)號碼', `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)建時(shí)間', `updater` bigint DEFAULT '1' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新時(shí)間', `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='后臺(tái)用戶表';
客戶表
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 '手機(jī)號碼', `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)建時(shí)間', `updater` bigint DEFAULT '1' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新時(shí)間', `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)建項(xiàng)目
登錄功能,使用非常簡單的三層架構(gòu),技術(shù)選型有:
- SpringBoot 3.1.X
- SpringSecurity 6.1.X
- Mybatis Plus
- lombok【簡化實(shí)體類】,可以通過注解生成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 "后臺(tái)用戶登錄======》" +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實(shí)現(xiàn)的,我們就是去告訴SpringSecurity現(xiàn)在要登錄 * SpringSecirity登錄是通過 AuthticationManager 實(shí)現(xiàn)的 * 將AuthticationManager引入到service中,調(diào)用他的認(rèn)證方法就可以了 * @param loginParam * @return */ @Override public String sysLogin(LoginParam loginParam) { // 通過authenticationManager 的認(rèn)證方法實(shí)現(xiàn)登錄,該方法需要傳入 Authentication 對象 就是一個(gè)認(rèn)證對象 // Authenticationl里邊存儲(chǔ)的就是用戶的認(rèn)證信息,權(quán)限,用戶名,密碼的等信息,其實(shí)就是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); // 強(qiáng)轉(zhuǎn)為用戶類型 SiteUser siteUser = (SiteUser) authenticate.getPrincipal(); log.info("siteUser===========>{}",siteUser); return siteUser.getUsername(); } }
實(shí)現(xiàn)login功能
項(xiàng)目中引入SpringSecurity,SpringSecurity在實(shí)現(xiàn)用戶登錄【認(rèn)證】時(shí)需要使用到兩個(gè)接口
- UserDetailsService:是一個(gè)接口 ,提供了一個(gè)方法loadUserByUsername();
- UserDetails:是一個(gè)接口,用來存儲(chǔ)用戶權(quán)限,狀態(tài)【是否禁用,超時(shí)等】
通過UserDetailsService查詢用戶,將用戶信息放到UserDetails中,剩下的就交給SpringSecurity的AuthenticationManager做判斷,判斷用戶是否允許登錄
分兩步走
創(chuàng)建UserDetailsService接口實(shí)現(xiàn)類
查詢用戶,分別為客戶和用后臺(tái)系統(tǒng)用戶創(chuàng)建對應(yīng)的查詢用戶的實(shí)現(xiàn)類
// 系統(tǒng)用戶的DetailsService @Service public class SysUserDetailsService implements UserDetailsService { private final SysUserMapper sysUserMapper; public SysUserDetailsService(SysUserMapper sysUserMapper) { this.sysUserMapper = sysUserMapper; } /** * 此方法從數(shù)據(jù)庫中查詢用戶 * 返回一個(gè) UserDetails */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { log.info("后臺(tái)系統(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("用戶名或密碼錯(cuò)誤!"); } return siteUser; } }
創(chuàng)建UserDetails接口實(shí)現(xiàn)類
存儲(chǔ)用戶信息,同樣的創(chuàng)建兩個(gè)實(shí)現(xiàn)類,存儲(chǔ)不同的用戶信息,再實(shí)體類上直接修改
// 后臺(tái)管理系統(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用戶實(shí)體類 @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)限。現(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方法】,實(shí)現(xiàn)方式就是在SpringSecurity的配置類中實(shí)現(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,配置兩個(gè)。一個(gè)管理后臺(tái)用戶 @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); } /** * 密碼管理器,會(huì)將明文密碼轉(zhuǎn)換成密文,加密,而且不能解碼 * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
密碼
需要對數(shù)據(jù)庫中存儲(chǔ)的密碼進(jìn)行編碼,因?yàn)镾pringSecurity進(jìn)行密碼匹配時(shí),會(huì)對用戶輸入的密碼先編碼,再驗(yàn)證,先通過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)建兩個(gè)AuthenticationManager,一定要設(shè)置一個(gè)主AuthenticationManager
否則將會(huì)報(bào)錯(cuò),即在任意一個(gè)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; } /** * 外部用戶驗(yàn)證管理器 * @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如何進(jìn)行身份認(rèn)證,之后將認(rèn)證信息封裝在Authentication對象上,設(shè)置到SecurityContextHolder上,AuthenticationManager常用的實(shí)現(xiàn)是ProviderManager,你也可以對其做自定義實(shí)現(xiàn)。
到此這篇關(guān)于SpringSecurity多表多端賬戶登錄的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringSecurity多表多端賬戶登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot+SpringSecurity處理Ajax登錄請求問題(推薦)
- 解析SpringSecurity自定義登錄驗(yàn)證成功與失敗的結(jié)果處理問題
- SpringSecurity動(dòng)態(tài)加載用戶角色權(quán)限實(shí)現(xiàn)登錄及鑒權(quán)功能
- 解決SpringSecurity 一直登錄失敗的問題
- SpringBoot如何整合Springsecurity實(shí)現(xiàn)數(shù)據(jù)庫登錄及權(quán)限控制
- SpringSecurity6.x多種登錄方式配置小結(jié)
- SpringSecurity表單配置之登錄成功及頁面跳轉(zhuǎn)原理解析
- SpringSecurity集成第三方登錄過程詳解(最新推薦)
- Spring?Security重寫AuthenticationManager實(shí)現(xiàn)賬號密碼登錄或者手機(jī)號碼登錄
相關(guān)文章
詳解JDK 5 Annotation 注解之@Target的用法介紹
這篇文章主要介紹了詳解JDK 5 Annotation 注解之@Target的用法介紹,需要的朋友可以參考下2016-02-02SpringBoot中的自定義FailureAnalyzer詳解
這篇文章主要介紹了SpringBoot中的自定義FailureAnalyzer詳解,FailureAnalyzer是一種很好的方式在啟動(dòng)時(shí)攔截異常并將其轉(zhuǎn)換為易讀的消息,并將其包含在FailureAnalysis中, Spring Boot為應(yīng)用程序上下文相關(guān)異常、JSR-303驗(yàn)證等提供了此類分析器,需要的朋友可以參考下2023-12-12IDEA 2021配置JavaWeb項(xiàng)目超詳細(xì)教程
本文通過圖文并茂的形式給大家介紹IDEA 2021配置JavaWeb項(xiàng)目的過程,內(nèi)容簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-08-08request如何獲取完整url(包括域名、端口、參數(shù))
這篇文章主要介紹了request如何獲取完整url(包括域名、端口、參數(shù))問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12httpclient模擬post請求json封裝表單數(shù)據(jù)的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猦ttpclient模擬post請求json封裝表單數(shù)據(jù)的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12解析Neatbeans(常見錯(cuò)誤) build-impl.xml:305: Compile failed
本篇文章是對Neatbeans(常見錯(cuò)誤) build-impl.xml:305: Compile failed的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-07-07