亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Security添加二次認證的項目實踐

 更新時間:2023年12月11日 10:18:19   作者:格林希爾  
在用戶自動登錄后,可以通過對密碼進行二次校驗進而確保用戶的真實性,本文就來介紹一下Spring Security添加二次認證的項目實踐,具有一定的參考價值,感興趣的可以了解一下

一、 簡介

1 Spring Security概述

Spring Security是一個基于Spring框架的安全框架,用于為Java應(yīng)用程序提供身份驗證和授權(quán)服務(wù)。

2 二次認證的必要性

傳統(tǒng)的用戶名和密碼驗證方式存在被破解的風(fēng)險,因此在用戶登錄后需要進行二次認證,增強身份驗證的安全性。

二、 Spring Security實現(xiàn)二次認證的方式

1 使用已有二次認證服務(wù)

1.1 集成Google Authenticator

Google Authenticator是一款基于TOTP算法的開源軟件,用戶可以將其安裝在智能手機上,以便進行二次認證。Spring Security可以通過Google Authenticator進行二次認證。下面是一個簡單的示例。

1.1.1 在pom.xml中添加依賴

<dependency>
    <groupId>com.warrenstrange</groupId>
    <artifactId>googleauth</artifactId>
    <version>1.0.0</version>
</dependency>

1.1.2 實現(xiàn)Google Authenticator

@Service
public class GoogleAuthenticatorService {

    private final GoogleAuthenticator gAuth = new GoogleAuthenticator();

    /** 獲取密鑰 **/
    public String createSecret() {
        final GoogleAuthenticatorKey gak = gAuth.createCredentials();
        return gak.getKey();
    }

    /** 驗證身份 **/
    public boolean authorize(final String secret, final int otp) {
        return gAuth.authorize(secret, otp);
    }

    /** 獲取二維碼 **/
    public String getQR(final String secret, final String account) {
        final String format = "otpauth://totp/%s?secret=%s&issuer=%s";
        return String.format(format, account, secret, account);
    }

}

在上述代碼中創(chuàng)建了一個GoogleAuthenticatorService類,用于實現(xiàn)Google Authenticator的相關(guān)功能。其中,createSecret()方法用于創(chuàng)建一個新的密鑰,authorize()方法用于驗證一個TOTP是否有效,getQR()方法用于生成一個TOTP的二維碼。

1.1.3 在Spring Security中配置Google Authenticator

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private GoogleAuthenticatorService googleAuthenticatorService;

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                    .antMatchers("/user/**").authenticated()
                    .antMatchers("/admin/**").hasAnyRole("ADMIN")
                    .and()
                .formLogin()
                    .and()
                .addFilterBefore(buildGoogleAuthFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    private GoogleAuthFilter buildGoogleAuthFilter() throws Exception {
        final GoogleAuthFilter filter = new GoogleAuthFilter("/check-google-auth");
        filter.setSecretProvider((request, username) -> {
            final String secret = userService.getSecret(username); // 獲取用戶的密鑰
            return secret != null ? secret : "";
        });
        filter.setGoogleAuthenticator(googleAuthenticatorService.getGoogleAuthenticator());
        return filter;
    }

}

在上述代碼中創(chuàng)建了一個SecurityConfig類,并在其中定義了一個GoogleAuthFilter過濾器用于添加二次認證功能。在該過濾器中,我們通過setSecretProvider()方法獲取用戶的密鑰,然后通過setGoogleAuthenticator()方法設(shè)置Google Authenticator的實例。

1.2 集成Authy

Authy是一款基于TOTP算法的二次認證服務(wù)提供商,可以為Spring Security提供二次認證服務(wù)。下面是一個簡單的示例。

1.2.1 在pom.xml中添加依賴

<dependency>
    <groupId>com.authy</groupId>
    <artifactId>authy-client</artifactId>
    <version>1.2</version>
</dependency>

1.2.2 實現(xiàn)Authy

@Service
public class AuthyService {

    /** Authy API Key **/
    private final static String AUTHY_API_KEY = "your-authy-api-key";

    /** Authy客戶端 **/
    private final AuthyApiClient authyApiClient = new AuthyApiClient(AUTHY_API_KEY);

    /** 注冊用戶 **/
    public User createUser(final String email, final String countryCode, final String phone) throws Exception {
        final Users users = authyApiClient.getUsers();
        final User user = users.createUser(email, phone, countryCode);
        return user;
    }

    /** 發(fā)送驗證碼 **/
    public void sendVerification(final String userId, final String via) throws Exception {
        final Tokens tokens = authyApiClient.getTokens();
        tokens.requestSms(Integer.valueOf(userId), via);
    }

    /** 驗證驗證碼 **/
    public boolean verifyToken(final String userId, final int token) throws Exception {
        final Tokens tokens = authyApiClient.getTokens();
        final TokenVerification tokenVerification = tokens.verify(Integer.valueOf(userId), token);
        return tokenVerification.isOk();
    }

}

在上述代碼中創(chuàng)建了一個AuthyService類,用于與Authy客戶端進行交互。其中,createUser()方法用于注冊一個新用戶,sendVerification()方法用于向用戶發(fā)送驗證碼,verifyToken()方法用于驗證驗證碼是否有效。

1.2.3 在Spring Security中配置Authy

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthyService authyService;

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                    .antMatchers("/user/**").authenticated()
                    .antMatchers("/admin/**").hasAnyRole("ADMIN")
                    .and()
                .formLogin()
                    .and()
                .addFilterBefore(buildAuthyFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    private AuthyFilter buildAuthyFilter() throws Exception {
        final AuthyFilter filter = new AuthyFilter("/check-authy");
        filter.setAuthyService(authyService);
        return filter;
    }

}

在上述代碼中,創(chuàng)建了一個SecurityConfig類,并在其中定義了一個AuthyFilter過濾器用于添加二次認證功能。在該過濾器中,我們通過setAuthyService()方法設(shè)置AuthyService的實例。

2 自己開發(fā)二次認證模塊

2.1 實現(xiàn)二次認證功能

自己開發(fā)二次認證模塊意味著我們需要實現(xiàn)自己的TOTP算法。下面是一個簡單的例子。

@Service
public class TotpService {

    private final static int WINDOW_SIZE = 3;
    private final static int CODE_DIGITS = 6;
    private final static String HMAC_ALGORITHM = "HmacSHA1";

    private static final int[] DIGITS_POWER
            = // 0 1  2   3    4     5      6       7        8
            {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };

    /** 獲取密鑰 **/
    public String createSecret() {
        final SecureRandom random = new SecureRandom();
        final byte[] bytes = new byte[64];
        random.nextBytes(bytes);
        return Base32Utils.encode(bytes);
    }

    /** 生成驗證碼 **/
    public int generateCode(final String secret) throws Exception {
        final long timeIndex = System.currentTimeMillis() / 30000L; // 30秒
        final byte[] keyBytes = Base32Utils.decode(secret);
        final byte[] data = new byte[8];
        for (int i = 7; i >= 0; i--) {
            data[i] = (byte) (timeIndex & 0xff);
            timeIndex >>= 8;
        }
        final SecretKeySpec signingKey = new SecretKeySpec(keyBytes, HMAC_ALGORITHM);
        final Mac mac = Mac.getInstance(HMAC_ALGORITHM);
        mac.init(signingKey);
        final byte[] hmac = mac.doFinal(data);
        int offset = hmac[hmac.length - 1] & 0xf;
        int binCode = ((hmac[offset] & 0x7f) << 24)
                | ((hmac[offset + 1] & 0xff) << 16)
                | ((hmac[offset + 2] & 0xff) << 8)
                | (hmac[offset + 3] & 0xff);
        return binCode % DIGITS_POWER[CODE_DIGITS];
    }

    /** 驗證身份 **/
    public boolean authorize(final String secret, final int otp, final int tolerance) throws Exception {
        final long timeIndex = System.currentTimeMillis() / 30000L; // 30秒
        final byte[] keyBytes = Base32Utils.decode(secret);
        for (int i = -tolerance; i <= tolerance; i++) {
            final long ti = timeIndex + i;
            final byte[] data = new byte[8];
            for (int j = 7; j >= 0; j--) {
                data[j] = (byte) (ti & 0xff);
                ti >>= 8;
            }
            final SecretKeySpec signingKey = new SecretKeySpec(keyBytes, HMAC_ALGORITHM);
            final Mac mac = Mac.getInstance(HMAC_ALGORITHM);
            mac.init(signingKey);
            final byte[] hmac = mac.doFinal(data);
            int offset = hmac[hmac.length - 1] & 0xf;
            int binCode = ((hmac[offset] & 0x7f) << 24)
                    | ((hmac[offset + 1] & 0xff) << 16)
                    | ((hmac[offset + 2] & 0xff) << 8)
                    | (hmac[offset + 3] & 0xff);
            if (binCode % DIGITS_POWER[CODE_DIGITS] == otp) {
                return true;
            }
        }
        return false;
    }

}

在上述代碼中創(chuàng)建了一個TotpService類,用于實現(xiàn)二次認證功能。其中,createSecret()方法用于創(chuàng)建一個新的密鑰,generateCode()方法用于生成一個TOTP驗證碼,authorize()方法用于驗證TOTP是否有效。

2.2 在Spring Security中配置自己的二次認證模塊

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private TotpService totpService;

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                    .antMatchers("/user/**").authenticated()
                    .antMatchers("/admin/**").hasAnyRole("ADMIN")
                    .and()
                .formLogin()
                    .and()
                .addFilterBefore(buildTotpFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    private TotpFilter buildTotpFilter() throws Exception {
        final TotpFilter filter = new TotpFilter("/check-totp");
        filter.setTotpService(totpService);
        return filter;
    }

}

在上述代碼中創(chuàng)建了一個SecurityConfig類,并在其中定義了一個TotpFilter過濾器用于添加二次認證功能。在該過濾器中,我們通過setTotpService()方法設(shè)置TotpService的實例。

三、實現(xiàn)過程

1 集成Google Authenticator

1.1 安裝并配置Google Authenticator

首先需要在服務(wù)器端安裝和配置Google Authenticator。具體步驟可以按照以下操作進行:

  • 在Linux系統(tǒng)下,使用以下命令安裝Google Authenticator:

    sudo apt-get install libpam-google-authenticator -y
    
  • 創(chuàng)建Google Authenticator配置文件,使用以下命令:

    google-authenticator
    

    在進行配置時需要解答一系列問題,例如:

    • 你是否要對已映射的用戶強制啟用谷歌身份驗證?
    • 你是否要對所有新用戶強制啟用谷歌身份驗證?
    • 是否允許谷歌身份驗證用于SSH登錄?
    • 是否允許網(wǎng)頁版的Google Authenticator?
    • 是否在身份驗證時使用時間戳窗口?

    一般情況下可以選擇默認值。最后會得到一個二維碼和一個密鑰,需要記錄下來用于后面的配置。

  • 配置PAM服務(wù),編輯/etc/pam.d/sshd文件,添加以下命令:

    auth required pam_google_authenticator.so nullok
    

    這個命令將強制啟用Google Authenticator身份驗證并防止用戶跳過身份驗證。如果你只想讓一部分用戶使用Google Authenticator進行身份驗證,可以使用下面的命令:

    auth [success=done new_authtok_reqd=done default=ignore] pam_google_authenticator.so nullok
    

1.2 在Spring Security中配置Google Authenticator

在將Google Authenticator與Spring Security集成時需要完成以下步驟:

  • 添加依賴

    我們需要在項目中添加以下依賴項:

    <dependency>
        <groupId>com.warrenstrange</groupId>
        <artifactId>googleauth</artifactId>
        <version>0.8.1</version>
    </dependency>
    

    這是一個開源的Google Authenticator實現(xiàn)庫,我們將使用它來生成TOTP(基于時間的一次性密碼)。

  • 實現(xiàn)自定義的Spring Security過濾器

    我們需要自定義一個Spring Security過濾器,用于驗證用戶的TOTP是否合法。過濾器的核心代碼如下所示:

    public class TotpFilter extends OncePerRequestFilter {
    
        // 處理Totp認證請求的Handler
        private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler("/login?error");
    
        // 用于獲取用戶通過表單提交的Totp碼
        private String totpParameter = "totpCode";
    
        // 處理Totp認證請求的URL
        private String authenticationUrl = "/authenticate/totp";
    
        // 用于生成Totp驗證碼
        private GoogleAuthenticator gAuth;
    
        // 構(gòu)造函數(shù),傳入處理Totp認證請求的URL
        public TotpFilter(String authenticationUrl) {
            gAuth = new GoogleAuthenticator();
            setFilterProcessesUrl(authenticationUrl);
        }
    
        @Override
        public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
            try {
                // 獲取表單中提交的Totp碼
                String totpCode = request.getParameter(totpParameter);
                if (StringUtils.isBlank(totpCode)) {
                    throw new TotpException("Totp code is missing.");
                }
    
                // 獲取當(dāng)前用戶
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication == null || !(authentication.getPrincipal() instanceof User)) {
                    throw new TotpException("User not authenticated.");
                }
    
                // 生成Totp驗證碼
                User user = (User) authentication.getPrincipal();
                int code = Integer.parseInt(totpCode);
                boolean isCodeValid = gAuth.authorize(user.getTotpSecret(), code);
                if (isCodeValid) {
                    // 驗證通過,繼續(xù)執(zhí)行過濾鏈
                    chain.doFilter(request, response);
                } else {
                    // 驗證失敗,跳轉(zhuǎn)到錯誤頁面
                    failureHandler.onAuthenticationFailure(request, response, new BadCredentialsException("Invalid totp code."));
                }
            } catch (TotpException e) {
                // 驗證失敗,跳轉(zhuǎn)到錯誤頁面
                failureHandler.onAuthenticationFailure(request, response, new BadCredentialsException(e.getMessage()));
            }
        }
    }
    

    這個過濾器的核心邏輯是從請求中獲取用戶提交的TOTP碼,并使用Google Authenticator庫驗證TOTP碼是否合法。如果TOTP碼合法,則繼續(xù)執(zhí)行過濾器鏈,否則跳轉(zhuǎn)到錯誤頁面。

  • 在Spring Security配置中注冊自定義過濾器

    我們需要在Spring Security配置中注冊自定義過濾器:

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .addFilterBefore(new TotpFilter("/authenticate/totp"), UsernamePasswordAuthenticationFilter.class)
                    .authorizeRequests()
                    .antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                    .antMatchers("/admin/**").hasRole("ADMIN")
                    .anyRequest().authenticated()
                    .and()
                    .formLogin().defaultSuccessUrl("/home")
                    .and()
                    .logout().logoutSuccessUrl("/login?logout");
        }
    
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("user").password("password").roles("USER")
                    .and()
                    .withUser("admin").password("password").roles("ADMIN");
        }
    }
    

    在上述代碼中通過addFilterBefore()方法注冊了我們所實現(xiàn)的自定義Totp過濾器。這個過濾器將在Spring Security的UsernamePasswordAuthenticationFilter之前執(zhí)行,用于驗證用戶輸入的TOTP碼是否合法。

1.3 測試Google Authenticator的集成

在進行Google Authenticator集成測試時需要完成以下步驟:

  • 使用Google Authenticator手機應(yīng)用程序掃描生成二維碼的圖像。掃描后,你將獲得一個六位數(shù)字的二次認證Totp碼。

  • 在登錄時,除了用戶名和密碼外,還需要輸入TOTP碼。

  • 如果TOTP碼是有效的,將會自動跳轉(zhuǎn)到指定的首頁。

2 集成Authy

2.1 注冊并配置Authy

首先需要在Authy網(wǎng)站上注冊一個賬戶。然后需要創(chuàng)建一個新的應(yīng)用程序,獲取應(yīng)用程序的Api Key和App Id,這兩個值后面需要在代碼中使用。

2.2 在Spring Security中配置Authy

在沒有使用Google Authenticator身份驗證的情況下,Authy可以實現(xiàn)二次身份驗證。我們可以按照以下步驟來將Authy與Spring Security集成:

  • 添加Authy SDK

    我們需要添加Authy的java SDK作為項目的依賴:

    <dependency>
        <groupId>com.authy</groupId>
        <artifactId>authy-java</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  • 自定義Spring Security過濾器

    我們需要自定義一個Spring Security過濾器,用于驗證用戶的Authy TOTP是否合法。過濾器的核心代碼如下所示:

     /**
      * 過濾器,用于校驗Authy TOTP驗證碼是否合法。
      */
     public class AuthyTotpFilter extends OncePerRequestFilter {
    
         // 處理Totp認證請求的Handler
         private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler("/login?error");
    
         // 用于獲取用戶通過表單提交的Totp碼
         private String totpParameter = "totpCode";
    
         // 處理Authy認證請求的URL
         private String authenticationUrl = "/authenticate/authy";
    
         // Authy SDK的實例
         private AuthyApiClient authyApiClient = new AuthyApiClient(
                 System.getProperty("authy.api.key"), System.getProperty("authy.api.url"));
    
         /**
          * 構(gòu)造函數(shù),需要傳入處理Totp認證請求的URL。
          */
         public AuthyTotpFilter(String authenticationUrl) {
             setFilterProcessesUrl(authenticationUrl);
         }
    
         @Override
         protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
             try {
                 // 獲取表單中提交的Authy TOTP碼
                 String token = request.getParameter(totpParameter);
                 if (StringUtils.isBlank(token)) {
                     throw new AuthyException("Authy token is missing.");
                 }
    
                 // 獲取當(dāng)前用戶
                 Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                 if (authentication == null || !(authentication.getPrincipal() instanceof User)) {
                     throw new AuthyException("User not authenticated.");
                 }
    
                 // 獲取用戶綁定的Authy設(shè)備
                 User user = (User) authentication.getPrincipal();
                 String authyId = user.getAuthyId();
                 if (StringUtils.isBlank(authyId)) {
                     throw new AuthyException("Authy ID not found.");
                 }
    
                 // 驗證Authy TOTP碼
                 StartTokenVerificationResponse response1 = authyApiClient.getTokens().verify(authyId, token, true);
                 if (response1.isValid()) {
                     // 認證通過,繼續(xù)執(zhí)行過濾鏈
                     filterChain.doFilter(request, response);
                 } else {
                     // 認證失敗,跳轉(zhuǎn)到錯誤頁面
                     failureHandler.onAuthenticationFailure(request, response, new BadCredentialsException("Invalid authy token."));
                 }
             } catch (AuthyException e) {
                 // 認證失敗,跳轉(zhuǎn)到錯誤頁面
                 failureHandler.onAuthenticationFailure(request, response, new BadCredentialsException(e.getMessage()));
             }
         }
     }
    

    這個過濾器的核心邏輯是從表單中獲取用戶的Authy的TOTP碼,并通過AuthyApiClient類進行驗證。如果驗證通過,則繼續(xù)執(zhí)行過濾器鏈,否則跳轉(zhuǎn)到錯誤頁面。

  • 在Spring Security配置中注冊自定義過濾器

我們需要在Spring Security配置中注冊自定義的過濾器:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(new AuthyTotpFilter("/authenticate/authy"), UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                    .antMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                    .antMatchers("/admin/**").hasRole("ADMIN")
                    .anyRequest().authenticated()
                    .and()
                .formLogin().defaultSuccessUrl("/home")
                    .and()
                .logout().logoutSuccessUrl("/login?logout");
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("password").roles("USER")
                .and()
                .withUser("admin").password("password").roles("ADMIN");
    }
}

在上述代碼中通過addFilterBefore()方法注冊了我們所實現(xiàn)的自定義AuthyTotp過濾器。這個過濾器將在Spring Security的UsernamePasswordAuthenticationFilter之前執(zhí)行,用于驗證用戶輸入的Authy TOTP碼是否合法。

2.3 測試Authy的集成

在進行Authy集成測試時需要完成以下步驟:

  • 首先需要獲取用戶的Authy ID,這個ID是在用戶注冊時與Authy相關(guān)的。你可以通過短信、電話、電話回撥和Authy手機應(yīng)用等途徑獲取這個ID。

  • 在Authy手機應(yīng)用中可以看到一個數(shù)字碼,用于確認設(shè)備。輸入這個數(shù)字碼后,你可以得到一個六位數(shù)字的二次認證Totp碼。

  • 在登錄時除了用戶名和密碼外還需要輸入Authy TOTP碼。

  • 如果Authy TOTP碼是有效的,將會自動跳轉(zhuǎn)到指定的首頁。

3 自己開發(fā)二次認證模塊

當(dāng)系統(tǒng)需求與現(xiàn)有的二次認證方式不匹配時,我們可以自己開發(fā)二次認證模塊。現(xiàn)在,我們打算實現(xiàn)一個基于手機短信的二次認證模塊,并將其集成到一個基于Spring Security的Java Web應(yīng)用程序中。

3.1 實現(xiàn)基于手機短信的二次認證

我們的基于手機短信的二次認證模塊,需要完成以下任務(wù):

  • 接收用戶提交的手機號碼和用戶ID
  • 對用戶的手機號碼進行驗證,并向該手機號碼發(fā)送短信驗證碼
  • 用戶提交短信驗證碼,并進行驗證

我們使用Twilio API實現(xiàn)短信驗證碼的發(fā)送和驗證過程。Twilio是一個使用簡單的云通訊平臺,可以方便地發(fā)送短信和語音信息。我們可以使用Twilio提供的REST API來發(fā)送和驗證短信驗證碼,并在Java Web應(yīng)用程序中進行封裝。

首先在網(wǎng)站上注冊賬號獲取API Key和API Secret,用于進行短信驗證碼的發(fā)送和驗證。

其次在Java Web應(yīng)用程序中需要使用Twilio提供的Java庫,引入以下代碼:

import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;
import com.twilio.type.PhoneNumber;

public class TwilioSMSVerifier {
  private static final String TWILIO_ACCOUNT_SID = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
  private static final String TWILIO_AUTH_TOKEN = "your_auth_token";
  private static final String TWILIO_PHONE_NUMBER = "+1415XXXXXXX";

  public static void sendMessage(String toPhoneNumber, String message) {
    Twilio.init(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
    Message twilioMessage = Message.creator(
            new PhoneNumber(toPhoneNumber),
            new PhoneNumber(TWILIO_PHONE_NUMBER),
            message
    ).create();
  }

  public static boolean verifyCode(String toPhoneNumber, String code) {
    // 進行驗證碼驗證...
    return true;
  }
}

這里我們引入了Twilio庫,并定義了發(fā)送短信和驗證驗證碼的方法。請注意,這里的TWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKEN需要替換為您自己的Twilio賬戶信息。TWILIO_PHONE_NUMBER是您的Twilio賬戶綁定的電話號碼。

接下來實現(xiàn)sendMessage方法,調(diào)用Twilio API發(fā)送短信驗證碼:

public static void sendMessage(String toPhoneNumber, String message) {
    Twilio.init(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
    Message twilioMessage = Message.creator(
            new PhoneNumber(toPhoneNumber),
            new PhoneNumber(TWILIO_PHONE_NUMBER),
            message
    ).create();

    System.out.println("Twilio message SID: " + twilioMessage.getSid());
}

這里使用Twilio提供的Message對象進行短信驗證碼的發(fā)送。我們使用PhoneNumber對象來表示電話號碼,在Message構(gòu)造函數(shù)中指定了tofrom電話號碼,以及需發(fā)送的消息內(nèi)容message。發(fā)送后,我們通過System.out輸出Twilio消息的SID。

接下來實現(xiàn)verifyCode方法,驗證用戶提交的短信驗證碼:

public static boolean verifyCode(String toPhoneNumber, String code) {
    Twilio.init(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

    List<Message> messages = Message.reader()
            .setTo(new PhoneNumber(toPhoneNumber))
            .read();

    for (Message message : messages) {
        String body = message.getBody();
        if (body.contains(code)) {
            return true;
        }
    }

    return false;
}

這里使用Twilio提供的Message.reader對象,查詢該手機號碼的短信信息。我們遍歷查詢結(jié)果,判斷消息正文是否包含提交的驗證碼。如果存在,則返回true,否則返回false。

3.2 在Spring Security中配置自己開發(fā)的二次認證模塊

現(xiàn)在已經(jīng)完成了基于手機短信的二次認證模塊的開發(fā)。接下來,我們將其集成到基于Spring Security的Java Web應(yīng)用程序中。為此,我們需要在Spring Security中添加一個新的認證提供程序,并將我們的驗證碼驗證邏輯集成在其中。

我們可以通過繼承AbstractUserDetailsAuthenticationProvider,實現(xiàn)自己的認證方式。我們的認證提供程序,需要完成以下任務(wù):

  • 在進行認證時,獲取用戶提交的手機號碼和用戶ID,并向該手機號碼發(fā)送驗證碼
  • 接收用戶提交的短信驗證碼,并驗證其正確性

下面是我們自己開發(fā)的二次認證提供程序的Java代碼:

import java.util.ArrayList;
import java.util.List;

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class SMSAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
  private UserDetailsService userDetailsService;
  private int codeLength; // 驗證碼長度
  private int codeExpiration; // 驗證碼有效期

  public SMSAuthenticationProvider(UserDetailsService userDetailsService, int codeLength,
      int codeExpiration) {
    this.userDetailsService = userDetailsService;
    this.codeLength = codeLength;
    this.codeExpiration = codeExpiration;
  }

  @Override
  protected void additionalAuthenticationChecks(UserDetails userDetails,
      UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    String code = authentication.getCredentials().toString();
    SMSUserDetails smsUser = (SMSUserDetails) userDetails;
    if (!TwilioSMSVerifier.verifyCode(smsUser.getMobile(), code)) {
      throw new BadCredentialsException("Invalid verification code");
    }
  }

  @Override
  protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
      throws AuthenticationException {
    String mobile = (String) authentication.getDetails();
    UserDetails loadedUser;
    try {
      loadedUser = userDetailsService.loadUserByUsername(username);
    } catch (UsernameNotFoundException notFound) {
      throw new BadCredentialsException("Account not found");
    }
    return new SMSUserDetails(loadedUser.getUsername(), loadedUser.getPassword(), mobile,
        loadedUser.getAuthorities());
  }

  @Override
  public boolean supports(Class<?> authentication) {
    return authentication.equals(UsernamePasswordAuthenticationToken.class);
  }

  public class SMSUserDetails implements UserDetails {
    private final String username;
    private final String password;
    private final String mobile;
    private final List<GrantedAuthority> authorities;

    public SMSUserDetails(String username, String password, String mobile, List<GrantedAuthority> authorities) {
      this.username = username;
      this.password = password;
      this.mobile = mobile;
      this.authorities = new ArrayList<>(authorities);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
      return authorities;
    }

    @Override
    public String getPassword() {
      return password;
    }

    @Override
    public String getUsername() {
      return username;
    }

    public String getMobile() {
      return mobile;
    }

    @Override
    public boolean isAccountNonExpired() {
      return true;
    }

    @Override
    public boolean isAccountNonLocked() {
      return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
      return true;
    }

    @Override
    public boolean isEnabled() {
      return true;
    }
  }
}

首先定義了一個描述用戶詳情的內(nèi)部類SMSUserDetails,用于存儲用戶ID、密碼、手機號碼和權(quán)限等信息。在retrieveUser方法中,我們首先獲取用戶提交的手機號碼,然后調(diào)用userDetailsService獲取用戶信息。最后,我們返回SMSUserDetails對象,封裝用戶詳情。需要注意的是,SMSUserDetails必須實現(xiàn)UserDetails接口,以保證在之后的認證過程中能夠正確地獲取用戶信息。

additionalAuthenticationChecks中,我們使用TwilioSMSVerifier對象驗證用戶提交的驗證碼。在這里,我們獲取authentication.getCredentials(),即用戶提交的驗證碼,進行驗證。如果認證不通過,我們則拋出一個BadCredentialsException,表示認證失敗。

最后在supports方法中返回支持的認證類型。這里,我們只支持UsernamePasswordAuthenticationToken類型的認證。如果使用其他類型的認證對象,Spring Security將會拋出異常。

3.3 測試自己開發(fā)的二次認證模塊的集成

現(xiàn)在已經(jīng)完成了基于手機短信的二次認證模塊的開發(fā),并將其集成到了基于Spring Security的Java Web應(yīng)用程序中。接下來,我們將會對這個模塊進行測試,驗證其是否可以正常工作。

首先需要使用Twilio API向我們自己的手機號碼發(fā)送一條測試短信。我們可以使用以下代碼:

TwilioSMSVerifier.sendMessage("+8612345678901", "Your verification code is: 123456");

在發(fā)送短信后可以使用以下代碼,在Spring Security中進行認證:

@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(HttpServletRequest request) {
  String username = request.getParameter("username");
  String password = request.getParameter("password");
  String mobile = request.getParameter("mobile");
  String code = request.getParameter("code");

  UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
      username, password, Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
  authRequest.setDetails(mobile);

  Authentication authentication = authManager.authenticate(authRequest);
  SecurityContextHolder.getContext().setAuthentication(authentication);

  return "redirect:/home";
}

這里首先獲取用戶提交的認證信息,包括用戶名、密碼、手機號碼和短信驗證碼。接下來,我們創(chuàng)建一個UsernamePasswordAuthenticationToken對象,并設(shè)置其類型為ROLE_USER。我們將手機號碼設(shè)置為該對象的詳情信息。最后,我們使用authManager進行認證,并將認證結(jié)果保存在SecurityContextHolder中。

現(xiàn)在已經(jīng)完成了自己開發(fā)的二次認證模塊的集成測試。需要注意的是需要使用真實手機號碼進行測試,以確保短信驗證碼可以正常發(fā)送和驗證。如果您沒有真實手機號碼,可以注冊一個Twilio賬號,并使用Twilio提供的測試號碼進行測試。

四、小結(jié)回顧

在本篇文章中詳細介紹了二次認證的概念以及其優(yōu)缺點。二次認證可以通過多種方式實現(xiàn)包括基于硬件令牌、短信驗證碼和移動應(yīng)用等。不同的實現(xiàn)方式各有優(yōu)缺點,需要按照實際情況進行選擇。

其次使用Spring Security演示了如何在一個基于Web的應(yīng)用程序中添加二次認證。通過使用Spring Security可以輕松地將一個二次認證模塊與我們的應(yīng)用程序集成。在這個過程中了解如何配置Spring Security以及如何使用Spring Security提供的安全特性。

最后還討論了一些可能的問題以及解決方案。例如,提到了可能會遇到與移動設(shè)備兼容性和惡意攻擊等問題。為了解決這些問題提供了一些方法和建議,例如使用較新的移動設(shè)備和實施防范措施等。

希望大家能夠了解二次認證的主要概念以及如何使用Spring Security實現(xiàn)二次認證。同時也希望大家能夠了解二次認證可能遇到的挑戰(zhàn),并能夠選擇適當(dāng)?shù)慕鉀Q方案。

4.1 二次認證的優(yōu)缺點

二次認證可以提供額外的安全保障,增強用戶賬號的安全性。通過二次認證,用戶需要在登錄時進行額外的驗證,使得其他人無法直接登錄到該賬號中。尤其是在一些關(guān)鍵應(yīng)用場景中,例如銀行賬戶和電子商務(wù)平臺,二次認證可以提供更加可靠的賬號保護。

雖然二次認證可以提供更高的安全保障,但它也可能會影響系統(tǒng)的便捷性和易用性。尤其是在一些需要頻繁登錄的應(yīng)用中,例如互聯(lián)網(wǎng)社交應(yīng)用和游戲應(yīng)用,過多的驗證步驟可能會導(dǎo)致用戶反感。此外二次認證實現(xiàn)的復(fù)雜度也可能會造成不必要的負擔(dān)和成本。

因此在實踐中需要根據(jù)實際情況進行選擇。我們可以在關(guān)鍵應(yīng)用場景中使用二次認證,同時在其他應(yīng)用場景中采用其他的驗證方式,以提供更好的用戶體驗和系統(tǒng)安全性的平衡。

4.2 Spring Security添加二次認證的效果

通過使用Spring Security可以輕松地將二次認證模塊集成到我們的應(yīng)用程序中。可以使用Spring Security提供的二次認證過濾器,并按照要求配置我們的認證方式。在進行認證時可以使用Spring Security提供的許多安全特性,以保護我們的應(yīng)用程序不受惡意攻擊。

同時通過Spring Security還可以輕松地實現(xiàn)其他的安全特性,例如用戶角色控制、會話管理和密碼加密等。這樣,我們可以構(gòu)建一個更加健壯和安全的應(yīng)用程序,并幫助用戶獲得更好的體驗和信任感。

4.3 可能的問題及解決方案

在實踐中能會遇到一些問題如移動設(shè)備兼容性、短信驗證碼實現(xiàn)和惡意攻擊等。為了解決這些問題,我們可以采取一些措施例如:

  • 選擇較新版本設(shè)備。隨著移動設(shè)備技術(shù)的不斷更新,較新的設(shè)備可能會更加支持多種驗證方式,例如指紋識別和面部識別等。

  • 使用多個驗證碼驗證方式。我們可以使用多種驗證碼驗證方式,例如郵件驗證碼、電話驗證碼和圖形驗證碼等,以提高安全性。

  • 實施防范措施。我們可以通過使用Web防火墻和入侵檢測系統(tǒng)等安全技術(shù),來防范惡意攻擊和入侵行為。

總之通過有效地管理和保護我們的應(yīng)用程序可以提供更高質(zhì)量的用戶體驗和安全性。通過Spring Security,我們可以輕松地添加二次認證模塊,并讓我們的應(yīng)用程序更加可靠和安全。

到此這篇關(guān)于Spring Security添加二次認證的項目實踐的文章就介紹到這了,更多相關(guān)Spring Security二次認證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 在SpringBoot中實現(xiàn)一個訂單號生成系統(tǒng)的示例代碼

    在SpringBoot中實現(xiàn)一個訂單號生成系統(tǒng)的示例代碼

    在Spring Boot中設(shè)計一個訂單號生成系統(tǒng),主要考慮到生成的訂單號需要滿足的幾個要求:唯一性、可擴展性、以及可能的業(yè)務(wù)相關(guān)性,本文給大家介紹了幾種常見的解決方案及相應(yīng)的示例代碼,需要的朋友可以參考下
    2024-02-02
  • java Socket簡易聊天工具

    java Socket簡易聊天工具

    這篇文章主要為大家詳細介紹了java Socket簡易聊天工具,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Kotlin中?StateFlow?或?SharedFlow?的區(qū)別解析

    Kotlin中?StateFlow?或?SharedFlow?的區(qū)別解析

    Kotlin協(xié)程中的StateFlow和SharedFlow是響應(yīng)式數(shù)據(jù)流,分別用于UI狀態(tài)管理和事件通知,StateFlow有初始值,只保留最新值,適用于UI狀態(tài)管理;SharedFlow沒有初始值,可以配置緩存大小,適用于事件通知,感興趣的朋友一起看看吧
    2025-03-03
  • SpringSecurity微服務(wù)實戰(zhàn)之公共模塊詳解

    SpringSecurity微服務(wù)實戰(zhàn)之公共模塊詳解

    這篇文章主要為大家介紹了SpringSecurity微服務(wù)實戰(zhàn)之公共模塊詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • java 中enum的使用方法詳解

    java 中enum的使用方法詳解

    這篇文章主要介紹了java 中enum的使用方法詳解的相關(guān)資料,希望通過本文能幫助到大家,理解掌握java 中enum的使用方法,需要的朋友可以參考下
    2017-09-09
  • 詳解MyBatis中column屬性的總結(jié)

    詳解MyBatis中column屬性的總結(jié)

    在MyBatis的映射中有column這么一個屬性,我一直以為它映射的是數(shù)據(jù)庫表中的列名,但經(jīng)過學(xué)習(xí)發(fā)現(xiàn)他似乎映射的是SQL語句中的列名,或者說是查詢結(jié)果所得到的表的列名,這篇文章主要介紹了MyBatis中column屬性的總結(jié),需要的朋友可以參考下
    2022-09-09
  • 詳解如何用spring Restdocs創(chuàng)建API文檔

    詳解如何用spring Restdocs創(chuàng)建API文檔

    這篇文章將帶你了解如何用spring官方推薦的restdoc去生成api文檔。具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Java通過Fork/Join優(yōu)化并行計算

    Java通過Fork/Join優(yōu)化并行計算

    這篇文章主要為大家詳細介紹了Java通過Fork、Join來優(yōu)化并行計算,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • maven 配置多個倉庫的方法

    maven 配置多個倉庫的方法

    這篇文章主要介紹了maven 配置多個倉庫的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Java實現(xiàn)圖書借閱系統(tǒng)

    Java實現(xiàn)圖書借閱系統(tǒng)

    這篇文章主要為大家詳細介紹了Java實現(xiàn)圖書借閱系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評論