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

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

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

1. 前言

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

2. 注冊

2.1. 手機驗證碼注冊流程

以下是對流程圖的具體分析:

前端請求和手機號碼處理

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

驗證碼發(fā)送

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

用戶驗證和注冊提交

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

2.2. 代碼實現(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)造請求Body體
     * @param sender
     * @param receiver
     * @param templateId
     * @param templateParas
     * @param statusCallBack
     * @param signature | 簽名名稱,使用國內(nèi)短信通用模板時填寫
     * @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,請加載原生Base64類,并使用如下代碼
        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
        //如果JDK版本低于1.8,請加載三方庫提供Base64類,并使用如下代碼
        //String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
        //若passwordDigestBase64Str中包含換行符,請執(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 是一個用于通過華為云短信服務(wù)發(fā)送短信驗證碼的工具類。它通過構(gòu)建請求體和鑒權(quán)頭信息,將短信發(fā)送請求發(fā)送到華為短信服務(wù)接口。該類包含了短信發(fā)送的核心邏輯,包括生成X-WSSE頭用于請求認(rèn)證、構(gòu)造請求體以及處理HTTPS連接的相關(guān)邏輯。同時,工具類還包含了信任所有HTTPS證書的設(shè)置,以確保與華為云服務(wù)器的安全連接。 

3. 發(fā)送驗證碼函數(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ā)送注冊短信驗證碼:{}"
                : "發(fā)送登錄短信驗證碼:{}";
        sendSmsUtil.sendSms(templateId, phone, "[\"" + captcha + "\"]");
        log.info(message, captcha);
        redisUtils.set(redisKey, captcha, 300);
        return "發(fā)送短信成功";
    }

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

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

3. 登錄

3.1. 手機驗證碼登錄流程

以下是對流程圖的具體分析:

驗證碼發(fā)送流程

  • 流程依然從用戶請求驗證碼開始,后端接收手機號并生成驗證碼,通過短信服務(wù)平臺(如阿里云、華為云)發(fā)送驗證碼。

驗證碼驗證及登錄提交

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

用戶信息查詢及Token生成

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

3.2. 涉及到的Spring Security組件

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

1. AuthenticationManager

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

2. AuthenticationProvider

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

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

3. UserDetailsService

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

4. AuthenticationToken

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

5. SecurityConfigurerAdapter

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

6. 自定義過濾器

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

7. SecurityContextHolder

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

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

3.3.1.  編寫SmsAuthenticationFilter

public class SmsAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String PHONE_KEY = "phone";  // 手機號字段
    public static final String CAPTCHA_KEY = "captcha";  // 驗證碼字段
    private boolean postOnly = true;
    private final ObjectMapper objectMapper = new ObjectMapper();
    public SmsAuthenticationFilter() {
        super("/sms/login"); // 攔截短信驗證碼登錄請求
    }
    @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 {
            // 讀取請求體中的 JSON 數(shù)據(jù)并解析
            Map<String, String> requestBody = objectMapper.readValue(request.getInputStream(), Map.class);
            phone = requestBody.get(PHONE_KEY);  // 獲取手機號
            captcha = requestBody.get(CAPTCHA_KEY);  // 獲取驗證碼
        } 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)建驗證請求的 Token
        SmsAuthenticationToken authRequest = new SmsAuthenticationToken(phone, captcha);
        return this.getAuthenticationManager().authenticate(authRequest);
    }
    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }
}

上述代碼實現(xiàn)了一個 SmsAuthenticationFilter,用于處理短信驗證碼登錄請求。它繼承了 AbstractAuthenticationProcessingFilter,并在接收到 POST 請求時從請求體中解析手機號和驗證碼的 JSON 數(shù)據(jù),創(chuàng)建一個 SmsAuthenticationToken,然后通過 Spring Security 的認(rèn)證管理器進(jìn)行身份驗證。如果請求不是 POST 方法或解析 JSON 失敗,會拋出相應(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();  // 獲取手機號
        String captcha = (String) authentication.getCredentials();  // 獲取驗證碼
        if(!redisUtils.hasKey(REDIS_LOGIN_CAPTCHA_KEY + phone)){
            throw new BadCredentialsException("驗證碼已過期");
        }
        // 驗證碼是否正確
        String redisCaptcha = redisUtils.get(REDIS_LOGIN_CAPTCHA_KEY + phone).toString();
        if (redisCaptcha == null || !redisCaptcha.equals(captcha)) {
            throw new BadCredentialsException("驗證碼錯誤");
        }
        // 驗證用戶信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
        if (userDetails == null) {
            throw new BadCredentialsException("未找到對應(yīng)的用戶,請先注冊");
        }
        // 創(chuàng)建已認(rèn)證的Token
        return new SmsAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }
    @Override
    public boolean supports(Class<?> authentication) {
        return SmsAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

上述代碼實現(xiàn)了一個 SmsAuthenticationProvider,用于處理短信驗證碼登錄的身份驗證邏輯。它通過 UserDetailsService 加載用戶信息,并使用 RedisUtils 從 Redis 中獲取驗證碼進(jìn)行比對。如果驗證碼不存在或不匹配,會拋出 BadCredentialsException 異常。如果驗證碼正確且用戶存在,則生成已認(rèn)證的 SmsAuthenticationToken 并返回,完成用戶身份驗證。該類還定義了它支持的身份驗證類型為 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; // 用戶的手機號
        this.credentials = credentials; // 驗證碼
        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;
    }
}

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

3.3.4. 配置WebSecurityConfigurerAdapter

新增驗證碼過濾

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

定義短信驗證碼認(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;
    }

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

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

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

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

3.4. 效果測試

基于上述的手機驗證碼登錄代碼,我們來測試一下接口成果:

4. 結(jié)語

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

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

相關(guān)文章

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

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

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

    java后臺如何利用Pattern提取所需字符詳解

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

    springMVC中RestTemplate傳值接值方法

    今天小編就為大家分享一篇springMVC中RestTemplate傳值接值方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    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)不生效解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringBoot利用jackson格式化時間的三種方法

    SpringBoot利用jackson格式化時間的三種方法

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

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

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

    Java多線程+鎖機制實現(xiàn)簡單模擬搶票的項目實踐

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

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

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

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

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

    java分頁之假分頁實現(xiàn)簡單的分頁器

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

最新評論