樂搏自動化測試 - 知己知彼之驗證碼原理【樂搏TestPRO】

前篇文講到了實際工作中驗證碼的處理方法,接下來一起看一下驗證碼的生成原理,以及驗證碼是如何在後臺進行驗證的。

先說一下原理:

直接驗證碼的原理

image

短信驗證碼的原理:

image

無論是哪種驗證碼的生成,過程都是:

1、客戶端訪問了需要驗證碼的頁面

**2、後臺調用驗證碼生成代碼先生成一個驗證碼,**先將生成的驗證碼存放起來以便後續校驗,然後再將這個驗證碼和用戶的請求頁面一起發給客戶

3、客戶填寫了必要信息及驗證碼後,再將頁面信息一併提交給服務器,服務器在後臺將用戶填寫的驗證碼與2中存儲的驗證碼進行對比

問題是這個生成的驗證碼如何存儲?

對於後臺的驗證碼存儲,一般是存放到Seesion域中,下面請看簡單代碼演示傳統Servlet代碼:

生成驗證碼的工具類定義:

@WebServlet(name = "VerifyCodeServlet", urlPatterns = "/code")

public class VerifyCodeServlet extends HttpServlet {

    //創建一個隨機類
    private Random ran = new Random();

    //寫一個方法隨機生成一種顏色
    private Color getRandomColor() {

        //隨機生成0~255之間的數
        int red = ran.nextInt(256);

        int green = ran.nextInt(256);

        int blue = ran.nextInt(256);

        return new Color(red, green, blue);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1\. 創建緩存圖片
        int width = 90, height = 30;

        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        //2\. 獲取畫筆對象
        Graphics graphics = img.getGraphics();

        //3\. 設置畫筆顏色
        graphics.setColor(Color.WHITE);

        //4\. 填充矩形區域
        graphics.fillRect(0, 0, width, height);

        //5\. 從字符數組中隨機得到字符
        char[] arr = { 'A', 'B', 'C', 'D', 'N', 'E', 'W', 'b', 'o', 'y', '1', '2', '3', '4','5','6' };

        //6\. 循環4次,畫4個字符
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < 4; i++) {

            //7\. 設置字的顏色爲隨機
            graphics.setColor(getRandomColor());

            //8\. 設置字體,大小爲19
            graphics.setFont(new Font(Font.SANS_SERIF,Font.BOLD+Font.ITALIC,19));

            //隨機得到下標
            int index = ran.nextInt(arr.length);

            char c = arr[index];

            //將循環得到的字符拼接成字符串
            sb.append(c);

            //9\. 將每個字符畫到圖片,x增加,y不變。
            graphics.drawString(String.valueOf(c),10+(i*20), 20);
        }

        //將驗證碼以字符串的方式放到會話域中
        HttpSession session = request.getSession();

        System.out.println("驗證碼:"+sb.toString());

        session.setAttribute("code",sb.toString());

        //11\. 畫8條幹擾線,每條線的顏色不同
        for (int i = 0; i < 8; i++) {

            //10\. 線的位置是隨機的,x範圍在width之中,y的範圍在height之中。
            int x1 = ran.nextInt(width);

            int y1 = ran.nextInt(height);

            int x2 = ran.nextInt(width);

            int y2 = ran.nextInt(height);

            graphics.setColor(getRandomColor());

            graphics.drawLine(x1,y1,x2,y2);
        }

        //12\. 將緩存的圖片輸出到響應輸出流中
        ImageIO.write(img,"jpg",response.getOutputStream());
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

需要驗證碼的用戶登錄代碼:

@WebServlet(name = "LoginServlet", urlPatterns = "/login")

public class LoginServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

response.setContentType("application/json");

// 得到Session對象
HttpSession session = request.getSession();

// 得到session中的驗證碼,該碼和UI上顯示的一致
String sessionCode = (String) session.getAttribute("code");

// 得到登陸頁面上輸入的驗證碼
String requesCode = request.getParameter("code");

// 用戶輸入的驗證碼與session中的驗證碼進行比較
if ((! requesCode.equalsIgnoreCase(sessionCode))) {

//用戶輸入的驗證碼也seesion中的不一致,將錯誤信息記錄到session中,再重定向回登錄頁面,提示錯誤
session.setAttribute("errorMsg", "驗證碼錯誤!");

session.setAttribute("username", username);

response.sendRedirect("login.jsp");

return;
}

如果要設置萬能驗證碼,只需將“if ((! requesCode.equalsIgnoreCase(sessionCode)))” 這一行代碼改爲以下即可:

if ((! requesCode.equalsIgnoreCase(sessionCode)) **|| ("123456".equals(sessionCode))**){ ....}

通過以上的簡單後臺代碼,我們就可以完全理解了一般的驗證碼生成與驗證原理,以及如何設置“萬能驗證碼”,從而方便我們測試
前端代碼:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<!DOCTYPE html>
  <html>
  <head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>會員登錄</title>
  <link rel="stylesheet" href="css/bootstrap.min.css" type="text/css" />
  <script src="js/jquery-1.11.3.min.js" type="text/javascript"></script>
  <script src="js/bootstrap.min.js" type="text/javascript"></script>
  <!-- 引入自定義css文件 style.css -->
  <link rel="stylesheet" href="css/style.css" type="text/css" />
  <style>
  body {
          margin-top: 20px;
          margin: 0 auto;
}
.carousel-inner .item img {
          width: 100%;
          height: 300px;
}
font {
          color: #666;
          font-size: 22px;
          font-weight: normal;
          padding-right: 17px;
  }
  </style>
</head>
<body>
<c:if test="${not empty errorMsg  }">
<script type="text/javascript">
alert("${errorMsg}")
</script>
</c:if>
<div class="container-fluid">
<div class="col-md-4">
<img src="./img/lebo-logo.png" />
<div class="line"></div>
</div>
<div class="col-md-3" style="padding-top:20px">
<ol class="list-inline">
<!-- <li><a href="login.htm">登錄</a> </li>
<li><a href="register.htm">註冊</a> </li> -->
</ol>
</div>
</div>
<div class="container-fluid">
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span> 
<span class="icon-bar"></span> <span class="icon-bar"></span> 
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">首頁</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">班次信息<span class="sr-only">(current)</span>
</a>
</li>
<li><a href="#">師資力量</a>
</li>
</ul>
<form class="navbar-form navbar-right" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid -->
</nav>
</div>
<div class="container" style="width:100%;height:460px;background:#FF2C4C url('img/loginbg.jpg') no-repeat;">
<div class="row">
<div class="col-md-7">
</div>
<div class="col-md-5">
<div style="width:440px;border:1px solid #E7E7E7;padding:20px 0 20px 30px;border-radius:5px;margin-top:60px;background:#fff;">
<font>學員登錄</font>
<div>&nbsp;</div>
<form class="form-horizontal" action="${pageContext.request.contextPath }/login" method="post">
<div class="form-group">
<label for="username" class="col-sm-2 control-label">用戶名</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="username" value="${username}" id="username" placeholder="請輸入用戶名">
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">密&nbsp;&nbsp 碼</label>
<div class="col-sm-6">
<input type="password" class="form-control" name="password" id="password" placeholder="請輸入密碼">
</div>
</div>
<div class="form-group">
<label for="code" class="col-sm-2 control-label">驗證碼</label>
<div class="col-sm-3">
<input type="text" name="code" class="form-control" id="code" placeholder="輸入驗證碼">
</div>
<div class="col-sm-3">
<img src="${pageContext.request.contextPath }/verifyCode" title="換一張" style="cursor: pointer" id="imgcode" />
<script type="text/javascript">
document.getElementById("imgcode").onclick = function(
ev) {
this.src = "${pageContext.request.contextPath }/verifyCode?t=" + Math.random();
}
</script>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label> <input type="checkbox" name="autoLogin" value="yes"> 自動登錄 </label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <label>
<input type="checkbox" name="remember" value="yes"> 記住用戶名 </label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type="submit" width="100" value="" border="0"
style="background: url('./img/login.png') no-repeat scroll 0 0 rgba(0, 0, 0, 0); height:41px;width:120px;color:white;">
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div style="text-align: center;margin-top: 5px;">
<ul class="list-inline">
<li><a>關於我們</a>
</li>
<li><a>聯繫我們</a>
</li>
<li><a>招賢納士</a>
</li>
<li><a>法律聲明</a>
</li>
<li><a>友情鏈接</a>
</li>
<li><a target="_blank">支付方式</a>
</li>
<li><a>服務聲明</a>
</li>
<li><a>廣告聲明</a>
</li>
</ul>
</div>
<div style="text-align: center;margin-top: 5px;margin-bottom:20px;">
Copyright &copy; 2005-2019</div>
</body>
</html>

對於比較複雜的場景,如,在分佈式和集羣的環境中,具體的處理方式不盡相同,分佈式環境中一般將在後臺生成的驗證碼以一個Key-Vlaue的形式進行處理,Key是一個隨機生成的“token”,Value是真正生成的驗證碼,然後將驗證碼的Key-Value放入Redis緩存中,再將Key傳遞給客戶端。

進行後臺校驗時,先根據客戶端傳遞的Key得到客戶端填寫的驗證碼,再從Redis中將token對應的Value取出進行校驗。

無論哪個方法,其原理都是相通的,在我們測試的過程中,如果需要設置萬能驗證碼,或是城將驗證碼暫時去掉,我們做爲測試人員,在和開發溝通的過程中,應該做到心中有數,進行有效的溝通。

每天持續更新,軟件測試知識!今天的分享先到這裏,如果想要測試學習視頻資料,可以加入軟件測試學習交流答疑qun :六3六85九九六4

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章