前面已经进行了登陆模块的讲解,本节讨论登陆模块的一些安全机制,例如验证码在登陆模块中所起到的安全作用。

验证码及它的作用

  验证码为全自动区分计算机和人类的图灵测试的缩写,是一种区分用户是计算机的公共全自动程序,这个问题可以由计算机生成并评判,但是必须只有人类才能解答.可以防止恶意破解密码、刷票、论坛灌水、有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登录。

图文验证码的原理

  在servlet中随机生成一个指定位置的验证码,一般为四位,然后把该验证码保存到session中.在通过Java的绘图类以图片的形式输出该验证码。为了增加验证码的安全级别,可以输出图片的同时输出干扰线,最后在用户提交数据的时候,在服务器端将用户提交的验证码和Session保存的验证码进行比较。

验证码所需的技术

  i.因为验证码中的文字,数字,应为都是可变的,故要用到随机生成数技术
  ii.如果验证码中包含汉字,则要用到汉字生成技术
  iii.可以使用Ajax技术实现局部刷新
  iv.可以使用图片的缩放和旋转技术
  v.随机绘制干扰线(可以是折现,直线等)
  vi.如果考虑到验证码的安全性,可以使用MD5加密

登陆页面

  首先,在登陆页面login.jsp中合适位置加入如下代码:

<img alt="点击更换验证码" src="createimage" id="CreateCheckCode" onclick="changeImage()">  
  验证码:<input name="check" type="text" /><br/>

  第一行代码用于显示验证码图片,第二行代码用于输入验证码图片中的数字。此外,当验证码不能识别时,应该可以通过单机验证码,更换验证码。那么,需要在head头部加入如下js代码:

<script type="text/javascript">  
    function changeImage(){
        document.getElementById("CreateCheckCode").src = document
            .getElementById("CreateCheckCode").src
            + "?nocache=" + new Date().getTime();
    }
    </script>

制作验证码

  制作验证码的方法请参考前面所需技术小节的介绍,本节采用随机方法生成4位数字的技术方法,最后将这4位数字采用画笔工具写入到图片中。
  首先,在项目中新建一个类:CheckCode,里面包含两个方法,第一个方法是随机生成4位数字,第二个方法是生成一个图片,并将前面生成的4位数字写入到图片,并将图片返回。代码如下:

package a;  
import java.awt.Color;  
import java.awt.Font;  
import java.awt.Graphics;  
import java.awt.image.BufferedImage;  
import java.io.IOException;  
import java.io.OutputStream;  
import javax.imageio.ImageIO;

public class CheckCode {  
    private String getRandomString() {
        int ranNum = (int) (Math.random() * 9000) + 1000;
        return ranNum + "";
    }

    public String getEnsure(int width, int height,OutputStream os) {
        // 在内存中创建图象
        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        Graphics g=image.getGraphics();     //创建Graphics对象,其作用相当于画笔
        g.setColor(Color.getColor("F8F8F8"));
        g.fillRect(0, 0, width, height);    //绘制背景
        Font mfont=new Font("楷体",Font.BOLD,16); //定义字体样式
        g.setFont(mfont);                   //设置字体
        g.setColor(Color.RED);
        String rans=getRandomString();
        g.drawString(rans, 5, 20);
        // 图象生效 
        g.dispose();
        try {
            ImageIO.write(image, "JPEG", os);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return rans;
    }
}

新建生成验证码的Servlet

  本节主要的业务流程是,禁止浏览器对验证码进行缓存,以及生成验证码图片,并将验证码保存在会话中。代码如下:

package a;

import java.io.IOException;  
import javax.servlet.ServletException;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import javax.servlet.http.HttpSession;

public class CreateImage extends HttpServlet {  
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //禁用缓存,每次访问此页面,都重新生成
        response.setHeader("Pragma","No-cache"); 
        response.setHeader("Cache-Control","no-cache"); 
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");
        int width=40;
        int height=30;
        //生成验证码的实例对象
        CheckCode ie = new CheckCode();
        //调用里面的方法,返回的是生成的验证码中的字符串
        String str = ie.getEnsure(width,height,response.getOutputStream());
        //获得session,并把字符串保存在session中,为后面的对比做基础
        HttpSession session = request.getSession();
        session.setAttribute("check1", str);      
    }
}

配置生成验证码的Servlet

  在web.xml中加入如下代码:

  <servlet>
    <servlet-name>CreateImage</servlet-name>
    <servlet-class>a.CreateImage</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>CreateImage</servlet-name>
    <url-pattern>/createimage</url-pattern>
  </servlet-mapping>

修改登录模块的业务逻辑

  在登陆模块的loginServlet.java中修改登录业务流程,加入对验证码的验证,修改后的代码如下:

package a;

import java.io.IOException;

import javax.servlet.ServletConfig;  
import javax.servlet.ServletException;  
import javax.servlet.ServletRequest;  
import javax.servlet.ServletResponse;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;

public class loginServlet implements javax.servlet.Servlet{  
    public void doPost(HttpServletRequest request,HttpServletResponse response)
    throws ServletException,IOException{
    String userName = request.getParameter("username");//取得用户名
    String password = request.getParameter("password");//取得密码
    String check1 = (String)request.getSession().getAttribute("check1"); 
    String check2 = request.getParameter("check"); 
    DBUtil db = new DBUtil();//构建数据库对象
    boolean canLogin = db.loginSuccess(userName, password);
    if(check1.equals(check2)&&canLogin){//根据登陆情况,跳转页面
    response.sendRedirect("index.jsp");
    }else{
    response.sendRedirect("login.jsp");
    }
    }

    public void destroy() {
        // TODO Auto-generated method stub

    }

    public ServletConfig getServletConfig() {
        // TODO Auto-generated method stub
        return null;
    }

    public String getServletInfo() {
        // TODO Auto-generated method stub
        return null;
    }

    public void init(ServletConfig arg0) throws ServletException {
        // TODO Auto-generated method stub

    }

    public void service(ServletRequest request, ServletResponse response)
            throws ServletException, IOException {
        HttpServletRequest rq = (HttpServletRequest)request;
        HttpServletResponse rs = (HttpServletResponse) response;
        doPost(rq,rs);

    }
}

调试页面

  到目前位置,基本完成了登陆模块的安全机制,但是依旧存在很多安全隐患,后面的章节将继续对登陆模块的安全机制进行分析。