php实现动态口令认证(谷歌身份验证器)

  1. 谷歌身份验证器Google Authenticator是谷歌推出的一款动态口令工具,解决大家各平台账户遭到恶意攻击的问题,一般在相关的服务平台登陆中除了用正常用户名和密码外,需要再输入一次谷歌认证器生成的动态口令才能验证成功,相当于输入二次密码,以达到账户的高安全性。
  2. 例如交易所、金融平台、以及一些钱包等项目等等,都会使用谷歌身份验证器Google Authenticator来做二次认证,开启谷歌身份验证之后,登录账户,除了输入用户名和密码,还需要输入谷歌验证器上的动态密码。谷歌验证器上的动态密码,也称为一次性密码,密码按照时间或使用次数不断动态变化(默认 30 秒变更一次)
  3. 代码参考:https://github.com/PHPGangsta/GoogleAuthenticator
  4. 关键代码:
<?php
// https://github.com/PHPGangsta/GoogleAuthenticator
error_reporting(0);// 关闭错误报告
session_start(); // 启动session  
require_once 'PHPGangsta/GoogleAuthenticator.php';
$ga = new PHPGangsta_GoogleAuthenticator();
// $secret = $ga->createSecret();
// 自定义安全密钥
$secret = "62H6TMAXQTZBVTRB";
// 手机端扫描二维码获取动态口令
$qrCodeUrl = $ga->getQRCodeGoogleUrl('username', $secret);
echo "二维码地址: ".$qrCodeUrl."\n\n";
// 输出动态口令
$oneCode = $ga->getCode($secret);
echo "本次登录的动态口令:'$oneCode'\n";
// 动态口令认证
$checkResult = $ga->verifyCode($secret, $password,2);    // 2 = 2*30sec clock tolerance
if ($checkResult) {
    $_SESSION['username'] = $username;
    echo "<h1>登录成功!</h1>";
    header("Refresh: 5; url=main.php");
    exit;
} else {
    echo "<h1>登录失败!</h1>";
    header("Refresh: 3; url=login.html");
    exit;
}
?>
  • 使用方法:
  1. 手机端安装 Microsoft Authenticator
  2. 下载地址:https://www.microsoft.com/en-us/security/mobile-authenticator-app
  3. 将以上代码生成的二维码地址在浏览器中访问
  4. 手机端扫描二维码获取动态验证码
  • 代码示例:
  • login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>系统运维管理平台</title>
    <link rel="stylesheet" type="text/css" href="login.css"/>
</head>
<body>
    <div id="login">
        <h1>Login</h1>
        <form method="post" action="login.php">
            <input type="text" required="required" placeholder="用户名" name="username"></input>
            <input type="password" required="required" placeholder="密码" name="password"></input>
            <button class="but" type="submit">登录</button>
        </form>
    </div>
</body>
</html>
  • login.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>系统运维管理平台</title>
    <link rel="stylesheet" type="text/css" href="login.css"/>
</head>
<body>
<div id="login">
<?php
// https://github.com/PHPGangsta/GoogleAuthenticator
error_reporting(0);// 关闭错误报告
session_start(); // 启动session  
require_once 'PHPGangsta/GoogleAuthenticator.php';
$ga = new PHPGangsta_GoogleAuthenticator();
// $secret = $ga->createSecret();
# 自定义安全密钥
$secret = "62H6TMAXQTZBVTRB";
// $qrCodeUrl = $ga->getQRCodeGoogleUrl('admin', $secret);
// echo "二维码: ".$qrCodeUrl."\n\n";

// 检查用户是否已经登录  
if (isset($_SESSION['username'])) {  
    // 用户已登录,显示用户信息或其他操作  
    header("Refresh: 3; url=main.php");
} else {  
    if(!isset($_SESSION['num'])){//isset() — 检测num变量是否设置。
        $_SESSION['num'] = 0;
    }
    // 密码输入错误3次,将不允许登录!
    if($_SESSION['num']<3){
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $username = $_POST['username'];  
            $password = $_POST['password'];  
            //此处应该从数据库中查询是否存在系统用户,再进行口令验证
            if($username){
                $oneCode = $ga->getCode($secret);
                echo "本次登录的动态口令:'$oneCode'\n";
                $checkResult = $ga->verifyCode($secret, $password,2);    // 2 = 2*30sec clock tolerance
                if ($checkResult) {
                    $_SESSION['username'] = $username;
                    echo "<h1>登录成功!</h1>";
                    header("Refresh: 5; url=main.php");
                    exit;
                } else {
                    $_SESSION['num']++;
                    echo "<h1>登录失败!</h1>";
                    header("Refresh: 3; url=login.html");
                    exit;
                }
            }else{
                echo "<h1>登录失败!</h1>";
                header("Refresh: 3; url=login.html");
                exit;
            }
        } else {  
            header("Location: login.html");
            exit; 
        }
    }else{
        echo "<h1>密码输入错误已超过3次,系统已不允许登录!</h1>";
        header("Refresh: 3; url=login.html");
        exit;
    }
}
?>
</div>
</body>
</html>
  • main.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>系统运维管理平台</title>
    <link rel="stylesheet" type="text/css" href="login.css"/>
</head>
<body>
    <div id="login">
    <?php
    session_start(); // 启动session 
    if (isset($_SESSION['username'])) {  
        echo "<h2>".$_SESSION['username']."您已登录!</h2>";
        echo "<h2><a href='logout.php'>退出登录</a></h2>";
    } else{
        header("Refresh: 3; url=login.html");
    }
    ?>
</body>
</html>
  • logout.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>系统运维管理平台</title>
    <link rel="stylesheet" type="text/css" href="login.css"/>
</head>
<body>
    <div id="login">
    <?php
        session_start();
        if(isset($_SESSION['username']))
        {
            session_destroy();
        }
        header("Refresh: 3; url=login.html");
    ?>
</body>
</html>
  • login.css
html{   
    width: 100%;   
    height: 100%;   
    overflow: hidden;   
    font-style: sans-serif;   
}   
body{   
    width: 100%;   
    height: 100%;   
    font-family: 'Open Sans',sans-serif;   
    margin: 0;   
    background-color: #4A374A;   
}   
#login{   
    position: absolute;   
    top: 50%;   
    left:50%;   
    margin: -150px 0 0 -150px;   
    width: 300px;   
    height: 300px;   
}   
#login h1,h2{   
    color: #fff;   
    /* text-shadow:0 0 10px;    */
    letter-spacing: 1px;   
    text-align: center;   
}   
h1,h2{   
    font-size: 2em;   
    margin: 0.67em 0;   
}   
input{   
    width: 278px;   
    height: 18px;   
    margin-bottom: 10px;   
    outline: none;   
    padding: 10px;   
    font-size: 13px;   
    color: #fff;   
    /* text-shadow:1px 1px 1px;    */
    border-top: 1px solid #312E3D;   
    border-left: 1px solid #312E3D;   
    border-right: 1px solid #312E3D;   
    border-bottom: 1px solid #56536A;   
    border-radius: 4px;   
    background-color: #2D2D3F;   
}   
.but{   
    width: 300px;   
    min-height: 20px;   
    display: block;   
    background-color: #4a77d4;   
    border: 1px solid #3762bc;   
    color: #fff;   
    padding: 9px 14px;   
    font-size: 15px;   
    line-height: normal;   
    border-radius: 5px;   
    margin: 0;   
}
  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
动态口令认证是一种基于时间同步技术的认证方式,常用于网络身份认证、支付等场景。在Java中,我们可以通过以下步骤实现动态口令认证: 1. 获取当前时间 使用Java中的System.currentTimeMillis()方法获取当前时间戳,单位为毫秒。 2. 生成随机密钥 使用Java中的SecureRandom类生成一个随机密钥,长度为6-8位。 3. 计算动态口令 将当前时间戳除以一个固定的时间间隔(通常为30秒或60秒),得到的结果称为时间步长。使用Java中的HMAC-SHA1算法,将时间步长与随机密钥进行哈希运算,得到一个20字节的哈希值。根据RFC4226标准,从哈希值中取出4个字节作为动态口令。 4. 验证动态口令 在服务端,使用相同的算法和密钥生成动态口令,与用户输入的口令进行比较。如果相同,则认证通过。 下面是一个简单的Java代码示例: ```java import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; public class TOTP { // 时间步长,单位为秒 private static final int TIME_STEP = 30; // 动态口令长度 private static final int TOKEN_LENGTH = 6; // 哈希算法 private static final String HASH_ALGORITHM = "HmacSHA1"; // 随机密钥长度 private static final int SECRET_LENGTH = 8; public static void main(String[] args) { // 生成随机密钥 byte[] secret = new byte[SECRET_LENGTH]; SecureRandom random = new SecureRandom(); random.nextBytes(secret); // 获取当前时间戳 long timestamp = System.currentTimeMillis() / 1000; // 计算时间步长 long timeStep = timestamp / TIME_STEP; // 计算哈希值 byte[] hash = hmacSha1(secret, longToBytes(timeStep)); // 取出动态口令 int offset = hash[hash.length - 1] & 0xf; int token = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff); token %= Math.pow(10, TOKEN_LENGTH); System.out.println("动态口令:" + String.format("%06d", token)); } /** * HMAC-SHA1算法 */ private static byte[] hmacSha1(byte[] key, byte[] data) { try { Mac mac = Mac.getInstance(HASH_ALGORITHM); SecretKeySpec secret = new SecretKeySpec(key, HASH_ALGORITHM); mac.init(secret); return mac.doFinal(data); } catch (NoSuchAlgorithmException | InvalidKeyException e) { throw new RuntimeException("HMAC-SHA1算法失败", e); } } /** * 将long类型转换为byte数组 */ private static byte[] longToBytes(long value) { byte[] bytes = new byte[8]; for (int i = 7; i >= 0; i--) { bytes[i] = (byte) (value & 0xff); value >>= 8; } return bytes; } } ``` 在实际应用中,我们需要将随机密钥保存在服务端,每次认证时从数据库中获取,并与用户输入的口令进行比较。同时,由于时间同步存在延迟和误差,我们需要允许一定的时间差来允许用户的动态口令通过认证
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值