Encrypting User Data in a Points Mall: A Practical Retrofit Guide
- Implementation Strategy: Use AES encryption to store sensitive user fields while maintaining backward compatibility with existing data operations.
- Solution: Decouple encryption from application logic through a custom MyBatis TypeHandler, which automatically encrypts and decrypts specified fields without modifying application code.
- Phased Execution:
- Analyze fields requiring encryption
- Build an AES encryption utility
- Implement a MyBatis TypeHandler
- Create a data migration script for legacy data
3. Technical Implementation
3.1 Field Analysis
Based on the data structure, identify fields that require encryption:
- User name
- Phone number
- Email address
- ID number
3.2 AES Encryption Utility
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AesUtil {
private static final String ALGORITHM = "AES";
private static final String KEY = "1234567890abcdef"; // 16位密钥
public static String encrypt(String value) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decrypt(String encrypted) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decoded = Base64.getDecoder().decode(encrypted);
byte[] original = cipher.doFinal(decoded);
return new String(original);
}
}
3.3 Implementing MyBatis TypeHandler
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class EncryptTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
try {
ps.setString(i, AesUtil.encrypt(parameter));
} catch (Exception e) {
throw new SQLException("Encryption failed", e);
}
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
try {
return AesUtil.decrypt(rs.getString(columnName));
} catch (Exception e) {
return null;
}
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
try {
return AesUtil.decrypt(rs.getString(columnIndex));
} catch (Exception e) {
return null;
}
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
try {
return AesUtil.decrypt(cs.getString(columnIndex));
} catch (Exception e) {
return null;
}
}
}
Using this with a MyBatis Mapper:
<result property="phone" column="phone" typeHandler="xxx.EncryptTypeHandler" />
3.4 Data Migration Script
Create a migration script to encrypt legacy data.
Basic steps:
- Identify all existing data
- Encrypt using AesUtil
- Update the original data table
Example:
List<User> users = userDao.findAll();
for (User user : users) {
String encryptedPhone = AesUtil.encrypt(user.getPhone());
userDao.updatePhone(user.getId(), encryptedPhone);
}
4. Results
After completing the encryption retrofit, the system operated normally with no perceptible user experience impact, while improving data security and successfully meeting subsequent security management requirements.
Throughout the process, the critical factor was establishing clear standards, following the encryption protocol, and ensuring component compatibility. In production, you might consider selective encryption by field category for more granular control.