java 中对自定义注解的说明请参见:
http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
http://www.cnblogs.com/peida/archive/2013/04/26/3038503.html
有这样一个场景,系统中可以出现敏感的数据,在打印日志的时候,我们并不希望打印出现,这样,我们使用自己定义注解,来解决这个问题。
定义需要脱敏的字段规则。
-
import java.lang.reflect.Array;
-
import java.lang.reflect.Field;
-
import java.lang.reflect.Method;
-
import java.util.Collection;
-
import java.util.HashSet;
-
import java.util.Iterator;
-
import java.util.Map;
-
import java.util.Map.Entry;
-
import java.util.Set;
-
import org.apache.commons.lang.ArrayUtils;
-
import org.apache.commons.lang.StringUtils;
-
import com.alibaba.fastjson.JSON;
-
import com.alibaba.fastjson.serializer.SerializerFeature;
-
import com.google.gson.Gson;
-
import com.ucf.platform.framework.core.annotation.SensitiveInfo;
-
import com.ucf.platform.framework.core.log.UcfLogger;
-
import com.ucf.platform.framework.core.log.UcfLoggerFactory;
-
/**
-
* @Title: SensitiveInfoUtils.java
-
* @Copyright: Copyright (c) 2011
-
* @Description: <br>
-
* 敏感信息屏蔽工具<br>
-
*/
-
public
final
class
SensitiveInfoUtils {
-
private
final
static
UcfLogger
logger
= UcfLoggerFactory.getLogger(SensitiveInfoUtils.class);
-
/**
-
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
-
*
-
* @param name
-
* @return
-
*/
-
public
static String
chineseName
(String fullName) {
-
if (StringUtils.isBlank(fullName)) {
-
return
"";
-
}
-
String
name
= StringUtils.left(fullName,
1);
-
return StringUtils.rightPad(name, StringUtils.length(fullName),
"*");
-
}
-
/**
-
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
-
*
-
* @param familyName
-
* @param givenName
-
* @return
-
*/
-
public
static String
chineseName
(String familyName, String givenName) {
-
if (StringUtils.isBlank(familyName) || StringUtils.isBlank(givenName)) {
-
return
"";
-
}
-
return chineseName(familyName + givenName);
-
}
-
/**
-
* [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
-
*
-
* @param id
-
* @return
-
*/
-
public
static String
idCardNum
(String id) {
-
if (StringUtils.isBlank(id)) {
-
return
"";
-
}
-
String
num
= StringUtils.right(id,
4);
-
return StringUtils.leftPad(num, StringUtils.length(id),
"*");
-
}
-
/**
-
* [固定电话] 后四位,其他隐藏<例子:****1234>
-
*
-
* @param num
-
* @return
-
*/
-
public
static String
fixedPhone
(String num) {
-
if (StringUtils.isBlank(num)) {
-
return
"";
-
}
-
return StringUtils.leftPad(StringUtils.right(num,
4), StringUtils.length(num),
"*");
-
}
-
/**
-
* [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
-
*
-
* @param num
-
* @return
-
*/
-
public
static String
mobilePhone
(String num) {
-
if (StringUtils.isBlank(num)) {
-
return
"";
-
}
-
return StringUtils.left(num,
3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num,
4), StringUtils.length(num),
"*"),
"***"));
-
}
-
/**
-
* [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
-
*
-
* @param address
-
* @param sensitiveSize
-
* 敏感信息长度
-
* @return
-
*/
-
public
static String
address
(String address, int sensitiveSize) {
-
if (StringUtils.isBlank(address)) {
-
return
"";
-
}
-
int
length
= StringUtils.length(address);
-
return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length,
"*");
-
}
-
/**
-
* [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
-
*
-
* @param email
-
* @return
-
*/
-
public
static String
email
(String email) {
-
if (StringUtils.isBlank(email)) {
-
return
"";
-
}
-
int
index
= StringUtils.indexOf(email,
"@");
-
if (index <=
1)
-
return email;
-
else
-
return StringUtils.rightPad(StringUtils.left(email,
1), index,
"*").concat(StringUtils.mid(email, index, StringUtils.length(email)));
-
}
-
/**
-
* [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
-
*
-
* @param cardNum
-
* @return
-
*/
-
public
static String
bankCard
(String cardNum) {
-
if (StringUtils.isBlank(cardNum)) {
-
return
"";
-
}
-
return StringUtils.left(cardNum,
6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum,
4), StringUtils.length(cardNum),
"*"),
"******"));
-
}
-
/**
-
* [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
-
*
-
* @param code
-
* @return
-
*/
-
public
static String
cnapsCode
(String code) {
-
if (StringUtils.isBlank(code)) {
-
return
"";
-
}
-
return StringUtils.rightPad(StringUtils.left(code,
2), StringUtils.length(code),
"*");
-
}
-
/**
-
* 获取脱敏json串 <注意:递归引用会导致java.lang.StackOverflowError>
-
*
-
* @param javaBean
-
* @return
-
*/
-
public
static String
getJson
(Object javaBean) {
-
String
json
=
null;
-
if (
null != javaBean) {
-
Class<?
extends
Object> raw = javaBean.getClass();
-
try {
-
if (raw.isInterface())
-
return json;
-
Gson
g
=
new
Gson();
-
Object
clone
= g.fromJson(g.toJson(javaBean, javaBean.getClass()), javaBean.getClass());
-
Set<Integer> referenceCounter =
new
HashSet<Integer>();
-
SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(raw), clone, referenceCounter);
-
json = JSON.toJSONString(clone, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty);
-
referenceCounter.clear();
-
referenceCounter =
null;
-
}
catch (Throwable e) {
-
logger.error(
"SensitiveInfoUtils.getJson() ERROR", e);
-
}
-
}
-
return json;
-
}
-
private
static Field[] findAllField(Class<?> clazz) {
-
Field[] fileds = clazz.getDeclaredFields();
-
while (
null != clazz.getSuperclass() && !Object.class.equals(clazz.getSuperclass())) {
-
fileds = (Field[]) ArrayUtils.addAll(fileds, clazz.getSuperclass().getDeclaredFields());
-
clazz = clazz.getSuperclass();
-
}
-
return fileds;
-
}
-
private
static
void
replace
(Field[] fields, Object javaBean, Set<Integer> referenceCounter)
throws IllegalArgumentException, IllegalAccessException {
-
if (
null != fields && fields.length >
0) {
-
for (Field field : fields) {
-
field.setAccessible(
true);
-
if (
null != field &&
null != javaBean) {
-
Object
value
= field.get(javaBean);
-
if (
null != value) {
-
Class<?> type = value.getClass();
-
// 1.处理子属性,包括集合中的
-
if (type.isArray()) {
-
int
len
= Array.getLength(value);
-
for (
int
i
=
0; i < len; i++) {
-
Object
arrayObject
= Array.get(value, i);
-
SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(arrayObject.getClass()), arrayObject, referenceCounter);
-
}
-
}
else
if (value
instanceof Collection<?>) {
-
Collection<?> c = (Collection<?>) value;
-
Iterator<?> it = c.iterator();
-
while (it.hasNext()) {
-
Object
collectionObj
= it.next();
-
SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(collectionObj.getClass()), collectionObj, referenceCounter);
-
}
-
}
else
if (value
instanceof Map<?, ?>) {
-
Map<?, ?> m = (Map<?, ?>) value;
-
Set<?> set = m.entrySet();
-
for (Object o : set) {
-
Entry<?, ?> entry = (Entry<?, ?>) o;
-
Object
mapVal
= entry.getValue();
-
SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(mapVal.getClass()), mapVal, referenceCounter);
-
}
-
}
else
if (!type.isPrimitive()
-
&& !StringUtils.startsWith(type.getPackage().getName(),
"javax.")
-
&& !StringUtils.startsWith(type.getPackage().getName(),
"java.")
-
&& !StringUtils.startsWith(field.getType().getName(),
"javax.")
-
&& !StringUtils.startsWith(field.getName(),
"java.")
-
&& referenceCounter.add(value.hashCode())) {
-
SensitiveInfoUtils.replace(SensitiveInfoUtils.findAllField(type), value, referenceCounter);
-
}
-
}
-
// 2. 处理自身的属性
-
SensitiveInfo
annotation
= field.getAnnotation(SensitiveInfo.class);
-
if (field.getType().equals(String.class) &&
null != annotation) {
-
String
valueStr
= (String) value;
-
if (StringUtils.isNotBlank(valueStr)) {
-
switch (annotation.type()) {
-
case CHINESE_NAME: {
-
field.set(javaBean, SensitiveInfoUtils.chineseName(valueStr));
-
break;
-
}
-
case ID_CARD: {
-
field.set(javaBean, SensitiveInfoUtils.idCardNum(valueStr));
-
break;
-
}
-
case FIXED_PHONE: {
-
field.set(javaBean, SensitiveInfoUtils.fixedPhone(valueStr));
-
break;
-
}
-
case MOBILE_PHONE: {
-
field.set(javaBean, SensitiveInfoUtils.mobilePhone(valueStr));
-
break;
-
}
-
case ADDRESS: {
-
field.set(javaBean, SensitiveInfoUtils.address(valueStr,
4));
-
break;
-
}
-
case EMAIL: {
-
field.set(javaBean, SensitiveInfoUtils.email(valueStr));
-
break;
-
}
-
case BANK_CARD: {
-
field.set(javaBean, SensitiveInfoUtils.bankCard(valueStr));
-
break;
-
}
-
case CNAPS_CODE: {
-
field.set(javaBean, SensitiveInfoUtils.cnapsCode(valueStr));
-
break;
-
}
-
}
-
}
-
}
-
}
-
}
-
}
-
}
-
//----------------------------------------------------------------------------------------------
-
public
static Method [] findAllMethod(Class<?> clazz){
-
Method [] methods= clazz.getMethods();
-
return methods;
-
}
-
//----------------------------------------------------------------------------------------------
-
public
static
enum
SensitiveType {
-
/**
-
* 中文名
-
*/
-
CHINESE_NAME,
-
/**
-
* 身份证号
-
*/
-
ID_CARD,
-
/**
-
* 座机号
-
*/
-
FIXED_PHONE,
-
/**
-
* 手机号
-
*/
-
MOBILE_PHONE,
-
/**
-
* 地址
-
*/
-
ADDRESS,
-
/**
-
* 电子邮件
-
*/
-
EMAIL,
-
/**
-
* 银行卡
-
*/
-
BANK_CARD,
-
/**
-
* 公司开户银行联号
-
*/
-
CNAPS_CODE;
-
}
-
}
声明注解:
-
import java.lang.annotation.Documented;
-
import java.lang.annotation.ElementType;
-
import java.lang.annotation.Inherited;
-
import java.lang.annotation.Retention;
-
import java.lang.annotation.RetentionPolicy;
-
import java.lang.annotation.Target;
-
import com.ucf.platform.framework.core.util.SensitiveInfoUtils;
-
/**
-
* @Title: SensitiveInfo.java
-
* @Copyright: Copyright (c) 2015
-
* @Description: <br>
-
* 敏感信息注解标记 <br>
-
*/
-
@Target({ElementType.FIELD,ElementType.METHOD})
-
@Retention(RetentionPolicy.RUNTIME)
-
@Inherited
-
@Documented
-
public
@interface SensitiveInfo {
-
public SensitiveInfoUtils.SensitiveType
type
() ;
-
}
测试:
-
public
class
JavaBeanA {
-
public
JavaBeanA
(String name,String id){
-
}
-
@SensitiveInfo(type=SensitiveType.CHINESE_NAME)
-
private
String
name
=
"A先生";
-
private JavaBeanB b;
-
private Date date;
-
private List<JavaBeanB> list;
-
private Map<String,JavaBeanB> map;
-
public String
getName
() {
-
return name;
-
}
-
public
void
setName
(String name) {
-
this.name = name;
-
}
-
public JavaBeanB
getB
() {
-
return b;
-
}
-
public
void
setB
(JavaBeanB b) {
-
this.b = b;
-
}
-
public List<JavaBeanB>
getList
() {
-
return list;
-
}
-
public
void
setList
(List<JavaBeanB> list) {
-
this.list = list;
-
}
-
public Map<String, JavaBeanB>
getMap
() {
-
return map;
-
}
-
public
void
setMap
(Map<String, JavaBeanB> map) {
-
this.map = map;
-
}
-
public Date
getDate
() {
-
return date;
-
}
-
public
void
setDate
(Date date) {
-
this.date = date;
-
}
-
}
-
public
class
JavaBeanB {
-
@SensitiveInfo(type=SensitiveType.CHINESE_NAME)
-
private
String
name
=
"B先生";
-
private JavaBeanA a;
-
private Set<JavaBeanA> list;
-
private Map<String,JavaBeanA> map;
-
public String
getName
() {
-
return name;
-
}
-
public
void
setName
(String name) {
-
this.name = name;
-
}
-
public JavaBeanA
getA
() {
-
return a;
-
}
-
public
void
setA
(JavaBeanA a) {
-
this.a = a;
-
}
-
public Set<JavaBeanA>
getList
() {
-
return list;
-
}
-
public
void
setList
(Set<JavaBeanA> list) {
-
this.list = list;
-
}
-
public Map<String, JavaBeanA>
getMap
() {
-
return map;
-
}
-
public
void
setMap
(Map<String, JavaBeanA> map) {
-
this.map = map;
-
}
-
}
-
public
class
SensitiveInfoUtilsTest {
-
/**
-
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
-
*/
-
@Test
-
public
void
testChineseNameString
() {
-
System.out.println(SensitiveInfoUtils.chineseName(
"李先生"));
-
}
-
/**
-
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
-
*/
-
@Test
-
public
void
testChineseNameStringString
() {
-
System.out.println(SensitiveInfoUtils.chineseName(
"李",
"雷"));
-
}
-
/**
-
* [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
-
*/
-
@Test
-
public
void
testIdCardNum
() {
-
System.out.println(SensitiveInfoUtils.idCardNum(
"1103541983073188711"));
-
}
-
/**
-
* [固定电话] 后四位,其他隐藏<例子:****1234>
-
*/
-
@Test
-
public
void
testFixedPhone
() {
-
System.out.println(SensitiveInfoUtils.fixedPhone(
"01077482277"));
-
}
-
/**
-
* [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
-
*/
-
@Test
-
public
void
testMobilePhone
() {
-
System.out.println(SensitiveInfoUtils.mobilePhone(
"13777446578"));
-
}
-
/**
-
* [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
-
*/
-
@Test
-
public
void
testAddress
() {
-
System.out.println(SensitiveInfoUtils.address(
"北京朝阳区酒仙桥中路26号院4号楼人人大厦",
8));
-
}
-
/**
-
* [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
-
*/
-
@Test
-
public
void
testEmail
() {
-
System.out.println(SensitiveInfoUtils.email(
"66374777@qq.com"));
-
}
-
/**
-
* [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234>
-
*/
-
@Test
-
public
void
testBankCard
() {
-
System.out.println(SensitiveInfoUtils.bankCard(
"6228480402565890018"));
-
}
-
/**
-
* [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
-
*/
-
@Test
-
public
void
testCnapsCode
() {
-
System.out.println(SensitiveInfoUtils.cnapsCode(
"102100029679"));
-
}
-
/**
-
* 获取脱敏json串 <注意:递归引用会导致java.lang.StackOverflowError>
-
*/
-
@Test
-
public
void
testGetJson
() {
-
// ThreadPoolExecutor consumeExecutor = new ThreadPoolExecutor(30, 30 + 10, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(30 + 10), new ThreadFactory() {
-
// @Override
-
// public Thread newThread(Runnable r) {
-
// Thread myThread = new Thread(r);
-
// myThread.setName("TT");
-
// return myThread;
-
// }
-
// }, new ThreadPoolExecutor.CallerRunsPolicy());
-
// while (true) {
-
// consumeExecutor.execute(new Runnable() {
-
// @Override
-
// public void run() {}
-
// });
-
// }
-
JavaBeanA
a1
=
new
JavaBeanA(
"",
"");
-
JavaBeanA
a2
=
new
JavaBeanA(
"",
"");
-
JavaBeanB
b1
=
new
JavaBeanB();
-
a1.setB(b1);
-
// a1.setDate(new Date());
-
List<JavaBeanB> a1l =
new
ArrayList<JavaBeanB>();
-
a1l.add(b1);
-
a1.setList(a1l);
-
Map<String, JavaBeanB> a1m =
new
HashMap<String, JavaBeanB>();
-
a1m.put(
"b1", b1);
-
a1.setMap(a1m);
-
b1.setA(a2);
-
Set<JavaBeanA> b1l =
new
HashSet<JavaBeanA>();
-
b1.setList(b1l);
-
Map<String, JavaBeanA> b1m =
new
HashMap<String, JavaBeanA>();
-
b1m.put(
"a2", a2);
-
b1.setMap(b1m);
-
long
t
= System.currentTimeMillis();
-
System.out.println(t);
-
System.out.println(SensitiveInfoUtils.getJson(a1));
-
System.out.println(System.currentTimeMillis()-t);
-
System.out.println(JSON.toJSON(a1));
-
System.out.println(System.currentTimeMillis()-t);
-
}
-
}
测试结果:
-
李**
-
李*
-
***************8711
-
*******2277
-
137****6578
-
北京朝阳区酒仙桥中路26号********
-
6*******@qq.com
-
622848*********0018
-
10**********
-
1443435915750
-
{"b":{"a":{"b":null,"date":null,"list":[],"map":null,"name":"A**"},"list":[],"map":{"a2":{"b":null,"date":null,"list":[],"map":null,"name":"A**"}},"name":"B**"},"date":null,"list":[{"a":{"b":null,"date":null,"list":[],"map":null,"name":"A**"},"list":[],"map":{"a2":{"b":null,"date":null,"list":[],"map":null,"name":"A**"}},"name":"B**"}],"map":{"b1":{"a":{"b":null,"date":null,"list":[],"map":null,"name":"A**"},"list":[],"map":{"a2":{"b":null,"date":null,"list":[],"map":null,"name":"A**"}},"name":"B**"}},"name":"A**"}
-
289
-
{"b":{"a":{"name":"A先生"},"list":[],"map":{"a2":{"name":"A先生"}},"name":"B先生"},"list":[{"a":{"name":"A先生"},"list":[],"map":{"a2":{"name":"A先生"}},"name":"B先生"}],"map":{"b1":{"a":{"name":"A先生"},"list":[],"map":{"a2":{"name":"A先生"}},"name":"B先生"}},"name":"A先生"}
-
300
-
<!-- gson -->
-
<dependency>
-
<groupId>com.google.code.gson
</groupId>
-
<artifactId>gson
</artifactId>
-
</dependency>
.
说明:在需要脱敏的字段上使用定义好的注解,在具体的使用时用SensitiveInfoUtils.getJson(a1),如果不需要脱敏的输出,尽量不要打印JSON,使用对象的toString()输出。效率更高。