জাভা (স্প্রিং) এপ্লিকেশনে এ পাসওয়ার্ড রিসেট ইমপ্লিমেন্টেশন

spring-security
password-reset
rest-api
পাসওয়ার্ড-রিসেট
স্প্রিং-সিকিউরিটি

(Sayem Hossain) #1

স্বাগতম!

এই আর্টিকেলে দেখাবো কিভাবে আপনি আপনার জাভা এপ্লিকেশনে খুবই সহজে পাসওয়ার্ড রিসেট ফিচারটি ইমপ্লিমেন্ট করতে পারেন।

৫টি স্টেপে আপনি খুবই সহজে আপনার এপ্লিকেশনে পাসওয়ার্ড রিসেট ফিচারটি ইমপ্লিমেন্ট করতে পারেন। হাজার হাজার কোড লিখে সময় নষ্ট করার চাইতে এটা একটা অপেক্ষাকৃত সহজ উপায়।

1. ভ্যালিডেশন টোকেনের জন্য মডেল তৈরী করুন
আপনি ValidationToken নামে একটি প্যারেন্ট ক্লাস তৈরী করতে পারেন যেটা পরবর্তীতে স্পেসিফিক ভালিডেশন টোকেন ক্লাসের জন্য এক্সটেন্ড করা যাবে।

    @MappedSuperclass
    public class ValidationToken extends BaseEntity {
        private String token;    
        private boolean tokenValid;    
        // getters and setters
     }

এখন AcValidationToken ক্লাসে ValidationToken এক্সটেন্ড করুন। এই ক্লাসটা আপনি অ্যাকাউন্ট ভ্যালিডেশন প্রসেসের জন্য ব্যাবহার করবেন।

@Entity
public class AcValidationToken extends ValidationToken{
    @OneToOne   
     private User user;    
     private String reason;
    // getters and setters
}

আমরা এখানে একটি @OneToOne রিলেশন তৈরী করেছি ইউজারের জন্য, যে ইউজারের পাসওয়ার্ড আমরা রিসেট করতে যাচ্ছি।

2. মডেলটির জন্য রিপোজিটরি এবং সার্ভিস

AcValidationTokenRepository.java

@Repository
public interface AcValidationTokenRepository extends JpaRepository<AcValidationToken, Long> {
    AcValidationToken findByToken(String token);
}

AcValidationTokenServiceImpl.java

@Service
public class AcValidationTokenServiceImpl implements AcValidationTokenService {
    private final AcValidationTokenRepository tokenRepo;
    @Autowired    
    public AcValidationTokenServiceImpl(AcValidationTokenRepository tokenRepo) {
            this.tokenRepo = tokenRepo;    
    }

    @Override    
    public AcValidationToken save(AcValidationToken acValidationToken) {
            return this.tokenRepo.save(acValidationToken);   
     }

    @Override    
    public AcValidationToken findOne(Long id) {
            return this.tokenRepo.findOne(id);    
    }

    @Override   
    public AcValidationToken findByToken(String token) {
            if (token == null) return null;        
            return this.tokenRepo.findByToken(token);    
    }

    @Override   
     public void delete(Long id) {
            this.tokenRepo.delete(id);    
    }

    @Override    
    public boolean isTokenValid(String token) {
        if (token == null || token.isEmpty()) return false;
        AcValidationToken acValidationToken = this.findByToken(token);
        return acValidationToken != null && acValidationToken.isTokenValid();    
    }
}

3. টোকেন জেনারেট করার জন্য একটি ইউটিলিটি ক্লাস তৈরী করুন

এই ক্লাশটি আমরা টোকেন জেনারেট করার জন্য ব্যাবহার করব।

SessionIdentifierGenerator.java

public class SessionIdentifierGenerator {
    private SecureRandom random = new SecureRandom();

    public String nextSessionId() {
            return new BigInteger(130, random).toString(32);    
    }

    public String nextPassword(){
        return new BigInteger(130, random).toString(32);
    }

}

4. সার্ভিস মেথড

তাহলে চলুন দুইটা সার্ভিস মেথড তৈরী করি। প্রথমটা টোকেন জেনারেট করে সেটা ইউজারকে ইমেইল করে পাঠানোর জন্য ব্যাবহৃত হবে।

এই মেথডটি একটি ইমেইল এবং ভ্যালিডেশন লিঙ্ক প্যারামিটার হিসেবে নেয় যেখানে ইউজারকে টোকেন/ভ্যালিডেশন ইউআরএল পাঠানো হবে। ভ্যালিডেশন লিংক পাঠানো হবে যাতে ইউজার ক্লিক করে তার ইমেইল ভ্যালিডেট করতে পারে। যদি ভ্যালিডেশন ইউআরএল null হয় তবে সে শুধুমাত্র টোকেন পাঠাবে ইমেইল। (useful when you are prompting user to enter the token to verify after sending the email)

@Override
public void requireAccountValidationByEmail(String email, String validationUrl) throws UserNotFoundException {
    if (email == null) throw new IllegalArgumentException("Email invalid!");    
    User user = this.findByEmail(email); 
    SessionIdentifierGenerator sessionIdentifierGenerator = new SessionIdentifierGenerator();    
    AcValidationToken acValidationToken = new AcValidationToken();    
    acValidationToken.setToken(sessionIdentifierGenerator.nextSessionId());    
    acValidationToken.setTokenValid(true);    
    acValidationToken.setUser(user);    // save acvalidationtoken    
    acValidationToken = this.acValidationTokenService.save(acValidationToken);    
    if (validationUrl == null) {
            this.mailService.sendEmail(user.getEmail(), "Verification token", "Your verification token is: " + acValidationToken.getToken());
        return;    
    }
    // build confirmation link    
    String confirmationLink = baseUrlApi.trim() + validationUrl + "?token=" + acValidationToken.getToken() + "&enabled=true";    
    // send link by email    
    this.mailService.sendEmail(user.getEmail(), "Please verify you account", "Please verify your email by clicking this link " + confirmationLink);
}

এই মেথডটি ব্যাবহৃত হবে ইউজারের পাসওয়ার্ড রিসেট করার জন্য।এটা দুইটা আর্গুমেন্ট নেয়। টোকেন এবং পাসওয়ার্ড যেটা নতুন পাসওয়ার্ড হিসেবে সেভ করা হবে। এই মেথডটি চেক করবে টোকেন ভ্যালিড কিনা, যদি ভ্যালিড হয় তবে পাসওয়ার্ড রিসেট করবে।

@Override
@Transactional
public User resetPassword(String token, String password) throws NullPasswordException, UserAlreadyExistsException, UserInvalidException {
    if (password.length() < 6)
        throw new IllegalArgumentException("Password length should be at least 6");    AcValidationToken 
    acValidationToken = this.acValidationTokenService.findByToken(token);    
    if(!acValidationTokenService.isTokenValid(token)) throw new UserInvalidException("Token invalid");    
    
    User user = acValidationToken.getUser();
    user.setPassword(PasswordUtil.encryptPassword(password, PasswordUtil.EncType.BCRYPT_ENCODER, null));
     acValidationToken.setTokenValid(false); 
    acValidationToken.setReason("Password Reset");
    user = this.save(user);
    acValidationToken.setUser(user);
    this.acValidationTokenService.save(acValidationToken);
    return user;
}

5. রাউট করফিগার করা
আচ্ছা, এখন আমাদের কন্ট্রোলারে এই চারটি রাউট করফিগার করতে হবে।

পাসওয়ার্ড রিসেট ইমেইল প্রেরন

// Password reset
@PostMapping("/resetPassword/verifyEmail")
private ResponseEntity verifyEmail(@RequestParam("email") String email) throws UserNotFoundException {
    this.userService.requireAccountValidationByEmail(email, null);   
     return ResponseEntity.status(HttpStatus.OK).build();
}

আপনি এই এন্ডপয়েন্টে যদি রিকোয়েস্ট করেন তবে এটা আপনার ইমেইল ঠিকানায় পাসওয়ার্ড রিসেট টোকেন/ঠিকানা প্রেরন করবে।

টোকেনের ভ্যালিডিটি যাচাই

@PostMapping("/checkTokenValidity")
private ResponseEntity checkTokenValidity(@RequestParam(value = "token") String token) {
    if (!this.acValidationTokenService.isTokenValid(token))
        return ResponseEntity.status(HttpStatus.FORBIDDEN).build();    
    return ResponseEntity.ok(token);
}

এই এন্ডপয়েন্টটা টোকেনের ভ্যালিডিটি যাচাই করার জন্য।পাসওয়ার্ড রিসেট করার পূর্বে এই এন্ডপয়েন্টে রিকোয়েস্ট করে ভ্যালিডিটি যাচাই করে নেবেন। কিন্তু চিন্তার কোন কারন নেই, resetPassword() মেথডের মাধ্যমে আমরা আরেকবার টোকেনের ভ্যালিডিটি যাচাই করে নিচ্ছি পাসওয়ার্ড রিসেটের পূর্বে। যদি পাসওয়ার্ড ইনভ্যালিড হয় তবে এটা একটা এক্সেপশন থ্রো করবে।

এখন পাসওয়ার্ড রিসেট করুন

@PostMapping("/resetPassword")
private ResponseEntity resetPassword(@RequestParam("token") String token,  @RequestParam("password") String password) throws Exception, NullPasswordException, UserAlreadyExistsException, UserInvalidException {
if (!this.acValidationTokenService.isTokenValid(token))
    return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); 
    User user = this.userService.resetPassword(token, password);
    return ResponseEntity.ok(user);
}

Wholla!! you are up!

এই প্রসেসটিতে যেহেতু ইমেইল ব্যাবহৃত হয়েছে , কাজেই স্প্রিং JavaMailSender দিয়ে কিভাবে ইমেইল পাঠাতে হয় সেটা এই আর্টিকেলে বর্ননা করা হয়েছে।

এই প্রোসেসে আপনার কোন সাজেশন কিংবা ইম্প্রুভমেন্ট থাকলে জানাতে পারেন। কোনরকম সিকিউরিটি হোল থাকলে অবশ্যই জানাবেন আশা করি।