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

使用Spring Security集成手機(jī)驗(yàn)證碼登錄功能實(shí)現(xiàn)

 更新時(shí)間:2024年10月18日 15:30:57   作者:后端小肥腸  
本文詳細(xì)介紹了如何利用SpringSecurity來實(shí)現(xiàn)手機(jī)驗(yàn)證碼的注冊(cè)和登錄功能,在登錄過程中,同樣需通過驗(yàn)證碼進(jìn)行驗(yàn)證,文章還提供了相關(guān)的代碼實(shí)現(xiàn)

1. 前言

在當(dāng)今的互聯(lián)網(wǎng)應(yīng)用中,手機(jī)驗(yàn)證碼登錄已經(jīng)成為一種常見的用戶身份驗(yàn)證方式。相比傳統(tǒng)的用戶名密碼登錄方式,手機(jī)驗(yàn)證碼具有使用方便、安全性較高的特點(diǎn)。對(duì)于開發(fā)者來說,如何在現(xiàn)有的系統(tǒng)中快速集成這一功能,尤其是在Spring Security框架下,可能是一個(gè)具有挑戰(zhàn)性的任務(wù)。這篇文章將詳細(xì)介紹如何利用Spring Security來實(shí)現(xiàn)手機(jī)驗(yàn)證碼的注冊(cè)和登錄功能,幫助你在短時(shí)間內(nèi)搞定這一需求。

2. 注冊(cè)

2.1. 手機(jī)驗(yàn)證碼注冊(cè)流程

以下是對(duì)流程圖的具體分析:

前端請(qǐng)求和手機(jī)號(hào)碼處理

  • 用戶發(fā)起獲取驗(yàn)證碼的請(qǐng)求,后端接收手機(jī)號(hào)碼,生成隨機(jī)驗(yàn)證碼并存儲(chǔ)在Redis中,這部分流程是標(biāo)準(zhǔn)的短信驗(yàn)證流程。
  • 在存儲(chǔ)到Redis時(shí)明確了驗(yàn)證碼的有效時(shí)間(5分鐘)。

驗(yàn)證碼發(fā)送

  • 驗(yàn)證碼通過調(diào)用短信服務(wù)發(fā)送,這里需要自行選擇像阿里云、華為云等短信發(fā)送平臺(tái)。

用戶驗(yàn)證和注冊(cè)提交

  • 用戶收到驗(yàn)證碼后,在前端輸入驗(yàn)證碼并提交注冊(cè)請(qǐng)求。
  • 系統(tǒng)從Redis中獲取驗(yàn)證碼并與用戶輸入的驗(yàn)證碼進(jìn)行匹配。
  • 如果匹配成功,注冊(cè)流程繼續(xù)進(jìn)行并完成注冊(cè)。
  • 如果匹配失敗,提示用戶驗(yàn)證碼錯(cuò)誤。

2.2. 代碼實(shí)現(xiàn)(僅核心)

1. 匹配短信消息發(fā)送相關(guān)參數(shù)(以華為云為例)

2. 編寫短信發(fā)送工具類

@Component
public class SendSmsUtil {
    @Value("${huawei.sms.url}")
    private String url;
    @Value("${huawei.sms.appKey}")
    private String appKey;
    @Value("${huawei.sms.appSecret}")
    private String appSecret;
    @Value("${huawei.sms.sender}")
    private String sender;
    @Value("${huawei.sms.signature}")
    private String signature;
    /**
     * 無需修改,用于格式化鑒權(quán)頭域,給"X-WSSE"參數(shù)賦值
     */
    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
    /**
     * 無需修改,用于格式化鑒權(quán)頭域,給"Authorization"參數(shù)賦值
     */
    private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
    public void sendSms(String templateId,String receiver, String templateParas) throws IOException {
        String body = buildRequestBody(sender, receiver, templateId, templateParas, "", signature);
        String wsseHeader = buildWsseHeader(appKey, appSecret);
        HttpsURLConnection connection = null;
        OutputStreamWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            URL realUrl = new URL(url);
            connection = (HttpsURLConnection) realUrl.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("Authorization", "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"");
            connection.setRequestProperty("X-WSSE", wsseHeader);
            out = new OutputStreamWriter(connection.getOutputStream());
            out.write(body);
            out.flush();
            int status = connection.getResponseCode();
            InputStream is;
            if (status == 200) {
                is = connection.getInputStream();
            } else {
                is = connection.getErrorStream();
            }
            in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            System.out.println(result.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                out.close();
            }
            if (in != null) {
                in.close();
            }
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
    /**
     * 構(gòu)造請(qǐng)求Body體
     * @param sender
     * @param receiver
     * @param templateId
     * @param templateParas
     * @param statusCallBack
     * @param signature | 簽名名稱,使用國內(nèi)短信通用模板時(shí)填寫
     * @return
     */
    static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
                                   String statusCallBack, String signature) {
        if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
                || templateId.isEmpty()) {
            System.out.println("buildRequestBody(): sender, receiver or templateId is null.");
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();
        map.put("from", sender);
        map.put("to", receiver);
        map.put("templateId", templateId);
        if (null != templateParas && !templateParas.isEmpty()) {
            map.put("templateParas", templateParas);
        }
        if (null != statusCallBack && !statusCallBack.isEmpty()) {
            map.put("statusCallback", statusCallBack);
        }
        if (null != signature && !signature.isEmpty()) {
            map.put("signature", signature);
        }
        StringBuilder sb = new StringBuilder();
        String temp = "";
        for (String s : map.keySet()) {
            try {
                temp = URLEncoder.encode(map.get(s), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            sb.append(s).append("=").append(temp).append("&");
        }
        return sb.deleteCharAt(sb.length()-1).toString();
    }
    /**
     * 構(gòu)造X-WSSE參數(shù)值
     * @param appKey
     * @param appSecret
     * @return
     */
    static String buildWsseHeader(String appKey, String appSecret) {
        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
            System.out.println("buildWsseHeader(): appKey or appSecret is null.");
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String time = sdf.format(new Date()); //Created
        String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce
        MessageDigest md;
        byte[] passwordDigest = null;
        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update((nonce + time + appSecret).getBytes());
            passwordDigest = md.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        //如果JDK版本是1.8,請(qǐng)加載原生Base64類,并使用如下代碼
        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
        //如果JDK版本低于1.8,請(qǐng)加載三方庫提供Base64類,并使用如下代碼
        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
        //若passwordDigestBase64Str中包含換行符,請(qǐng)執(zhí)行如下代碼進(jìn)行修正
        //passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
    }
    /*** @throws Exception
     */
    static void trustAllHttpsCertificates() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[] {
                new X509TrustManager() {
                    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        return;
                    }
                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                        return;
                    }
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                }
        };
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, null);
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }
}

上述工具類 SendSmsUtil 是一個(gè)用于通過華為云短信服務(wù)發(fā)送短信驗(yàn)證碼的工具類。它通過構(gòu)建請(qǐng)求體和鑒權(quán)頭信息,將短信發(fā)送請(qǐng)求發(fā)送到華為短信服務(wù)接口。該類包含了短信發(fā)送的核心邏輯,包括生成X-WSSE頭用于請(qǐng)求認(rèn)證、構(gòu)造請(qǐng)求體以及處理HTTPS連接的相關(guān)邏輯。同時(shí),工具類還包含了信任所有HTTPS證書的設(shè)置,以確保與華為云服務(wù)器的安全連接。 

3. 發(fā)送驗(yàn)證碼函數(shù)方法

    public String sendSMS(SendSMSDTO sendSMSDTO) throws IOException {
        String phone = sendSMSDTO.getPhone();
        String captcha = generateCaptcha();
        String redisKey = sendSMSDTO.getCaptchaType().equals(0)
                ? REDIS_REGISTER_CAPTCHA_KEY + phone
                : REDIS_LOGIN_CAPTCHA_KEY + phone;
        String message = sendSMSDTO.getCaptchaType().equals(0)
                ? "發(fā)送注冊(cè)短信驗(yàn)證碼:{}"
                : "發(fā)送登錄短信驗(yàn)證碼:{}";
        sendSmsUtil.sendSms(templateId, phone, "[\"" + captcha + "\"]");
        log.info(message, captcha);
        redisUtils.set(redisKey, captcha, 300);
        return "發(fā)送短信成功";
    }

上述代碼實(shí)現(xiàn)了一個(gè)短信驗(yàn)證碼發(fā)送流程。首先,通過 generateCaptcha() 方法生成一個(gè)驗(yàn)證碼,并調(diào)用 sendSmsUtil.sendSms() 將驗(yàn)證碼發(fā)送到用戶的手機(jī)號(hào)碼。短信發(fā)送后,利用日志記錄了發(fā)送的驗(yàn)證碼。接著,驗(yàn)證碼被存儲(chǔ)在 Redis 中,鍵為手機(jī)號(hào)加上特定前綴,且設(shè)置了300秒的有效期。最后,返回一個(gè)短信發(fā)送成功的消息。

之后還有提交注冊(cè)時(shí)的驗(yàn)證,這個(gè)較為簡單,不做講解,本來發(fā)送驗(yàn)證碼函數(shù)我都不想寫的╮(╯▽╰)╭。 

3. 登錄

3.1. 手機(jī)驗(yàn)證碼登錄流程

以下是對(duì)流程圖的具體分析:

驗(yàn)證碼發(fā)送流程

  • 流程依然從用戶請(qǐng)求驗(yàn)證碼開始,后端接收手機(jī)號(hào)并生成驗(yàn)證碼,通過短信服務(wù)平臺(tái)(如阿里云、華為云)發(fā)送驗(yàn)證碼。

驗(yàn)證碼驗(yàn)證及登錄提交

  • 用戶收到驗(yàn)證碼后輸入并提交登錄請(qǐng)求,系統(tǒng)從Redis中獲取存儲(chǔ)的驗(yàn)證碼,與用戶輸入的驗(yàn)證碼進(jìn)行匹配。
  • 如果驗(yàn)證碼匹配失敗,系統(tǒng)會(huì)提示用戶驗(yàn)證碼錯(cuò)誤。

用戶信息查詢及Token生成

  • 當(dāng)驗(yàn)證碼匹配成功后,系統(tǒng)會(huì)進(jìn)一步查詢用戶信息,檢查是否存在有效的用戶賬號(hào)。
  • 如果用戶信息存在,系統(tǒng)生成Token完成登錄,確保用戶的身份驗(yàn)證。

3.2. 涉及到的Spring Security組件

要實(shí)現(xiàn)手機(jī)驗(yàn)證碼登錄,我們需要靈活使用Spring Security的認(rèn)證流程,并在其中引入自定義的驗(yàn)證碼驗(yàn)證邏輯。以下是關(guān)鍵的Spring Security組件及其在實(shí)現(xiàn)手機(jī)驗(yàn)證碼登錄時(shí)的作用:

1. AuthenticationManager

AuthenticationManager 是Spring Security認(rèn)證的核心組件,負(fù)責(zé)處理不同的認(rèn)證請(qǐng)求。我們可以自定義一個(gè) AuthenticationProvider 來處理手機(jī)驗(yàn)證碼的認(rèn)證邏輯,并將其注入到 AuthenticationManager 中。這樣當(dāng)用戶提交驗(yàn)證碼登錄請(qǐng)求時(shí), AuthenticationManager 會(huì)調(diào)用我們的自定義認(rèn)證提供者進(jìn)行驗(yàn)證。

2. AuthenticationProvider

AuthenticationProvider 是處理認(rèn)證邏輯的核心接口。為了支持手機(jī)驗(yàn)證碼登錄,我們需要實(shí)現(xiàn)一個(gè)自定義的 AuthenticationProvider,其中包含以下邏輯:

  • 接收包含手機(jī)號(hào)和驗(yàn)證碼的登錄請(qǐng)求。
  • 驗(yàn)證Redis中存儲(chǔ)的驗(yàn)證碼是否與用戶輸入的驗(yàn)證碼匹配。
  • 驗(yàn)證成功后,創(chuàng)建并返回 Authentication 對(duì)象,表示用戶已通過認(rèn)證。

3. UserDetailsService

UserDetailsService 是Spring Security中用于加載用戶信息的接口。我們可以通過實(shí)現(xiàn) UserDetailsService 來查詢和加載用戶信息,比如通過手機(jī)號(hào)查詢用戶的詳細(xì)信息(包括權(quán)限、角色等)。如果用戶信息存在且驗(yàn)證碼驗(yàn)證通過,系統(tǒng)將生成相應(yīng)的 UserDetails 對(duì)象,并將其與Spring Security的認(rèn)證上下文進(jìn)行關(guān)聯(lián)。

4. AuthenticationToken

在Spring Security中,AuthenticationToken 是認(rèn)證過程中傳遞用戶憑據(jù)的對(duì)象。我們需要自定義一個(gè) SmsAuthenticationToken,用于封裝手機(jī)號(hào)和驗(yàn)證碼,并傳遞給 AuthenticationProvider 進(jìn)行處理。這個(gè)Token類需要繼承自 AbstractAuthenticationToken,并包含手機(jī)號(hào)和驗(yàn)證碼信息。

5. SecurityConfigurerAdapter

SecurityConfigurerAdapter 是Spring Security配置的核心類,用于配置Spring Security的各種安全策略。為了集成手機(jī)驗(yàn)證碼登錄,我們需要擴(kuò)展 SecurityConfigurerAdapter 并在其中配置我們的 AuthenticationProvider 和自定義的登錄過濾器。

6. 自定義過濾器

為了支持手機(jī)驗(yàn)證碼登錄,我們可以自定義一個(gè)類似的過濾器 SmsAuthenticationFilter,在其中獲取用戶的手機(jī)號(hào)和驗(yàn)證碼,然后交給 AuthenticationManager 進(jìn)行處理。這個(gè)過濾器將攔截驗(yàn)證碼登錄請(qǐng)求,并調(diào)用 AuthenticationProvider 進(jìn)行驗(yàn)證。

7. SecurityContextHolder

SecurityContextHolder 是Spring Security中用于存儲(chǔ)當(dāng)前認(rèn)證信息的類。在用戶成功通過驗(yàn)證碼登錄認(rèn)證后,系統(tǒng)會(huì)將 Authentication 對(duì)象存儲(chǔ)到 SecurityContextHolder 中,表明當(dāng)前用戶已經(jīng)成功登錄。

3.3. 代碼實(shí)現(xiàn)(僅核心)

3.3.1.  編寫SmsAuthenticationFilter

public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String PHONE_KEY = "phone";  // 手機(jī)號(hào)字段
    public static final String CAPTCHA_KEY = "captcha";  // 驗(yàn)證碼字段
    private boolean postOnly = true;
    private final ObjectMapper objectMapper = new ObjectMapper();
    public SmsAuthenticationFilter() {
        super("/sms/login"); // 攔截短信驗(yàn)證碼登錄請(qǐng)求
    }
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        String phone;
        String captcha;
        try {
            // 讀取請(qǐng)求體中的 JSON 數(shù)據(jù)并解析
            Map<String, String> requestBody = objectMapper.readValue(request.getInputStream(), Map.class);
            phone = requestBody.get(PHONE_KEY);  // 獲取手機(jī)號(hào)
            captcha = requestBody.get(CAPTCHA_KEY);  // 獲取驗(yàn)證碼
        } catch (IOException e) {
            throw new AuthenticationServiceException("Failed to parse authentication request body", e);
        }
        if (phone == null) {
            phone = "";
        }
        if (captcha == null) {
            captcha = "";
        }
        phone = phone.trim();
        // 創(chuàng)建驗(yàn)證請(qǐng)求的 Token
        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(phone, captcha);
        return this.getAuthenticationManager().authenticate(authRequest);
    }
    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }
}

上述代碼實(shí)現(xiàn)了一個(gè) SmsAuthenticationFilter,用于處理短信驗(yàn)證碼登錄請(qǐng)求。它繼承了 AbstractAuthenticationProcessingFilter,并在接收到 POST 請(qǐng)求時(shí)從請(qǐng)求體中解析手機(jī)號(hào)和驗(yàn)證碼的 JSON 數(shù)據(jù),創(chuàng)建一個(gè) SmsAuthenticationToken,然后通過 Spring Security 的認(rèn)證管理器進(jìn)行身份驗(yàn)證。如果請(qǐng)求不是 POST 方法或解析 JSON 失敗,會(huì)拋出相應(yīng)的異常。 

3.3.2.  編寫SmsAuthenticationProvider

public class SmsAuthenticationProvider implements AuthenticationProvider {
    private final UserDetailsService userDetailsService;
    private final RedisUtils redisUtils;
    public SmsAuthenticationProvider(UserDetailsService userDetailsService, RedisUtils redisUtils) {
        this.userDetailsService = userDetailsService;
        this.redisUtils = redisUtils;
    }
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String phone = (String) authentication.getPrincipal();  // 獲取手機(jī)號(hào)
        String captcha = (String) authentication.getCredentials();  // 獲取驗(yàn)證碼
        if(!redisUtils.hasKey(REDIS_LOGIN_CAPTCHA_KEY + phone)){
            throw new BadCredentialsException("驗(yàn)證碼已過期");
        }
        // 驗(yàn)證碼是否正確
        String redisCaptcha = redisUtils.get(REDIS_LOGIN_CAPTCHA_KEY + phone).toString();
        if (redisCaptcha == null || !redisCaptcha.equals(captcha)) {
            throw new BadCredentialsException("驗(yàn)證碼錯(cuò)誤");
        }
        // 驗(yàn)證用戶信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
        if (userDetails == null) {
            throw new BadCredentialsException("未找到對(duì)應(yīng)的用戶,請(qǐng)先注冊(cè)");
        }
        // 創(chuàng)建已認(rèn)證的Token
        return new SmsAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }
    @Override
    public boolean supports(Class<?> authentication) {
        return SmsAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

上述代碼實(shí)現(xiàn)了一個(gè) SmsAuthenticationProvider,用于處理短信驗(yàn)證碼登錄的身份驗(yàn)證邏輯。它通過 UserDetailsService 加載用戶信息,并使用 RedisUtils 從 Redis 中獲取驗(yàn)證碼進(jìn)行比對(duì)。如果驗(yàn)證碼不存在或不匹配,會(huì)拋出 BadCredentialsException 異常。如果驗(yàn)證碼正確且用戶存在,則生成已認(rèn)證的 SmsAuthenticationToken 并返回,完成用戶身份驗(yàn)證。該類還定義了它支持的身份驗(yàn)證類型為 SmsAuthenticationToken。 

3.3.3.  編寫SmsAuthenticationToken

public class SmsAuthenticationToken extends AbstractAuthenticationToken {
    private final Object principal;
    private Object credentials;
    public SmsAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal; // 用戶的手機(jī)號(hào)
        this.credentials = credentials; // 驗(yàn)證碼
        setAuthenticated(false);
    }
    public SmsAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        setAuthenticated(true);
    }
    @Override
    public Object getCredentials() {
        return this.credentials;
    }
    @Override
    public Object getPrincipal() {
        return this.principal;
    }
    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
        this.credentials = null;
    }
}

上述代碼實(shí)現(xiàn)了一個(gè)自定義的 SmsAuthenticationToken,繼承自 AbstractAuthenticationToken,用于表示短信驗(yàn)證碼登錄的認(rèn)證信息。它包含用戶的手機(jī)號(hào) (principal) 和驗(yàn)證碼 (credentials) 兩個(gè)字段,并提供兩種構(gòu)造方法:一種用于未認(rèn)證的登錄請(qǐng)求,另一種用于已認(rèn)證的用戶信息。通過 getPrincipal() 獲取手機(jī)號(hào),getCredentials() 獲取驗(yàn)證碼,并且在調(diào)用 eraseCredentials() 時(shí)清除驗(yàn)證碼以增強(qiáng)安全性。 

3.3.4. 配置WebSecurityConfigurerAdapter

新增驗(yàn)證碼過濾

  // 添加短信驗(yàn)證碼過濾器
        http.addFilterBefore(smsAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

定義短信驗(yàn)證碼認(rèn)證過濾器,設(shè)置認(rèn)證管理器及認(rèn)證成功和失敗的處理器。

    @Bean
    public SmsAuthenticationFilter smsAuthenticationFilter() throws Exception {
        SmsAuthenticationFilter filter = new SmsAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());  // 設(shè)置認(rèn)證管理器
        filter.setAuthenticationSuccessHandler(securAuthenticationSuccessHandler);  // 設(shè)置成功處理器
        filter.setAuthenticationFailureHandler(securAuthenticationFailureHandler);  // 設(shè)置失敗處理器
        return filter;
    }

 定義短信驗(yàn)證碼認(rèn)證提供者,注入用戶詳情服務(wù)和 Redis 工具類,用于處理短信驗(yàn)證碼的認(rèn)證邏輯。

    @Bean
    public SmsAuthenticationProvider smsAuthenticationProvider() {
        return new SmsAuthenticationProvider(smeUserDetailsService,redisUtils);
    }

配置認(rèn)證管理器,添加短信驗(yàn)證碼、微信登錄以及用戶名密碼的認(rèn)證提供者。

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 添加短信驗(yàn)證碼認(rèn)證提供者
        auth.authenticationProvider(smsAuthenticationProvider());
        // 添加微信登錄認(rèn)證提供者
        auth.authenticationProvider(weChatAuthenticationProvider());
        // 添加用戶名密碼登錄認(rèn)證提供者
        auth.authenticationProvider(daoAuthenticationProvider());
    }

3.4. 效果測(cè)試

基于上述的手機(jī)驗(yàn)證碼登錄代碼,我們來測(cè)試一下接口成果:

4. 結(jié)語

通過以上步驟,我們成功實(shí)現(xiàn)了基于Spring Security的手機(jī)驗(yàn)證碼登錄功能。無論是注冊(cè)流程中的驗(yàn)證碼發(fā)送與驗(yàn)證,還是登錄時(shí)的身份認(rèn)證,Spring Security提供了足夠的靈活性,讓我們能夠快速集成這項(xiàng)功能。在實(shí)際應(yīng)用中,開發(fā)者可以根據(jù)自身需求進(jìn)一步優(yōu)化和擴(kuò)展,比如增加更復(fù)雜的驗(yàn)證邏輯或增強(qiáng)安全性。希望本教程能幫助你輕松解決驗(yàn)證碼登錄的問題,讓開發(fā)過程更加順暢高效。

到此這篇關(guān)于如何用Spring Security集成手機(jī)驗(yàn)證碼登錄的文章就介紹到這了,更多相關(guān)Spring Security驗(yàn)證碼登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot防止消息重復(fù)消費(fèi)的方法步驟

    SpringBoot防止消息重復(fù)消費(fèi)的方法步驟

    這段文章詳細(xì)介紹了消息隊(duì)列中消息重復(fù)消費(fèi)的問題及解決方案,重點(diǎn)講解了Redis唯一ID防重、數(shù)據(jù)庫唯一索引防重、業(yè)務(wù)狀態(tài)機(jī)防重和全局唯一約束防重四種方案,適合不同業(yè)務(wù)場(chǎng)景和并發(fā)量的需求,需要的朋友可以參考下
    2026-05-05
  • Spring中的singleton和prototype的實(shí)現(xiàn)

    Spring中的singleton和prototype的實(shí)現(xiàn)

    這篇文章主要介紹了Spring中的singleton和prototype的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • JavaWeb文件上傳下載實(shí)例講解(酷炫的文件上傳技術(shù))

    JavaWeb文件上傳下載實(shí)例講解(酷炫的文件上傳技術(shù))

    在Web應(yīng)用系統(tǒng)開發(fā)中,文件上傳功能是非常常用的功能,今天來主要講講JavaWeb中的文件上傳功能的相關(guān)技術(shù)實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧
    2016-11-11
  • springboot中項(xiàng)目啟動(dòng)時(shí)實(shí)現(xiàn)初始化方法加載參數(shù)

    springboot中項(xiàng)目啟動(dòng)時(shí)實(shí)現(xiàn)初始化方法加載參數(shù)

    這篇文章主要介紹了springboot中項(xiàng)目啟動(dòng)時(shí)實(shí)現(xiàn)初始化方法加載參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 在SpringBoot的yml配置文件中讀取Windows系統(tǒng)環(huán)境變量的方法

    在SpringBoot的yml配置文件中讀取Windows系統(tǒng)環(huán)境變量的方法

    本文介紹了在SpringBoot的application.yml配置文件中使用${VARIABLE_NAME}占位符引用Windows系統(tǒng)環(huán)境變量的方法,并提供了設(shè)置默認(rèn)值、在Java代碼中獲取環(huán)境變量值的建議,同時(shí),文章詳細(xì)講述了如何通過系統(tǒng)設(shè)置和命令行設(shè)置環(huán)境變量,需要的朋友可以參考下
    2026-03-03
  • 基于@RequestBody和@ResponseBody及Stringify()的作用說明

    基于@RequestBody和@ResponseBody及Stringify()的作用說明

    這篇文章主要介紹了基于@RequestBody和@ResponseBody及Stringify()的作用說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 基于SpringBoot+Redis的Session共享與單點(diǎn)登錄詳解

    基于SpringBoot+Redis的Session共享與單點(diǎn)登錄詳解

    這篇文章主要介紹了基于SpringBoot+Redis的Session共享與單點(diǎn)登錄,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Springcloud ribbon負(fù)載均衡算法實(shí)現(xiàn)

    Springcloud ribbon負(fù)載均衡算法實(shí)現(xiàn)

    這篇文章主要介紹了Springcloud ribbon負(fù)載均衡算法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Spring Boot優(yōu)雅使用RocketMQ的方法實(shí)例

    Spring Boot優(yōu)雅使用RocketMQ的方法實(shí)例

    這篇文章主要給大家介紹了關(guān)于Spring Boot優(yōu)雅使用RocketMQ的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 使用SpringBoot配置https(SSL證書)

    使用SpringBoot配置https(SSL證書)

    這篇文章主要介紹了使用SpringBoot配置https(SSL證書),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評(píng)論

肃宁县| 广丰县| 姚安县| 石狮市| 东阿县| 当涂县| 拉萨市| 邵东县| 巴林右旗| 定襄县| 仁布县| 仁布县| 寻乌县| 萨迦县| 祁阳县| 科技| 河南省| 若尔盖县| 呼伦贝尔市| 乡城县| 迁西县| 达日县| 澜沧| 山西省| 托里县| 荆州市| 讷河市| 东乌珠穆沁旗| 福鼎市| 奈曼旗| 特克斯县| 新源县| 洪湖市| 保德县| 玉屏| 会理县| 修水县| 措勤县| 安吉县| 从江县| 柞水县|