Έλεγχος ταυτότητας δύο παραγόντων με Spring Security

0
Έλεγχος ταυτότητας δύο παραγόντων με Spring Security

Σε αυτό το άρθρο εκμάθησης ελατηρίου ασφάλειας, θα εξετάσουμε τον έλεγχο ταυτότητας δύο παραγόντων με την ασφάλεια Spring. Θα χρησιμοποιήσουμε το soft token με το Spring Security.

Έλεγχος ταυτότητας δύο παραγόντων με Spring Security

Είναι πάντα καλή πρακτική να προσθέτετε κάποιο επιπλέον επίπεδο ασφάλειας για την εφαρμογή σας, ειδικά με κάθε μεταβαλλόμενη δυναμική ασφάλειας. Για ορισμένες εφαρμογές, απαιτείται έλεγχος ταυτότητας δύο παραγόντων. Αν και δεν υπάρχει ενσωματωμένος έλεγχος ταυτότητας δύο παραγόντων με την ασφάλεια Spring, ωστόσο η ευέλικτη Αρχιτεκτονική Spring Security καθιστά πολύ εύκολη την προσθήκη αυτής της λειτουργικότητας για την εφαρμογή σας.

Απλώς, ο έλεγχος ταυτότητας δύο παραγόντων, που αναφέρεται ως επαλήθευση δύο βημάτων ή έλεγχος ταυτότητας δύο παραγόντων, είναι μια διαδικασία ασφαλείας στην οποία οι χρήστες παρέχουν δύο διαφορετικούς παράγοντες ελέγχου ταυτότητας για να επαληθεύσουν τους εαυτούς τους. Σε αυτό το άρθρο, θα αλλάξουμε τη διαδικασία σύνδεσης όπου ο πελάτης πρέπει να παρέχει ένα πρόσθετο διακριτικό ασφαλείας εκτός από τα διαπιστευτήρια σύνδεσης. Αυτό το πρόσθετο διακριτικό ασφαλείας θα είναι ένας κωδικός επαλήθευσης κωδικού πρόσβασης μίας χρήσης που βασίζεται στον Κωδικό μίας χρήσης που βασίζεται στον χρόνο TOTP algorithm.

Θα το χρησιμοποιησουμε Επαληθευτής Google ή παρόμοια εφαρμογή για τη δημιουργία αυτού του κωδικού επαλήθευσης.

1. Διαμόρφωση εφαρμογής – Maven

Θα χρησιμοποιήσουμε την υπάρχουσα εφαρμογή μας για να δημιουργήσουμε αυτήν την πρόσθετη δυνατότητα. Μπορείτε να κατεβάσετε τον κώδικα εργασίας από το δικό μας Αποθετήριο GitHub. Για να χρησιμοποιήσουμε τον Έλεγχο ταυτότητας Google για τη δημιουργία των κωδικών επαλήθευσης, χρειαζόμαστε να ακολουθήσουμε πρόσθετες αλλαγές στην εφαρμογή μας.

  1. Δημιουργήστε ένα μυστικό κλειδί κατά την εγγραφή χρήστη και αποθηκεύστε το με ένα προφίλ χρήστη.
  2. Δώστε το μυστικό κλειδί / QR-code στην καταχώρηση του πελάτη. Η εφαρμογή Έλεγχος ταυτότητας Google θα χρησιμοποιήσει αυτόν τον κωδικό QR για να δημιουργήσει τον Κωδικό μίας χρήσης βάσει χρόνου.
  3. Τέλος, πρέπει να αλλάξουμε τη διαδικασία σύνδεσης για να περάσουμε αυτό το πρόσθετο διακριτικό στον πάροχο ελέγχου ταυτότητας Spring Security και να επαληθεύσουμε το διακριτικό που εισήγαγε ο χρήστης χρησιμοποιώντας το μυστικό κλειδί.

Όπως ανέφερα προηγουμένως, η ασφάλεια Spring δεν διαθέτει μηχανισμό δημιουργίας και επαλήθευσης αυτού του διακριτικού ασφαλείας και θα χρησιμοποιήσουμε ως πρόσθετη βιβλιοθήκη για να μας βοηθήσει. Θα προσθέσουμε την ακόλουθη εξάρτησή μας pom.xml αρχείο.

<dependency>
   <groupId>dev.samstevens.totp</groupId>
   <artifactId>totp</artifactId>
   <version>1.7.1</version>
</dependency>

Υπάρχουν άλλες παρόμοιες βιβλιοθήκες για τη δημιουργία και την επαλήθευση αυτών των Κωδικών μίας χρήσης που βασίζονται στον χρόνο. Μπορείτε να χρησιμοποιήσετε οποιαδήποτε άλλη βιβλιοθήκη της επιλογής σας, καθώς η βασική ιδέα της ενσωμάτωσής της με την ασφάλεια Spring θα παραμείνει ίδια.

2. Αλλαγή οντότητας χρήστη

Η επόμενη αλλαγή είναι η οντότητα χρήστη. Πρέπει να αποθηκεύσουμε το μυστικό κλειδί στο προφίλ χρήστη κατά την εγγραφή. Αυτό το κλειδί είναι σημαντικό, καθώς θα το χρησιμοποιήσουμε κατά τη διαδικασία σύνδεσης για την επικύρωση του διακριτικού. Μπορούμε επίσης να εισαγάγουμε μια σημαία για να δείξουμε εάν ο έλεγχος ταυτότητας 2 παραγόντων είναι ενεργός για έναν πελάτη ή όχι. Έτσι φαίνεται η αλλαγμένη οντότητα χρήστη:

@Entity
@Table(name = "user")
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String firstName;
    private String lastName;
    @Column(unique = true)
    private String email;
    private String password;
    private String token;
    private boolean accountVerified;
    private int failedLoginAttempts;
    private boolean loginDisabled;
    private boolean mfaEnabled; // flag to indicate of mfa is active for profile
    private String secret; // secret store for the profile, this will be used during the login process.

    //getter and setters
}

3. Εγγραφή Πελατών

Το επόμενο βήμα για την ενεργοποίηση του ελέγχου ταυτότητας δύο παραγόντων με την ασφάλεια Spring είναι να αλλάξουμε τη ροή της διαδικασίας εγγραφής μας. Σε επιτυχή εγγραφή, θέλουμε να κάνουμε τα ακόλουθα πρόσθετα βήματα:

  1. Εμφάνιση μηνύματος επιτυχίας στον πελάτη.
  2. Δείξτε τον κωδικό QR στον πελάτη, ώστε να μπορεί να χρησιμοποιήσει την εφαρμογή για να σαρώσει και να αποθηκεύσει τον κωδικό QR για τη δημιουργία κώδικα.

3.1. MfaTokenManager

Για να αποθηκεύσουμε το μυστικό και να δείξουμε τον κωδικό QR στον πελάτη, θα εισαγάγουμε μια νέα υπηρεσία ως MfaTokenManagerτο οποίο θα μας βοηθήσει με τα ακόλουθα χαρακτηριστικά:

  1. Δημιουργήστε μυστικό κλειδί για έναν πελάτη κατά την εγγραφή.
  2. Δημιουργία κωδικού QR.
  3. Επαληθεύστε τον κωδικό που εισήγαγε ο πελάτης κατά τη διαδικασία σύνδεσης.

Έτσι φαίνεται η υπηρεσία μας:

public interface MFATokenManager {
    String generateSecretKey();
    String getQRCode(final String secret) throws QrGenerationException;
    boolean verifyTotp(final String code, final String secret);
}

package com.javadevjournal.core.security.mfa;

import dev.samstevens.totp.code.CodeVerifier;
import dev.samstevens.totp.code.HashingAlgorithm;
import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.secret.SecretGenerator;
import dev.samstevens.totp.util.Utils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service("mfaTokenManager")
public class DefaultMFATokenManager implements MFATokenManager {

    @Resource
    private SecretGenerator secretGenerator;

    @Resource
    private QrGenerator qrGenerator;

    @Resource
    private CodeVerifier codeVerifier;

    @Override
    public String generateSecretKey() {
        return secretGenerator.generate();
    }

    @Override
    public String getQRCode(String secret) throws QrGenerationException {
        QrData data = new QrData.Builder().label("MFA")
            .secret(secret)
            .issuer("Java Development Journal")
            .algorithm(HashingAlgorithm.SHA256)
            .digits(6)
            .period(30)
            .build();
        return Utils.getDataUriForImage(
            qrGenerator.generate(data),
            qrGenerator.getImageMimeType()
        );
    }

    @Override
    public boolean verifyTotp(String code, String secret) {
        return codeVerifier.isValidCode(secret, code);
    }
}

Το μεγαλύτερο μέρος του κώδικα είναι μέρος της βιβλιοθήκης που χρησιμοποιούμε για την εφαρμογή μας, ωστόσο, ο κώδικας είναι αυτονόητος. Κατά τη δημιουργία του κωδικού QR, χρειαζόμαστε το μυστικό κλειδί που θα δημιουργηθεί από την ίδια υπηρεσία κατά τη διαδικασία εγγραφής χρήστη.

3.2 Εξυπηρέτηση χρηστών

Μας UserService δημιουργεί την εγγραφή χρήστη στο σύστημα κατά τη διαδικασία εγγραφής. Θα κάνουμε επίσης κάποια τροποποίηση στο δικό μας UserService για την εκτέλεση των παρακάτω εργασιών:

  • Δημιουργήστε το μυστικό και αποθηκεύστε το με το προφίλ χρήστη.
  • Δημιουργήστε κωδικό QR κατά την επιτυχή εγγραφή χρήστη.

Εδώ είναι η αλλαγμένη έκδοση μας UserService.

@Override
public void register(UserData user) throws UserAlreadyExistException {
    if (checkIfUserExist(user.getEmail())) {
        throw new UserAlreadyExistException("User already exists for this email");
    }
    //some additional work
    userEntity.setSecret(mfaTokenManager.generateSecretKey()); //generating the secret and store with profile
    userRepository.save(userEntity);
}

Μια άλλη μέθοδος είναι να δημιουργήσετε τον κωδικό QR μόλις ολοκληρωθεί η εγγραφή.

Διαφημίσεις

@Override
public MfaTokenData mfaSetup(String email) throws UnkownIdentifierException, QrGenerationException {
    UserEntity user = userRepository.findByEmail(email);
    if (user == null) {
        // we will ignore in case account is not verified or account does not exists
        throw new UnkownIdentifierException("unable to find account or account is not active");
    }
    return new MfaTokenData(mfaTokenManager.getQRCode(user.getSecret()), user.getSecret());
}

Τέλος το δικό μας MfaTokenData Το bean περιέχει τον σύνδεσμο εικόνας κωδικού QR και το mfaCode. ο mfaCode είναι σημαντικό, καθώς θα δώσει στον πελάτη τη δυνατότητα να εισάγει χειροκίνητα το κλειδί στην εφαρμογή ελέγχου ταυτότητας σε περίπτωση που δεν μπορεί να σαρώσει τον κωδικό QR.

public class MfaTokenData implements Serializable {

    private String qrCode;
    private String mfaCode;
    //getter and setter
}

3.3. Ελεγκτής εγγραφής

Το άλλο μέρος είναι το RegistrationController. Σε αυτόν τον ελεγκτή, θα δημιουργήσουμε και θα δείξουμε τον κωδικό QR στον πελάτη κατά την επιτυχή εγγραφή.

@PostMapping
public String userRegistration(final @Valid UserData userData, final BindingResult bindingResult, final Model model) {

    try {
        userService.register(userData);
        MfaTokenData mfaData = userService.mfaSetup(userData.getEmail()); //setup QR code post registration
        model.addAttribute("qrCode", mfaData.getQrCode());
        model.addAttribute("qrCodeKey", mfaData.getMfaCode());
        model.addAttribute("qrCodeSetup", true);
    } catch (UserAlreadyExistException | QrGenerationException | UnkownIdentifierException e) {
        //error handing 
        return "account/register";
    }
    //handle message
    return "account/register";
}

3.4. Σελίδα εγγραφής

Το τελευταίο μέρος είναι να δείξουμε τον κωδικό QR στον πελάτη μας κατά την επιτυχή εγγραφή. Θα προσθέσουμε τον ακόλουθο πρόσθετο κώδικα στο πρότυπο Thymeleaf.

<img class="col-md-12" th:src="${qrCode}" />
<p th:text="${qrCodeKey}"></p>

Μπορείτε να ελέγξετε και να κατεβάσετε τον πλήρη πηγαίο κώδικα στο αποθετήριο GitHub.

3.5. Εμφάνιση του κωδικού QR

Για να ολοκληρώσουμε την εγγραφή για τον έλεγχο ταυτότητας δύο παραγόντων με την ασφάλεια Spring, πρέπει να εμφανίσουμε τον κωδικό στον πελάτη. Εάν εκτελέσετε την εφαρμογή και κάνετε νέα εγγραφή, θα δείτε την ακόλουθη οθόνη στην επιτυχή εγγραφή.

Έλεγχος ταυτότητας δύο παραγόντων με Spring Security

Μπορείτε να προσαρμόσετε την προβολή σύμφωνα με τις απαιτήσεις σας ή, αν θέλετε, μπορείτε να δώσετε την επιλογή στον πελάτη να ενεργοποιήσει τον έλεγχο ταυτότητας δύο παραγόντων με την ασφάλεια Spring από την ενότητα προφίλ. Εστιάζουμε μόνο στο κομμάτι της ένταξης.

Διαφημίσεις

4. Περάστε επιπλέον πεδία σύνδεσης με Spring Security

Το πρώτο μέρος της αίτησής μας έχει ολοκληρωθεί. Τώρα με τη διαδικασία σύνδεσης, θέλουμε ο χρήστης να παρέχει το TOTP κατά τη διαδικασία σύνδεσης για επικύρωση. Για να χειριστούμε αυτόν τον έλεγχο ταυτότητας δύο παραγόντων με ασφάλεια ελατηρίου, χρειαζόμαστε αυτό το πρόσθετο πεδίο. Έχω ήδη καλύψει μερικές επιλογές στο άλλο άρθρο μου σχετικά με τη μετάδοση ως πρόσθετο πεδίο με τη σελίδα σύνδεσης ασφαλείας Spring, αλλά θα χρησιμοποιήσουμε διαφορετική προσέγγιση για αυτό το άρθρο, καθώς αυτή η προσέγγιση είναι πιο κατάλληλη για τη δουλειά που κάνουμε.

Για αυτό το άρθρο, θα επεκτείνουμε το AuthenticationDetailsSource και WebAuthenticationDetails. Αν θυμάστε από το προηγούμενο άρθρο μου, το Spring Security χρησιμοποιήστε το UsernamePasswordAuthenticationFilter για σύνδεση βάσει φόρμας για να ρυθμίσετε και να μεταβιβάσετε τις παραμέτρους της φόρμας στον διαχειριστή ελέγχου ταυτότητας. Ακολουθεί ένα απόσπασμα από το UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);

Αν μπούμε στο setDetails() μέθοδος

Διαφημίσεις

protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
    authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}

Το AuthenticationDetailsSource δημιουργεί τις λεπτομέρειες ελέγχου ταυτότητας από το HttpServletRequest. Εφόσον εργαζόμαστε σε μια διαδικτυακή εφαρμογή, θα επεκτείνουμε το WebAuthenticationDetails και θα πει την ασφάλεια ελατηρίου να χρησιμοποιήσει αυτήν την κατηγορία πελατών. Αυτό είναι το πώς μας AuthenticationDetailsSource και WebAuthenticationDetails τα μαθήματα μοιάζουν με:

package com.javadevjournal.core.security.web.authentication;

import org.springframework.security.web.authentication.WebAuthenticationDetails;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {

    private String token;
    /**
     * Records the remote address and will also set the session Id if a session already
     * exists (it won't create one).
     *
     * @param request that the authentication request was received from
     */
    public CustomWebAuthenticationDetails(HttpServletRequest request) {
        super(request);
        this.token = request.getParameter("jdjCustomToken");
    }

    //override toString(), hashCode() and equals
}

@Component
public class CustomWebAuthenticationDetailsSource implements AuthenticationDetailsSource <HttpServletRequest, WebAuthenticationDetails> {

    @Override
    public CustomWebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new CustomWebAuthenticationDetails(context);
    }
}

Ο κώδικας είναι αρκετά αυτονόητος.

  1. Καλούμε το super to let WebAuthenticationDetails για να διαγωνιστεί απαιτείται εγκατάσταση.
  2. Ανακτήστε την παράμετρο αιτήματος πελάτη και ορίστε την στο token.
  3. Σε CustomWebAuthenticationDetailsSource τάξη, αρχικοποιούμε και επιστρέφουμε το έθιμο μας AuthenticationDetails τάξη

Το τελευταίο μέρος είναι να πει η Spring Security να χρησιμοποιήσει το προσαρμοσμένο μας CustomWebAuthenticationDetailsSource και όχι την προεπιλεγμένη υλοποίηση. Για να γίνει αυτό, θα διαμορφώσουμε το authenticationDetailsSource στην κλάση διαμόρφωσης ασφαλείας Spring.

@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private CustomWebAuthenticationDetailsSource authenticationDetailsSource;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/login", "/register", "/home")
            .permitAll()
            .antMatchers("/account/**").hasAnyAuthority("CUSTOMER", "ADMIN")
            .and()
            ......

            .formLogin()
            .authenticationDetailsSource(authenticationDetailsSource) //custom authenitcation source
            ....
    }
}

4.1. Προσθήκη επιπλέον παραμέτρου στη σελίδα σύνδεσης

Τέλος, το τελευταίο μέρος είναι να προσθέσετε την επιπλέον παράμετρο στη σελίδα σύνδεσης. Αυτό είναι ένα απλό πεδίο κειμένου που μπορεί να προστεθεί στο αρχείο login.html.

Διαφημίσεις

<div class="input-group mb-3">
   <input type="text" name="jdjCustomToken" class="form-control" placeholder="Token">
   <div class="input-group-append">
      <div class="input-group-text">
         <span class="fas fa-lock"></span>
      </div>
   </div>
</div>

Φροντίστε να ονομάσετε το όνομα του πεδίου όπως χρησιμοποιείται στο CustomWebAuthenticationDetails τάξη

5. Προσαρμοσμένος πάροχος ελέγχου ταυτότητας

Το τελευταίο μέρος για την ενεργοποίηση του ελέγχου ταυτότητας δύο παραγόντων με την ασφάλεια Spring είναι η επικύρωση του διακριτικού εισόδου χρήστη με το API και η απόρριψη κάθε προσπάθειας σύνδεσης με μη έγκυρο και ληγμένο διακριτικό. Υπάρχουν πολλές επιλογές για να γίνει αυτό, αλλά ο καλύτερος και προτεινόμενος τρόπος είναι να δημιουργήσετε ένα προσαρμοσμένο πάροχο ελέγχου ταυτότητας και επικυρώστε το διακριτικό στον πάροχο ελέγχου ταυτότητας.

Αφού θα κάνουμε τη δουλειά μας χρησιμοποιώντας το DAOAuthenticationProvider, θα επεκτείνουμε αυτόν τον πάροχο για να ολοκληρώσει το έργο μας. Θα χρησιμοποιήσουμε τον προεπιλεγμένο πάροχο για να κάνουμε το μεγαλύτερο μέρος της τυπικής εργασίας, αλλά θα το παρακάμψουμε additionalAuthenticationChecks() μέθοδος για την εκτέλεση του ελέγχου ταυτότητας δύο παραγόντων με την ασφάλεια Spring.

Διαφημίσεις

package com.javadevjournal.core.security.authentication;

@Component
public class CustomAuthenticationProvider extends DaoAuthenticationProvider {

    @Resource
    private MFATokenManager mfaTokenManager;

    @Autowired
    public CustomAuthenticationProvider(UserDetailsService userDetailsService) {
        super.setUserDetailsService(userDetailsService);
    }

    protected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication)
    throws AuthenticationException {

        super.additionalAuthenticationChecks(userDetails, authentication);
        //token check
        CustomWebAuthenticationDetails authenticationDetails = (CustomWebAuthenticationDetails) authentication.getDetails();
        CustomUser user = (CustomUser) userDetails;
        String mfaToken = authenticationDetails.getToken();
        if (!mfaTokenManager.verifyTotp(mfaToken, user.getSecret())) { //chekcing if the user token matching 
            throw new BadCredentialsException(messages.getMessage(
                "AbstractUserDetailsAuthenticationProvider.badCredentials",
                "Bad credentials"));
        }
    }
}

Και πάλι αυτό το μέρος είναι πολύ απλό:

Διαφημίσεις

  • Η προεπιλεγμένη DAOAuthenticationProvider θα εκτελέσει την τυπική εργασία.
  • Σε περίπτωση που ο χρήστης είναι έγκυρος, το additionalAuthenticationChecks() μέθοδος θα λάβει το διακριτικό ασφαλείας από το CustomWebAuthenticationDetails.
  • CustomeUser Η υπηρεσία θα περιέχει το μυστικό που δημιουργήθηκε και αποθηκεύτηκε κατά την εγγραφή του χρήστη.
  • Θα χρησιμοποιήσουμε το mfaTokenManager.verifyTotp() για να επαληθεύσετε το μυστικό με το διακριτικό που παρέχεται από τον πελάτη.
  • Στο διακριτικό είναι έγκυρο, η σύνδεση θα είναι επιτυχής. Στο άκυρο διακριτικό, ρίχνουμε το BadCredentialsException.

Πρέπει επίσης να διαμορφώσουμε τον προσαρμοσμένο πάροχο ελέγχου ταυτότητας για τον έλεγχο ταυτότητας δύο παραγόντων με την ασφάλεια Spring. Μπορούμε να το κάνουμε αυτό με την κλάση ρύθμισης παραμέτρων ασφαλείας.

@Override
protected void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(customAuthenticationProvider);
}

6. Εφαρμογή δοκιμής

Για να ελέγξετε εάν ο έλεγχος ταυτότητας δύο παραγόντων με την ασφάλεια Spring λειτουργεί όπως αναμένεται, εκτελέστε την εφαρμογή και μεταβείτε στη σελίδα σύνδεσης. Θα δείτε ένα πρόσθετο πεδίο για την παροχή του διακριτικού ασφαλείας κατά τη διαδικασία εγγραφής.

έλεγχος ταυτότητας δύο παραγόντων με ασφάλεια Spring
Διακριτικό ασφαλείας κατά τη σύνδεση

Εάν υποβάλετε με λάθος διακριτικό, θα λάβετε ένα μη έγκυρο σφάλμα σύνδεσης. Μόλις παρέχετε έγκυρα διαπιστευτήρια σύνδεσης μαζί με ένα έγκυρο διακριτικό, θα συνδεθείτε στο σύστημα.

Περίληψη

Σε αυτό το άρθρο, εξετάσαμε την επιλογή ενεργοποίησης του ελέγχου ταυτότητας δύο παραγόντων με την ασφάλεια Spring. Είδαμε πώς να διαμορφώνετε και να χρησιμοποιείτε τον αλγόριθμο TOTP One-time Password που βασίζεται στον χρόνο για την εφαρμογή σας. Στο τελευταίο μέρος, δημιουργήσαμε έναν προσαρμοσμένο πάροχο ελέγχου ταυτότητας για την ενσωμάτωση της διαδικασίας επικύρωσης σύνδεσης και διακριτικού. Ο πηγαίος κώδικας για αυτό το άρθρο είναι διαθέσιμος στην ιστοσελίδα μας Αποθετήριο GitHub.

Schreibe einen Kommentar