最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Security自定義AuthenticationManager實(shí)現(xiàn)手機(jī)號(hào)/密碼雙認(rèn)證

 更新時(shí)間:2026年06月03日 09:33:45   作者:碼語(yǔ)智行  
這篇文章給大家介紹了Spring?Security自定義AuthenticationManager實(shí)現(xiàn)手機(jī)號(hào)/密碼雙認(rèn)證,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧

01整體思路 3 步走

  1. 1. 自定義認(rèn)證提供者 CustomAuthenticationProvider
    識(shí)別登錄方式,分發(fā)給對(duì)應(yīng) UserDetailsService
  2. 2. 雙 Service
    • UserDetailsService 驗(yàn)證賬號(hào)密碼
    • PhoneNumberUserService 驗(yàn)證手機(jī)號(hào)驗(yàn)證碼
  3. 3. 配置注入:把自定義提供者塞進(jìn) Spring Security,讓它乖乖聽(tīng)話。

02自定義認(rèn)證提供者

public class CustomAuthenticationProvider implements AuthenticationProvider {  
    private final UserDetailsService userDetailsService;    // 賬號(hào)密碼驗(yàn)證  
    private final PasswordEncoder passwordEncoder;       // 密碼加密器  
    private final PhoneNumberUserService phoneNumberUserService; // 手機(jī)號(hào)驗(yàn)證  
    public CustomAuthenticationProvider(UserDetailsService userDetailsService,  
                                        PasswordEncoder passwordEncoder,  
                                        PhoneNumberUserService phoneNumberUserService) {  
        this.userDetailsService = userDetailsService;  
        this.passwordEncoder = passwordEncoder;  
        this.phoneNumberUserService = phoneNumberUserService;  
    }  
    @Override  
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {  
        String principal = (String) authentication.getPrincipal();  // username:xxx 或 phone:xxx        
        String credentials = (String) authentication.getCredentials(); // 密碼或驗(yàn)證碼  
        UserDetails userDetails;  
        if (principal.startsWith("username:")) {         // 賬號(hào)密碼登錄  
            String username = principal.substring("username:".length());  
            userDetails = userDetailsService.loadUserByUsername(username);  
            if (!passwordEncoder.matches(credentials, userDetails.getPassword())) {  
                throw new BadCredentialsException("密碼錯(cuò)誤");  
            }  
        } else if (principal.startsWith("phone:")) {         // 手機(jī)號(hào)登錄  
            String phoneNumber = principal.substring("phone:".length());  
            userDetails = phoneNumberUserService.loadUserByPhoneNumber(phoneNumber);         // 這里驗(yàn)證碼校驗(yàn)可放在 service 內(nèi),也可前置過(guò)濾器                      
		else{  
			throw new BadCredentialsException("登錄方式不支持");  
		}          
		// 生成已認(rèn)證令牌  
		UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(userDetails, credentials, userDetails.getAuthorities());  
			result.setDetails(authentication.getDetails());  
		return result;  
        }  
	@Override public boolean supports (Class < ? > authentication){  
		return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);  
	}  
    }  
}

注解:

  • 1. 前綴識(shí)別:用 username: 和 phone: 做路由,避免寫(xiě)兩套接口。
  • 2. 職責(zé)分離:驗(yàn)證碼校驗(yàn)交給 PhoneNumberUserService,保持單一職責(zé)。
  • 3. 線程安全:所有依賴通過(guò)構(gòu)造器注入,無(wú)共享可變狀態(tài),天然并發(fā)友好。

03雙 Service 實(shí)現(xiàn)

UserDetailsService(賬號(hào)密碼版)

@Service  
@RequiredArgsConstructor  
public class UserDetailsServiceImpl implements UserDetailsService {  
    private final UserMapper userMapper;  
    private final MenuMapper menuMapper;  
    @Override  
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {  
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserName, username));  
        if (user == null) throw new UsernameNotFoundException("用戶不存在");  
        List<String> perms = menuMapper.selectPermsByUserId(user.getId());  
        perms.add(user.getRoles()); // 合并角色         
		return new LoginUser(user, perms);  
    }  
}

PhoneNumberUserService(手機(jī)號(hào)驗(yàn)證碼版)

@Service  
@RequiredArgsConstructor  
public class PhoneNumberUserService {  
    private final UserMapper userMapper;  
    private final MenuMapper menuMapper;  
    private final RedisTemplate<String, String> redisTemplate; // 緩存驗(yàn)證碼          
    public UserDetails loadUserByPhoneNumber(String phoneNumber) {       // 1? 查庫(kù)         
User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhonenumber, phoneNumber));  
        if (user == null)  
            throw new RuntimeException("手機(jī)號(hào)未注冊(cè)");          // 2? 查權(quán)限         
List<String> perms = menuMapper.selectPermsByUserId(user.getId());  
        perms.add(user.getRoles());          // 3?驗(yàn)證碼校驗(yàn)示例(可前置過(guò)濾器)       
        //String codeInRedis = redisTemplate.opsForValue().get("SMS:" + phoneNumber);  
        return new LoginUser(user, perms);  
    }  
}

注解:

  • 1. LambdaQueryWrapper:MyBatis-Plus 寫(xiě)法,鏈?zhǔn)角逅?/li>
  • 2. 角色權(quán)限合并:把角色當(dāng)權(quán)限塞到同一集合,后續(xù)授權(quán)更絲滑。
  • 3. 驗(yàn)證碼解耦:校驗(yàn)邏輯可放在 Service,也可前置過(guò)濾器,靈活插拔。

04SecurityConfig:把自定義提供者塞進(jìn)去

@Configuration  
@EnableWebSecurity  
@RequiredArgsConstructor  
public class SecurityConfig {  
    private final AuthenticationConfiguration authenticationConfiguration;  
  //密碼加密器
    @Bean  
    public PasswordEncoder passwordEncoder() {  
        return new BCryptPasswordEncoder();  
    }  
    @Bean  
    public UserDetailsService userDetailsService() {  
        return new UserDetailsServiceImpl();  
    }  
    @Bean  
    public PhoneNumberUserService phoneNumberUserService() {  
        return new PhoneNumberUserService();  
    }  
    @Bean  
    public CustomAuthenticationProvider customAuthenticationProvider() {  
        return new CustomAuthenticationProvider(userDetailsService(), passwordEncoder(), phoneNumberUserService());  
    }  
    @Bean  
    public AuthenticationManager authenticationManager() throws Exception {       
    // 替換默認(rèn) AuthenticationManager
    return new ProviderManager(customAuthenticationProvider());  
    }  
}

注解:

  • 1. ProviderManager:Spring Security 的核心調(diào)度器,塞入我們的 Provider 就能接管認(rèn)證。
  • 2. 構(gòu)造器注入:Spring 推薦寫(xiě)法,避免循環(huán)依賴。
  • 3. 無(wú) @Autowired:全部顯式 Bean,方便單測(cè) Mock。

05登錄接口:一行代碼雙通道

@RestController  
@RequestMapping("/auth")  
@RequiredArgsConstructor  
public class AuthController {  
    private final AuthenticationManager authenticationManager;  
    private final RedisTemplate<String, Object> redisTemplate;  
    @PostMapping("/login")  
    public Result login(@RequestBody LoginDTO dto) {  
        String principal = dto.getLoginType() == 1 ? "username:" + dto.getUsername() : "phone:" + dto.getPhone();  
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal, dto.getCredential());  
        Authentication authenticate = authenticationManager.authenticate(token);  
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();  
        String jwt = JwtUtil.createJWT(loginUser.getUser().getId().toString());  
        redisTemplate.opsForValue().set("login:" + loginUser.getUser().getId(), loginUser);  
        return Result.OK("登錄成功", Map.of("token", jwt));  
    }  
}

注解:

  • 1. DTO 統(tǒng)一:前端傳 loginType=1 賬號(hào)密碼,2 手機(jī)號(hào)驗(yàn)證碼,后端零 if-else。
  • 2. JWT + Redis:無(wú)狀態(tài) Token + 在線用戶信息緩存,分布式登錄穩(wěn)穩(wěn)的。
  • 3. 異常透?jìng)?/strong>:認(rèn)證失敗直接拋異常,被全局異常處理器統(tǒng)一包裝,前端拿到統(tǒng)一格式。

測(cè)試

登錄方式請(qǐng)求體返回
賬號(hào)密碼{"loginType":1,"username":"yuqn","credential":"123456"}{"msg":"登錄成功","token":"eyJ..."}
手機(jī)驗(yàn)證碼{"loginType":2,"phone":"13800138000","credential":"8888"}同上

到此這篇關(guān)于Spring Security自定義AuthenticationManager實(shí)現(xiàn)手機(jī)號(hào)/密碼雙認(rèn)證的文章就介紹到這了,更多相關(guān)Spring Security自定義AuthenticationManager內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java使用swt顯示圖片示例分享

    java使用swt顯示圖片示例分享

    這篇文章主要介紹了java使用swt顯示圖片示例,修改后就可變?yōu)閳D片瀏覽器,需要的朋友可以參考下
    2014-02-02
  • mybatis使用雙層<foreach>循環(huán)嵌套方式

    mybatis使用雙層<foreach>循環(huán)嵌套方式

    在Mybatis中使用雙層循環(huán)嵌套插入數(shù)據(jù)可以有效減少數(shù)據(jù)庫(kù)IO操作,提高性能,通過(guò)在mapper.xml中定義雙層集合來(lái)實(shí)現(xiàn),外層集合為實(shí)體類中的集合字段,內(nèi)層集合為集合字段中的另一個(gè)集合,通過(guò)這種方式,可以在業(yè)務(wù)代碼中減少循環(huán)插入的次數(shù)
    2024-09-09
  • mybatis?foreach?屬性及其三種使用情況詳解

    mybatis?foreach?屬性及其三種使用情況詳解

    這篇文章主要介紹了mybatis?foreach?屬性及其三種使用情況詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Mybatis流式查詢并實(shí)現(xiàn)將結(jié)果分批寫(xiě)入文件

    Mybatis流式查詢并實(shí)現(xiàn)將結(jié)果分批寫(xiě)入文件

    這篇文章主要介紹了Mybatis流式查詢并實(shí)現(xiàn)將結(jié)果分批寫(xiě)入文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • springboot引用kettle實(shí)現(xiàn)對(duì)接oracle數(shù)據(jù)的示例代碼

    springboot引用kettle實(shí)現(xiàn)對(duì)接oracle數(shù)據(jù)的示例代碼

    這篇文章主要介紹了springboot引用kettle實(shí)現(xiàn)對(duì)接oracle數(shù)據(jù),其實(shí)kettle集成到springboot里面沒(méi)有多少代碼,這個(gè)功能最主要的還是ktr文件的編寫(xiě),只要ktr編寫(xiě)好了,放到指定文件夾下,寫(xiě)個(gè)定時(shí)任務(wù)就完事了,需要的朋友可以參考下
    2022-12-12
  • springboot常見(jiàn)的一些相關(guān)注解總結(jié)

    springboot常見(jiàn)的一些相關(guān)注解總結(jié)

    在springboot中,摒棄了spring以往項(xiàng)目中大量繁瑣的配置,遵循約定大于配置的原則,通過(guò)自身默認(rèn)配置,極大的降低了項(xiàng)目搭建的復(fù)雜度,這篇文章主要介紹了springboot常見(jiàn)的一些相關(guān)注解,需要的朋友可以參考下
    2025-10-10
  • Maven中Junit測(cè)試@Test等注解無(wú)法識(shí)別的問(wèn)題及解決

    Maven中Junit測(cè)試@Test等注解無(wú)法識(shí)別的問(wèn)題及解決

    這篇文章主要介紹了Maven中Junit測(cè)試@Test等注解無(wú)法識(shí)別的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • 常用的java日期比較和日期計(jì)算方法小結(jié)

    常用的java日期比較和日期計(jì)算方法小結(jié)

    這篇文章主要為大家詳細(xì)總結(jié)了常用的java日期比較和日期計(jì)算方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • java并發(fā)編程專題(九)----(JUC)淺析CyclicBarrier

    java并發(fā)編程專題(九)----(JUC)淺析CyclicBarrier

    這篇文章主要介紹了java CyclicBarrier的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • CentOS8.2安裝Java 14.0.2的教程詳解

    CentOS8.2安裝Java 14.0.2的教程詳解

    這篇文章主要介紹了CentOS8.2安裝Java 14.0.2的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12

最新評(píng)論

沙洋县| 金沙县| 镇原县| 凌云县| 共和县| 江阴市| 巴里| 郓城县| 西和县| 墨江| 城固县| 芮城县| 延安市| 承德市| 祁连县| 修文县| 泉州市| 金塔县| 郑州市| 板桥市| 遂川县| 九龙城区| 凯里市| 阿拉善右旗| 牡丹江市| 临洮县| 修水县| 天全县| 乌海市| 扶沟县| 松原市| 广汉市| 桦川县| 威海市| 文安县| 会同县| 华池县| 阿拉善右旗| 江陵县| 滦平县| 乌拉特前旗|