欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2024年10月18日 15:30:57   作者:后端小肥腸  
本文詳細(xì)介紹了如何利用SpringSecurity來(lái)實(shí)現(xiàn)手機(jī)驗(yàn)證碼的注冊(cè)和登錄功能,在登錄過(guò)程中,同樣需通過(guò)驗(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)成為一種常見(jiàn)的用戶(hù)身份驗(yàn)證方式。相比傳統(tǒng)的用戶(hù)名密碼登錄方式,手機(jī)驗(yàn)證碼具有使用方便、安全性較高的特點(diǎn)。對(duì)于開(kāi)發(fā)者來(lái)說(shuō),如何在現(xiàn)有的系統(tǒng)中快速集成這一功能,尤其是在Spring Security框架下,可能是一個(gè)具有挑戰(zhàn)性的任務(wù)。這篇文章將詳細(xì)介紹如何利用Spring Security來(lái)實(shí)現(xiàn)手機(jī)驗(yàn)證碼的注冊(cè)和登錄功能,幫助你在短時(shí)間內(nèi)搞定這一需求。

2. 注冊(cè)

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

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

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

  • 用戶(hù)發(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)證碼通過(guò)調(diào)用短信服務(wù)發(fā)送,這里需要自行選擇像阿里云、華為云等短信發(fā)送平臺(tái)。

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

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

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

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

2. 編寫(xiě)短信發(fā)送工具類(lèi)

@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;
    /**
     * 無(wú)需修改,用于格式化鑒權(quán)頭域,給"X-WSSE"參數(shù)賦值
     */
    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
    /**
     * 無(wú)需修改,用于格式化鑒權(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 | 簽名名稱(chēng),使用國(guó)內(nèi)短信通用模板時(shí)填寫(xiě)
     * @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類(lèi),并使用如下代碼
        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
        //如果JDK版本低于1.8,請(qǐng)加載三方庫(kù)提供Base64類(lèi),并使用如下代碼
        //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());
    }
}

上述工具類(lèi) SendSmsUtil 是一個(gè)用于通過(guò)華為云短信服務(wù)發(fā)送短信驗(yàn)證碼的工具類(lèi)。它通過(guò)構(gòu)建請(qǐng)求體和鑒權(quán)頭信息,將短信發(fā)送請(qǐng)求發(fā)送到華為短信服務(wù)接口。該類(lèi)包含了短信發(fā)送的核心邏輯,包括生成X-WSSE頭用于請(qǐng)求認(rèn)證、構(gòu)造請(qǐng)求體以及處理HTTPS連接的相關(guān)邏輯。同時(shí),工具類(lèi)還包含了信任所有HTTPS證書(shū)的設(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ā)送流程。首先,通過(guò) generateCaptcha() 方法生成一個(gè)驗(yàn)證碼,并調(diào)用 sendSmsUtil.sendSms() 將驗(yàn)證碼發(fā)送到用戶(hù)的手機(jī)號(hào)碼。短信發(fā)送后,利用日志記錄了發(fā)送的驗(yàn)證碼。接著,驗(yàn)證碼被存儲(chǔ)在 Redis 中,鍵為手機(jī)號(hào)加上特定前綴,且設(shè)置了300秒的有效期。最后,返回一個(gè)短信發(fā)送成功的消息。

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

3. 登錄

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

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

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

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

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

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

用戶(hù)信息查詢(xún)及Token生成

  • 當(dāng)驗(yàn)證碼匹配成功后,系統(tǒng)會(huì)進(jìn)一步查詢(xún)用戶(hù)信息,檢查是否存在有效的用戶(hù)賬號(hào)。
  • 如果用戶(hù)信息存在,系統(tǒng)生成Token完成登錄,確保用戶(hù)的身份驗(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 來(lái)處理手機(jī)驗(yàn)證碼的認(rèn)證邏輯,并將其注入到 AuthenticationManager 中。這樣當(dāng)用戶(hù)提交驗(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)證碼是否與用戶(hù)輸入的驗(yàn)證碼匹配。
  • 驗(yàn)證成功后,創(chuàng)建并返回 Authentication 對(duì)象,表示用戶(hù)已通過(guò)認(rèn)證。

3. UserDetailsService

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

4. AuthenticationToken

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

5. SecurityConfigurerAdapter

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

6. 自定義過(guò)濾器

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

7. SecurityContextHolder

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

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

3.3.1.  編寫(xiě)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,然后通過(guò) Spring Security 的認(rèn)證管理器進(jìn)行身份驗(yàn)證。如果請(qǐng)求不是 POST 方法或解析 JSON 失敗,會(huì)拋出相應(yīng)的異常。 

3.3.2.  編寫(xiě)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)證碼已過(guò)期");
        }
        // 驗(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)證用戶(hù)信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
        if (userDetails == null) {
            throw new BadCredentialsException("未找到對(duì)應(yīng)的用戶(hù),請(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)證邏輯。它通過(guò) UserDetailsService 加載用戶(hù)信息,并使用 RedisUtils 從 Redis 中獲取驗(yàn)證碼進(jìn)行比對(duì)。如果驗(yàn)證碼不存在或不匹配,會(huì)拋出 BadCredentialsException 異常。如果驗(yàn)證碼正確且用戶(hù)存在,則生成已認(rèn)證的 SmsAuthenticationToken 并返回,完成用戶(hù)身份驗(yàn)證。該類(lèi)還定義了它支持的身份驗(yàn)證類(lèi)型為 SmsAuthenticationToken。 

3.3.3.  編寫(xiě)SmsAuthenticationToken

public class SmsAuthenticationToken extends AbstractAuthenticationToken {
    private final Object principal;
    private Object credentials;
    public SmsAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal; // 用戶(hù)的手機(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)證信息。它包含用戶(hù)的手機(jī)號(hào) (principal) 和驗(yàn)證碼 (credentials) 兩個(gè)字段,并提供兩種構(gòu)造方法:一種用于未認(rèn)證的登錄請(qǐng)求,另一種用于已認(rèn)證的用戶(hù)信息。通過(guò) getPrincipal() 獲取手機(jī)號(hào),getCredentials() 獲取驗(yàn)證碼,并且在調(diào)用 eraseCredentials() 時(shí)清除驗(yàn)證碼以增強(qiáng)安全性。 

3.3.4. 配置WebSecurityConfigurerAdapter

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

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

定義短信驗(yàn)證碼認(rèn)證過(guò)濾器,設(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)證提供者,注入用戶(hù)詳情服務(wù)和 Redis 工具類(lèi),用于處理短信驗(yàn)證碼的認(rèn)證邏輯。

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

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

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

3.4. 效果測(cè)試

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

4. 結(jié)語(yǔ)

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

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

相關(guān)文章

  • MyBatis在mapper中傳遞參數(shù)的四種方式

    MyBatis在mapper中傳遞參數(shù)的四種方式

    MyBatis是一個(gè)持久層框架,它提供了一種將數(shù)據(jù)庫(kù)操作與Java對(duì)象之間的映射關(guān)系進(jìn)行配置的方式,在MyBatis中,Mapper是用于定義數(shù)據(jù)庫(kù)操作的接口,而參數(shù)傳遞則是通過(guò)Mapper接口的方法來(lái)實(shí)現(xiàn)的,本文給大家介紹了MyBatis在mapper中傳遞參數(shù)的四種方式,需要的朋友可以參考下
    2024-03-03
  • java后臺(tái)如何利用Pattern提取所需字符詳解

    java后臺(tái)如何利用Pattern提取所需字符詳解

    這篇文章主要給大家介紹了關(guān)于java后臺(tái)如何利用Pattern提取所需字符的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-01-01
  • springMVC中RestTemplate傳值接值方法

    springMVC中RestTemplate傳值接值方法

    今天小編就為大家分享一篇springMVC中RestTemplate傳值接值方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • 詳解springboot設(shè)置默認(rèn)參數(shù)Springboot.setDefaultProperties(map)不生效解決

    詳解springboot設(shè)置默認(rèn)參數(shù)Springboot.setDefaultProperties(map)不生效解決

    這篇文章主要介紹了詳解springboot設(shè)置默認(rèn)參數(shù)Springboot.setDefaultProperties(map)不生效解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringBoot利用jackson格式化時(shí)間的三種方法

    SpringBoot利用jackson格式化時(shí)間的三種方法

    日常開(kāi)發(fā)過(guò)程中經(jīng)常會(huì)使用json進(jìn)行數(shù)據(jù)的傳輸,這就涉及到了對(duì)象和json的相互轉(zhuǎn)化,常用的解決方案有:Jackson(推薦)、谷歌的Gson、阿里的Fastjson,這篇文章主要給大家介紹了關(guān)于SpringBoot如何利用jackson格式化時(shí)間的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • mybatis-plus實(shí)體類(lèi)中出現(xiàn)非數(shù)據(jù)庫(kù)映射字段解決辦法

    mybatis-plus實(shí)體類(lèi)中出現(xiàn)非數(shù)據(jù)庫(kù)映射字段解決辦法

    這篇文章主要介紹了mybatis-plus實(shí)體類(lèi)中出現(xiàn)非數(shù)據(jù)庫(kù)映射字段解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java多線(xiàn)程+鎖機(jī)制實(shí)現(xiàn)簡(jiǎn)單模擬搶票的項(xiàng)目實(shí)踐

    Java多線(xiàn)程+鎖機(jī)制實(shí)現(xiàn)簡(jiǎn)單模擬搶票的項(xiàng)目實(shí)踐

    鎖是一種同步機(jī)制,用于控制對(duì)共享資源的訪(fǎng)問(wèn),在線(xiàn)程獲取到鎖對(duì)象后,可以執(zhí)行搶票操作,本文主要介紹了Java多線(xiàn)程+鎖機(jī)制實(shí)現(xiàn)簡(jiǎn)單模擬搶票的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • Java 反射獲取類(lèi)詳細(xì)信息的常用方法總結(jié)

    Java 反射獲取類(lèi)詳細(xì)信息的常用方法總結(jié)

    Java 反射獲取類(lèi)詳細(xì)信息的常用方法總結(jié),需要的朋友可以參考一下
    2013-03-03
  • JAVA像SQL一樣對(duì)List對(duì)象集合進(jìn)行排序

    JAVA像SQL一樣對(duì)List對(duì)象集合進(jìn)行排序

    這篇文章主要介紹了JAVA像SQL一樣對(duì)List對(duì)象集合進(jìn)行排序的實(shí)現(xiàn)方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • java分頁(yè)之假分頁(yè)實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)器

    java分頁(yè)之假分頁(yè)實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)器

    這篇文章主要介紹了java分頁(yè)之假分頁(yè)實(shí)現(xiàn)簡(jiǎn)單的分頁(yè)器的相關(guān)資料,需要的朋友可以參考下
    2016-04-04

最新評(píng)論