publicclassGoogleAuthenticator{// 生成的key长度( Generate secret key length)publicstaticfinalint SECRET_SIZE =10;publicstaticfinal String SEED ="g8GjEvTbW5oVSV7avL47357438reyhreyuryetredLDVKs2m0QN7vxRs2im5MDaNCWGmcD2rvcZx";// Java实现随机数算法publicstaticfinal String RANDOM_NUMBER_ALGORITHM ="SHA1PRNG";// 最多可偏移的时间staticint window_size =3;// default 3 - max 17/**
* set the windows size. This is an integer value representing the number of
* 30 second windows we allow The bigger the window, the more tolerant of
* clock skew we are.
*
* @param s
* window size - must be >=1 and <=17. Other values are ignored
*/publicvoidsetWindowSize(int s){if(s >=1&& s <=17)
window_size = s;}/**
* Generate a random secret key. This must be saved by the server and
* associated with the users account to verify the code displayed by Google
* Authenticator. The user must register this secret on their device.
* 生成一个随机秘钥
*
* @return secret key
*/publicstatic String generateSecretKey(){
SecureRandom sr = null;try{
sr = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM);
sr.setSeed(Base64.decodeBase64(SEED));byte[] buffer = sr.generateSeed(SECRET_SIZE);
Base32 codec =newBase32();byte[] bEncodedKey = codec.encode(buffer);
String encodedKey =newString(bEncodedKey);return encodedKey;}catch(NoSuchAlgorithmException e){// should never occur... configuration error}return null;}publicstatic String generateCaoLiaoUrl(String user,String key,String companyUrl, String model){
StringBuffer sf =newStringBuffer("https://cli.im/api/qrcode/code?text=otpauth://totp/");
sf.append(user);
sf.append("-"+companyUrl);
sf.append("?secret=");
sf.append(key);
sf.append("&mhid=");
sf.append(model);return sf.toString();}/**
* Return a URL that generates and displays a QR barcode. The user scans
* this bar code with the Google Authenticator application on their
* smartphone to register the auth code. They can also manually enter the
* secret if desired
*
* @param user
* user id (e.g. fflinstone)
* @param host
* host or system that the code is for (e.g. myapp.com)
* @param secret
* the secret that was previously generated for this user
* @return the URL for the QR code to scan
*/publicstatic String getQRBarcodeURL(String user, String host, String secret){//https://cli.im/api/qrcode/code?text=二维码内容&mhid=sELPDFnok80gPHovKdI// String format = "http://www.google.com/chart?chs=200x200&chld=M%%7C0&cht=qr&chl=otpauth://totp/%s@%s?secret=%s";
String format ="https://cli.im/api/qrcode/code?text=otpauth://totp/%s@%s?secret=%s&mhid=sELPDFnok80gPHovKdI";return String.format(format, user, host, secret);}/**
* 生成一个google身份验证器,识别的字符串,只需要把该方法返回值生成二维码扫描就可以了。
*
* @param user
* 账号
* @param secret
* 密钥
* @return
*/publicstatic String getQRBarcode(String user, String secret){
String format ="otpauth://totp/%s?secret=%s";return String.format(format, user, secret);}/**
* Check the code entered by the user to see if it is valid 验证code是否合法
*
* @param secret
* The users secret.
* @param code
* The code displayed on the users device
* @param t
* The time in msec (System.currentTimeMillis() for example)
* @return
*/publicstaticbooleancheck_code(String secret,long code,long timeMsec){
Base32 codec =newBase32();byte[] decodedKey = codec.decode(secret);// convert unix msec time into a 30 second "window"// this is per the TOTP spec (see the RFC for details)long t =(timeMsec /1000L)/30L;// Window is used to check codes generated in the near past.// You can use this value to tune how far you're willing to go.for(int i =-window_size; i <= window_size;++i){long hash;try{
hash =verify_code(decodedKey, t + i);}catch(Exception e){// Yes, this is bad form - but// the exceptions thrown would be rare and a static// configuration problem
e.printStackTrace();thrownewRuntimeException(e.getMessage());// return false;}if(hash == code){returntrue;}}// The validation code is invalid.returnfalse;}privatestaticintverify_code(byte[] key,long t)throws NoSuchAlgorithmException, InvalidKeyException {byte[] data =newbyte[8];long value = t;for(int i =8; i-->0; value >>>=8){
data[i]=(byte) value;}
SecretKeySpec signKey =newSecretKeySpec(key,"HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signKey);byte[] hash = mac.doFinal(data);int offset = hash[20-1]&0xF;// We're using a long because Java hasn't got unsigned int.long truncatedHash =0;for(int i =0; i <4;++i){
truncatedHash <<=8;// We are dealing with signed bytes:// we just keep the first byte.
truncatedHash |=(hash[offset + i]&0xFF);}
truncatedHash &=0x7FFFFFFF;
truncatedHash %=1000000;return(int) truncatedHash;}}
二维码生成类
publicclassZxingUtils{privatestaticfinal Logger logger = LoggerFactory.getLogger(ZxingUtils.class);privatestaticfinalint BLACK =0xFF000000;privatestaticfinalint WHITE =0xFFFFFFFF;privatestatic BufferedImage toBufferedImage(BitMatrix matrix){
System.setProperty("java.awt.headless","true");int width = matrix.getWidth();int height = matrix.getHeight();
BufferedImage image =newBufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for(int x =0; x < width; x++){for(int y =0; y < height; y++){
image.setRGB(x, y, matrix.get(x, y)? BLACK : WHITE);}}return image;}privatestaticvoidwriteToFile(BitMatrix matrix, String format, File file)throws IOException {
BufferedImage image =toBufferedImage(matrix);if(!ImageIO.write(image, format, file)){thrownewIOException("Could not write an image of format "+ format +" to "+ file);}}/**
* 将内容contents生成长宽均为width的图片,图片路径由imgPath指定
*/publicstatic File getQRCodeImge(String contents,int width, String imgPath){returngetQRCodeImge(contents, width, width, imgPath);}/**
* 将内容contents生成长为width,宽为width的图片,图片路径由imgPath指定
*/publicstatic File getQRCodeImge(String contents,int width,int height, String imgPath){try{
Map<EncodeHintType, Object> hints =newHashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.CHARACTER_SET,"UTF8");
BitMatrix bitMatrix =newMultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, height, hints);
File imageFile =newFile(imgPath);writeToFile(bitMatrix,"png", imageFile);return imageFile;}catch(Exception e){
logger.error("create QR code error!", e);return null;}}/**
* 生成二维码,返回base64串
* @param contents
* @param width
* @return
*/publicstatic String getQRCodeImge(String contents,int width){try{
Map<EncodeHintType, Object> hints =newHashtable<EncodeHintType, Object>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.CHARACTER_SET,"UTF8");
BitMatrix bitMatrix =newMultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, width, width, hints);
BufferedImage image =toBufferedImage(bitMatrix);
ByteArrayOutputStream os =newByteArrayOutputStream();//新建流。
ImageIO.write(image,"png", os);byte b[]= os.toByteArray();returnnewBASE64Encoder().encode(b);}catch(Exception e){
logger.error("create QR code error!", e);return null;}}}