API前置系统开发流程:8.运用EhCache缓存,实现验证码验证

寵の児 提交于 2019-12-20 12:29:28

首先选用EhCache的原因是,他的体量小,引用jar包即可使用,相比于redis来讲使用方便,不需要专门起服务。而我要实现的功能很简单,只是将生成的验证码存储于缓存,五分钟后便将缓存清理,所以对服务器内存的占用并不大。如果是大体量的缓存服务,建议使用redis等缓存技术。

1.maven引入ehcache的jar包

	<dependency>
	    <groupId>net.sf.ehcache</groupId>
	    <artifactId>ehcache</artifactId>
	</dependency>

2.设置配置
在resource下面新建文件夹ehcacheconfig,并在文件夹中添加ehcache-config.xml文件。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
 
  <!-- 磁盘缓存位置 -->
  <diskStore path="java.io.tmpdir/ehcache"/>
 
  <!-- 默认缓存 -->
  <defaultCache
          maxEntriesLocalHeap="10000"
          eternal="false"
          timeToIdleSeconds="0"
          timeToLiveSeconds="300"
          maxEntriesLocalDisk="10000000"
          diskExpiryThreadIntervalSeconds="120"
          memoryStoreEvictionPolicy="LRU">
    <persistence strategy="localTempSwap"/>
  </defaultCache>
 
  <!-- 验证码缓存 -->
  <cache name="DynamicCodeCache"
         maxElementsInMemory="1000"
         eternal="false"
         timeToIdleSeconds="0"
         timeToLiveSeconds="300"
         overflowToDisk="false"
         memoryStoreEvictionPolicy="LRU"/>
</ehcache>

我的缓存服务名字叫DynamicCodeCache,最大缓存数为1000,缓存最大存活时间为300秒

在application.properties文件中,注入xml配置信息

spring.cache.ehcache.config=classpath:ehcacheconfig/ehcache-config.xml

3.编写实现类

@Service
	public class EhCacheServiceImpl implements EhCacheService {

		@Autowired
		private CacheManager cacheManager;
		
		/**
		 * 以手机号为key,将验证码存入缓存实例化对象
		 */
		@Override
		public void cacheDynamicCode(String key,String dynamicCode) {
			// TODO Auto-generated method stub
			
			Cache cache = cacheManager.getCache("DynamicCodeCache");//获取缓存池
			
			Element element = new Element(key,dynamicCode);//新建缓存元素
			
			cache.put(element);//加入缓存中
			
			Element el = cache.get(key);
			
			System.out.println(el);
			
			System.out.println(el.getObjectValue());
			
		}
	
		/**
		 * 验证验证码是否正确
		 */
		@Override
		public Map<String, String> verifyDynamicCode(String key, String dynamicCode) {
			// TODO Auto-generated method stub
			
			String flag = "1";
			
			String msg = "";
			
			Cache cache = cacheManager.getCache("DynamicCodeCache");//获取缓存池
			
			Element el = cache.get(key);
			
			if(el == null) {
				msg = "验证码超时";
			}else if(dynamicCode.equals(el.getObjectValue())) {
				msg = "验证成功";
				flag = "0";
			}else {
				msg = "验证码错误";
			}
			
			Map<String,String> map = new HashMap();
			
			map.put("msg", msg);
			map.put("flag", flag);
			
			return map;
		}
	
		/**
		 * 在客户申请重发时,将缓存中以有的老旧验证码删除
		 */
		@Override
		public void removeDynamicCode(String key) {
			// TODO Auto-generated method stub
			
			Cache cache = cacheManager.getCache("DynamicCodeCache");//获取缓存池
			
			Element el = cache.get(key);
			
			if(el == null) {
				return;
			}else {
				cache.remove(key);
			}
		}
	}

4.附验证码的生成
		
	@Service
public class OtpServiceImpl implements OtpService {
	
	/**
     * 共享密钥
     */
    private static final String SECRET_KEY = "2345678910jqkajoker";

    /**
     * 时间步长 单位:毫秒 作为口令变化的时间周期
     */
    private static final long STEP = 1000;

    /**
     * 转码位数 [1-8]
     */
    private static final int CODE_DIGITS = 6;

    /**
     * 初始化时间
     */
    private static final long INITIAL_TIME = 0;

     /**
     * 柔性时间回溯
     */
    private static final long FLEXIBILIT_TIME = 1000;

    /**
     * 数子量级
     */
    private static final int[] DIGITS_POWER = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};

    private OtpServiceImpl() {
    }

    /**
     * 生成一次性密码
     *
     * @param code 账户
     * @param pass 密码
     * @return String
     */
    public String generateMyTOTP(String code, String pass) {
    	
        if("".equals(code)||"".equals(pass)) {
        	throw new RuntimeException("账户密码不许为空");
        }
        long now = new Date().getTime();
        String time = Long.toHexString(timeFactor(now)).toUpperCase();
        return generateTOTP(code + pass + SECRET_KEY, time);
    }

    /**
     * 刚性口令验证
     *
     * @param code 账户
     * @param pass 密码
     * @param totp 待验证的口令
     * @return boolean
     */
    public boolean verifyTOTPRigidity(String code, String pass, String totp) {
        return generateMyTOTP(code, pass).equals(totp);
    }
    
    /**
     * 柔性口令验证
     *
     * @param code 账户
     * @param pass 密码
     * @param totp 待验证的口令
     * @return boolean
     */
    public static boolean verifyTOTPFlexibility(String code, String pass, String totp) {
        long now = new Date().getTime();
        String time = Long.toHexString(timeFactor(now)).toUpperCase();
        System.out.println(now);
        System.out.println(timeFactor(now));
        String tempTotp = generateTOTP(code + pass + SECRET_KEY, time);
        if (tempTotp.equals(totp)) {
            return true;
        }
        System.out.println(now - FLEXIBILIT_TIME);
        System.out.println(timeFactor(now - FLEXIBILIT_TIME));
        String time2 = Long.toHexString(timeFactor(now - FLEXIBILIT_TIME)).toUpperCase();
        String tempTotp2 = generateTOTP(code + pass + SECRET_KEY, time2);
        return tempTotp2.equals(totp);
    }

    /**
     * 获取动态因子
     *
     * @param targetTime 指定时间
     * @return long
     */
    private static long timeFactor(long targetTime) {
        return (targetTime - INITIAL_TIME) / STEP;
    }

    /**
     * 哈希加密
     *
     * @param crypto   加密算法
     * @param keyBytes 密钥数组
     * @param text     加密内容
     * @return byte[]
     */
    private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
        try {
            Mac hmac;
            hmac = Mac.getInstance(crypto);
            SecretKeySpec macKey = new SecretKeySpec(keyBytes, "AES");
            hmac.init(macKey);
            return hmac.doFinal(text);
        } catch (GeneralSecurityException gse) {
            throw new UndeclaredThrowableException(gse);
        }
    }

    private static byte[] hexStr2Bytes(String hex) {
        byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
        byte[] ret = new byte[bArray.length - 1];
        System.arraycopy(bArray, 1, ret, 0, ret.length);
        return ret;
    }

    private static String generateTOTP(String key, String time) {
        return generateTOTP(key, time, "HmacSHA1");
    }


    private static String generateTOTP256(String key, String time) {
        return generateTOTP(key, time, "HmacSHA256");
    }

    private static String generateTOTP512(String key, String time) {
        return generateTOTP(key, time, "HmacSHA512");
    }

    private static String generateTOTP(String key, String time, String crypto) {
        StringBuilder timeBuilder = new StringBuilder(time);
        while (timeBuilder.length() < 16)
            timeBuilder.insert(0, "0");
        time = timeBuilder.toString();

        byte[] msg = hexStr2Bytes(time);
        byte[] k = key.getBytes();
        byte[] hash = hmac_sha(crypto, k, msg);
        return truncate(hash);
    }

    /**
     * 截断函数
     *
     * @param target 20字节的字符串
     * @return String
     */
    private static String truncate(byte[] target) {
        StringBuilder result;
        int offset = target[target.length - 1] & 0xf;
        int binary = ((target[offset] & 0x7f) << 24)
                | ((target[offset + 1] & 0xff) << 16)
                | ((target[offset + 2] & 0xff) << 8) | (target[offset + 3] & 0xff);

        int otp = binary % DIGITS_POWER[CODE_DIGITS];
        result = new StringBuilder(Integer.toString(otp));
        while (result.length() < CODE_DIGITS) {
            result.insert(0, "0");
        }
        return result.toString();
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!