1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  package org.bremersee.security.core.userdetails;
18  
19  import java.nio.charset.StandardCharsets;
20  import java.security.MessageDigest;
21  import java.security.NoSuchAlgorithmException;
22  import java.util.Base64;
23  import lombok.AccessLevel;
24  import lombok.Getter;
25  import lombok.ToString;
26  import lombok.extern.slf4j.Slf4j;
27  import org.bremersee.exception.ServiceException;
28  import org.springframework.security.crypto.factory.PasswordEncoderFactories;
29  import org.springframework.security.crypto.password.PasswordEncoder;
30  import org.springframework.util.StringUtils;
31  
32  
33  
34  
35  
36  
37  @ToString
38  @Slf4j
39  public class LdaptivePasswordEncoder implements PasswordEncoder {
40  
41    
42  
43  
44  
45  
46    public static LdaptivePasswordEncoder plain() {
47      return new LdaptivePasswordEncoder("plain", null);
48    }
49  
50    
51  
52  
53  
54  
55    public static LdaptivePasswordEncoder plainWithNoLabel() {
56      return new LdaptivePasswordEncoder(null, null);
57    }
58  
59    
60  
61  
62    protected static final PasswordEncoder delegate = PasswordEncoderFactories.createDelegatingPasswordEncoder();
63  
64    @Getter(value = AccessLevel.PROTECTED)
65    private final String label;
66  
67    @Getter(value = AccessLevel.PROTECTED)
68    private final String algorithm;
69  
70    
71  
72  
73    public LdaptivePasswordEncoder() {
74      this("SHA", "SHA");
75    }
76  
77    
78  
79  
80  
81  
82  
83    public LdaptivePasswordEncoder(String label, String algorithm) {
84      this.label = label;
85      this.algorithm = algorithm;
86    }
87  
88    @Override
89    public String encode(CharSequence rawPassword) {
90      String raw = rawPassword != null ? rawPassword.toString() : "";
91      StringBuilder sb = new StringBuilder();
92      if (StringUtils.hasText(getLabel())) {
93        sb.append('{').append(getLabel()).append('}');
94      }
95      if (!StringUtils.hasText(getAlgorithm()) || "plain".equalsIgnoreCase(getAlgorithm())) {
96        sb.append(rawPassword);
97      } else {
98        sb.append(encrypt(raw));
99      }
100     return sb.toString();
101   }
102 
103   private String encrypt(String raw) {
104     try {
105       final MessageDigest md = MessageDigest.getInstance(getAlgorithm());
106       md.update(raw.getBytes(StandardCharsets.UTF_8));
107       byte[] hash = md.digest();
108       return new String(Base64.getEncoder().encode(hash), StandardCharsets.UTF_8);
109 
110     } catch (NoSuchAlgorithmException e) {
111       throw ServiceException.internalServerError("Algorithm '" + getAlgorithm() + "' was not found.", e);
112     }
113   }
114 
115   @Override
116   public boolean matches(CharSequence rawPassword, String encodedPassword) {
117     String raw = rawPassword != null ? rawPassword.toString() : "";
118     String enc = encodedPassword != null ? encodedPassword : "";
119     int index = enc.indexOf('}');
120     if (enc.startsWith("{") && index > 0) {
121       String foundLabel = enc.substring(1, index);
122       if ("plain".equalsIgnoreCase(foundLabel)) {
123         return enc.equals(LdaptivePasswordEncoder.plain().encode(raw));
124       } else if (foundLabel.equals(getLabel())) {
125         return enc.equals(encode(raw));
126       } else {
127         return delegate.matches(raw, enc);
128       }
129     } else if (!StringUtils.hasText(getLabel())) {
130       return StringUtils.hasText(getAlgorithm())
131           ? enc.equals(encode(raw))
132           : enc.equals(LdaptivePasswordEncoder.plainWithNoLabel().encode(raw));
133     }
134     return false;
135   }
136 
137 }