您的位置:首页 > 编程语言

后台验证码实现,附代码详解

2017-08-22 10:22 435 查看
验证码后台验证

登陆页面的验证码一直是大家头痛的问题,目前多数的验证码走的都是前端验证,但前端验证有许多危险漏洞,本文是一个简单的后台验证码实现,详细请看如下所述。

实现方式:我的框架是struts+spring+jdbc 为了越过struts 配置文件 选择使用serverlet 方式。

先附图看下验证码样式



第一步前端页面

jsp页面代码如下,其中引用了两个js文件(js中触发from表单提交)

<script type="text/javascript" src="js/login.js"></script>

<!-- 验证码图片 要自己写一个输入的文本框 -->

    <img src="/ZQZC-WEB/codeimage" id="Verify"  style="position: relative;top:333px;left:307px;width:68px;height:36px;" alt="看不清,换一张"/> 

<script src="js/JSCode.js"></script>

<script>

<form action="sys/loginAction.do"  method="post" id="loginForm">

//<input type="hidden" name="isIOS" id="isIOS" value=""/>

//<input type="hidden" name="password" id="password" value=""/>

//<input type="hidden" name="loginName" id="loginName" value=""/>

<input type="hidden" name="codeCon" id="codeCon_" value=""/>

</form>

$(function(){    

      //点击图片更换验证码  

     $("#Verify").click(function(){

            /* 点击图片发送请求,设置了struts拦截器允许该请求通过 */

            $(this).attr("src","/ZQZC-WEB/codeimage?timestamp="+new Date().getTime());  

        });

     });        

</script>

login.js 其中js中有些代码没用到可根据个人使用删除

/**

* 登陆调用的方法

*/

function checklogin(){

    if (!$("#inputloginName").val()){

        $("#message").html("请输入用户名!");

        $("#inputloginName").focus();

        return false;

    }

    var loginNameReg = /^[a-zA-Z0-9_]{3,32}$/i;

    if (!loginNameReg.test($("#inputloginName").val())){

        $("#message").html("用户名非法,请重新输入!");

        $("#inputloginName").focus();

        return false;

    }

    if (!$("#inputpassword").val()){

        $("#message").html("请输入密码!");

        $("#inputpassword").focus();

        return false;

    }

    if (!$("#codeCon").val()){

        $("#message").html("请输入验证码!");

        $("#codeCon").focus();

        return false;

    }

    

    $("#password").val($.md5($("#inputpassword").val()));

    $("#loginName").val($("#inputloginName").val());

    //新增验证码

    $("#codeCon_").val($("#codeCon").val());

    $("#loginForm").submit();

}

/**

* 为了在火狐和谷歌下支持回车可以调用登陆

*/

function keypress(e)

{

    var currKey=0,e=e||event;

    if(e.keyCode==13){

        //回车事件会自动触发对象的click事件,如果在这里直接调用checklogin会导致该方法执行两次,回车调用一次,click调用一次

        //结果会有两条登入日志,解决办法就是在回车事件中触发click

        $("input[type='image']").click();

        return false;

    }else return true;

}

document.onkeypress=keypress;

/**

* 获得焦点

*/

function getFocus(str){

    $("#"+str).focus();

}

/**

* 增加常见问题页面的链接

*/

function problem(){//ZQL 20110707

    

    javascript:win=open('njsp/ExtJsp/problem/problem.jsp', 'content', 'width=802,height=450,left=230,top=130,location=no,toolbar=no,status=no,resizable=no,scrollbars=no,menubar=no,directories=no');

}

/**

* 用来得到字符串的长度

*/

function getlength(str){

    //str表示确认密码或者新密码

    var strvalue = document.getElementById(str).value;//用户输入的新密码或者确认密码的值

    var realLength = 0, len = strvalue.length, charCode = -1;

    for (var i = 0; i < len; i++) {

        charCode = str.charCodeAt(i);

        if (charCode >= 0 && charCode <= 128) realLength += 1;

        else realLength += 2;

    }

    return realLength*1;

    

}

/**

 * 用来检查密码必须包含大小写字母和数字

 * */

function CheckStr(s){

                return /[a-zA-Z0-9]*[a-z]+[a-zA-Z0-9]*/.test(s)&&/[a-zA-Z0-9]*[A-Z]+[a-zA-Z0-9]*/.test(s)&&/[a-zA-Z0-9]*[0-9]+[a-zA-Z0-9]*/.test(s);

}

/**

 * 修改密码提交时用到的验证

 */

function checkform(forma){

    var password = $("#password").val();//用户输入的旧密码

    var newpassword = $("#newpassword").val();//用户输入的新密码

    var verifypassword = $("#verifypassword").val();//用户输入的确认密码

    var dbpassword = $("#dbpassword").val();//用户的数据库密码

    if(!password){

        $("#message").html("请输入旧密码!");

        $("#password").focus();

        return false;

    }

    if(($.md5(password))!=dbpassword){

        $("#message").html("密码错误,请重新输入!");

        $("#password").focus();

        return false;

    }

    if(!newpassword){

        $("#message").html("请输入新密码!");

        $("#newpassword").focus();

        return false;

    }

    if(getlength("newpassword")<8){

        $("#message").html("密码长度至少为8!");

        $("#newpassword").focus();

        return false;

    }

    if(CheckStr(newpassword)==false){

        $("#message").html("密码必须包含大小写字母和数字!");

        $("#newpassword").focus();

        return false;

    }

    if(!verifypassword){

        $("#message").html("请输入确认密码!");

        $("#verifypassword").focus();

        return false;

    }

    if(newpassword!=verifypassword){

        $("#message").html("两次输入的密码不一致!");

        $("#verifypassword").focus();

        return false;

    }

//    $("#password").val($.md5(password));

//    $("#newpassword").val($.md5(newpassword));

//    $("#verifypassword").val($.md5(newpassword));

    var postData = new Object();

    postData[$("#password").attr("name")]=$.md5(password);

    postData[$("#newpassword").attr("name")]=$.md5(newpassword);

    postData[$("#verifypassword").attr("name")]=$.md5(verifypassword);

    $.ajax({

        url:$("#updatePwdForm").attr("action"),

        data:postData,

        type:"POST",

        success:function(data, textStatus, jqXHR){

            if(data.success){

                alert("密码修改成功,请重新登录!");

                window.top.location.href=$("#updatePwdF
4000
orm").attr("doneurl");

            }else{

                alert(data.msg);

            }

        }

    });

//    $("#updatePwdForm").submit();

//    alert("密码修改成功,请重新登录!");

//    window.parent.location.href='../login.jsp';

}

JSCode.js

    //解决ie7/8下浏览器不兼容trim

    String.prototype.trim = function () {

        return this .replace(/^\s\s*/, '' ).replace(/\s\s*$/, '' );

     }

    //生成一定长度的随机数字和字母

    function randomString(len) {

      len = len || 32;

      var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';    /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/

      var maxPos = $chars.length;

      var pwd = '';

      for (i = 0; i < len; i++) {

        pwd += $chars.charAt(Math.floor(Math.random() * maxPos));

      }

      return pwd;

    }

    

    function randomColor() {  

    var rand = Math.floor(Math.random( ) * 0xFFFFFF).toString(16);  

    

        if(rand.length == 6){  

            return rand;  

        }else{  

            return randomColor();  

        }  

    }

    

    

    function createCode(n,obj){

        var codeHtml="";

        var codeVal="";

        codeHtml+="<div id='codeShade' style='border:1px solid red;position:absolute;width:66px;height:30px;z-index:10;background:#"+randomColor()+";opacity:0.2;filter:alpha(opacity=20)'></div>"

        for(var i=0;i<n;i++){

            var codeColor='#'+randomColor();

            var strCode = "<span style='color:"+codeColor+";display:inline-block;width:17px;height:27px;line-height:27px;font-size:30px;'>";

            var OneCode = randomString(1);

            strCode+=OneCode;

            codeVal+=OneCode;

            strCode+="</span>"

            codeHtml+=strCode;

        }

        codeHtml+="<span id='codeVal' style='display:none;'>"+codeVal+"</span>";

        obj.html(codeHtml);

        $("#codeShade").click(function(){

            createCode(n,obj);

        });

    }

    

    function CheckYN(obj){

        if(obj.val().trim().toUpperCase()==$("#codeVal").text().toUpperCase()){

            return true;

        }else{

            return false;

        }

    }

  

第二步 后台

会用到三个类文件 SecurityCode(生成验证码),SecurityImage(绘制验证码图片),CodeImage(验证码和图片合并到一起并存入session以便调用)

SecurityCode.java

package cn.cntomorrow.beidian.model;

import java.util.Arrays;

/**  

  * 工具类,生成随机验证码字符串  

  * @version 1.0   

  * @author Lyan by 2017-08-03  20:00  

  *  

  */  

public class SecurityCode {  

        /**  

          * 验证码难度级别,Simple只包含数字,Medium包含数字和小写英文,Hard包含数字和大小写英文  

          */  

         public enum SecurityCodeLevel {Simple,Medium,Hard};  

           

         /**  

          * 产生默认验证码,4位中等难度  

         * @return  String 验证码  

         */  

        public static String getSecurityCode(){  

             return getSecurityCode(4,SecurityCodeLevel.Medium,false);  

         }  

        /**  

         * 产生长度和难度任意的验证码  

         * @param length  长度  

          * @param level   难度级别  

          * @param isCanRepeat  是否能够出现重复的字符,如果为true,则可能出现 5578这样包含两个5,如果为false,则不可能出现这种情况  

         * @return  String 验证码  

         */  

         public static String getSecurityCode(int length,SecurityCodeLevel level,boolean isCanRepeat){  

             //随机抽取len个字符  

             int len=length;  

               

             //字符集合(除去易混淆的数字0、数字1、字母l、字母o、字母O)  

             char[] codes={'1','2','3','4','5','6','7','8','9',  

                           'a','b','c','d','e','f','g','h','i','j','k','m','n','p','q','r','s','t','u','v','w','x','y','z',  

                           'A','B','C','D','E','F','G','H','I','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z'};  

               

             //根据不同的难度截取字符数组  

             if(level==SecurityCodeLevel.Simple){  

                 codes=Arrays.copyOfRange(codes, 0,9);  

             }else if(level==SecurityCodeLevel.Medium){  

                 codes=Arrays.copyOfRange(codes, 0,33);  

             }  

             //字符集合长度  

             int n=codes.length;  

              

             //抛出运行时异常  

             if(len>n&&isCanRepeat==false){  

                 throw new RuntimeException(  

                        String.format("调用SecurityCode.getSecurityCode(%1$s,%2$s,%3$s)出现异常,当isCanRepeat为%3$s时,传入参数%1$s不能大于%4$s",  

                                        len,level,isCanRepeat,n));  

            }  

            //存放抽取出来的字符  

             char[] result=new char[len];  

             //判断能否出现重复的字符  

            if(isCanRepeat){  

                 for(int i=0;i<result.length;i++){  

                     //索引 0 and n-1  

                     int r=(int)(Math.random()*n);  

                   

                     //将result中的第i个元素设置为codes[r]存放的数值  

                     result[i]=codes[r];  

                }  

             }else{  

                 for(int i=0;i<result.length;i++){  

                     //索引 0 and n-1  

                     int r=(int)(Math.random()*n);  

                      

                     //将result中的第i个元素设置为codes[r]存放的数值  

                     result[i]=codes[r];  

                       

                    //必须确保不会再次抽取到那个字符,因为所有抽取的字符必须不相同。  

                     //因此,这里用数组中的最后一个字符改写codes[r],并将n减1  

                    codes[r]=codes[n-1];  

                    n--;  

                 }  

            }  

             return String.valueOf(result);  

        }  

}

SecurityImage.java

package cn.cntomorrow.beidian.model;

import java.awt.Color;  

import java.awt.Font;  

import java.awt.Graphics;  

import java.awt.image.BufferedImage;  

import java.io.ByteArrayInputStream;  

import java.io.ByteArrayOutputStream;  

import java.io.IOException;  

import java.util.Random;  

import com.sun.image.codec.jpeg.ImageFormatException;  

import com.sun.image.codec.jpeg.JPEGCodec;  

import com.sun.image.codec.jpeg.JPEGImageEncoder;  

/**  

 * 验证码生成器类,可生成数字、大写、小写字母及三者混合类型的验证码。  

 * 支持自定义验证码字符数量;  

 * 支持自定义验证码图片的大小;  

 * 支持自定义需排除的特殊字符;  

 * 支持自定义干扰线的数量;  

 * 支持自定义验证码图文颜色  

 * @author Lyan by 2017-08-03  20:00

 * @version 1.0   

 */  

public class SecurityImage {  

         /**  

           * 生成验证码图片  

           * @param securityCode   验证码字符  

           * @return  BufferedImage  图片  

          */  

         public static BufferedImage createImage(String securityCode){  

             //验证码长度  

             int codeLength=securityCode.length();  

             //字体大小  

             int fSize = 15;  

             int fWidth = fSize + 1;  

             //图片宽度  

             int width = 68;  

             //图片高度  

             int height = 36;  

              //图片  

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

              Graphics g=image.createGraphics();  

             //设置背景色  

              g.setColor(Color.WHITE);  

               //填充背景  

              g.fillRect(0, 0, width, height);  

               //设置边框颜色  

              g.setColor(Color.LIGHT_GRAY);  

               //边框字体样式  

              g.setFont(new Font("Arial", Font.BOLD, height - 2));  

               //绘制边框  

              g.drawRect(0, 0, width - 1, height -1);  

               //绘制噪点  

              Random rand = new Random();  

               //设置噪点颜色  

               g.setColor(Color.LIGHT_GRAY);  

               for(int i = 0;i < codeLength * 6;i++){  

                   int x = rand.nextInt(width);  

                   int y = rand.nextInt(height);  

                  //绘制1*1大小的矩形  

                   g.drawRect(x, y, 1, 1);  

               }  

              //绘制验证码  

             int codeY = height - 10;    

              //设置字体颜色和样式  

               g.setColor(new Color(19,148,246));  

               g.setFont(new Font("Georgia", Font.BOLD, fSize));  

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

                   g.drawString(String.valueOf(securityCode.charAt(i)), i * 16 + 5, codeY);  

              }  

               //关闭资源  

               g.dispose();  

               return image;  

           }  

         

}

CodeImage.java

package cn.cntomorrow.beidian.action;

import java.awt.image.BufferedImage;

import java.io.ByteArrayInputStream;

import java.io.IOException;

import javax.imageio.ImageIO;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import cn.cntomorrow.beidian.model.SecurityCode;

import cn.cntomorrow.beidian.model.SecurityImage;

/**

 * 页面请求生成验证码

 * by Lyan 2017-08-04 20:00

 * */

public class CodeImage extends HttpServlet{

    private static final long serialVersionUID = 1L;

    @Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {

        //在doget中执行dopost方法

        doPost(req,resp);

    }

    @Override

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {

        String securityCode = SecurityCode.getSecurityCode();

        BufferedImage createImage = SecurityImage.createImage(securityCode);

        // 将生成的验证码code放入sessoin中

        req.getSession().setAttribute("code", securityCode);

        // 通过ImageIO将图片输出

        ImageIO.write(createImage, "JPG", resp.getOutputStream());

        

    }

}

第三步 登陆的Action 去验证验证码  要生成codeCon的get,set 方法

  HttpServletRequest request = ServletActionContext.getRequest();

        HttpSession gsession = request.getSession();

        Object codeConq = gsession.getAttribute("code");

        if (!codeCon.equals(codeConq.toString())) {

            System.out.println(codeConq);

            throw new Exception("验证码错误!!!");

        }

第四步 web.xml 配置文件

    <!-- 配置登陆页面验证码请求 -->

    <servlet>

        <servlet-name>codeimage</servlet-name>

<!-- 下面为Codeimage的路径-->

        <servlet-class>cn.cntomorrow.beidian.action.CodeImage</servlet-class>

        <load-on-startup>1</load-on-startup>

    </servlet>

    <servlet-mapping>

        <servlet-name>codeimage</servlet-name>

<!--如果访问不到加上项目名 /项目名
/codeimage -->

        <
bdd5
url-pattern>/codeimage</url-pattern>

    </servlet-mapping>

<!-- struts不被拦截的请求 -->

    <filter>

        <filter-name>sessionFilter</filter-name>

        <filter-class>cn.cntomorrow.web.base.SessionFilter</filter-class>

        <init-param>

            <param-name>notCheckURLList</param-name>

<!--里面是不走struts配置文件的请求-->

            <param-value>loginAction.do,exemptLogin.do,.js,.css,codeimage</param-value>

        </init-param>

        <init-param>

            <param-name>redirectURL</param-name>

            <param-value>/login.jsp</param-value>

        </init-param>

    </filter>

第五步 Session.Filter类 可以用我的,也可以自己写一个

package cn.cntomorrow.web.base;

import java.io.IOException;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

import mt.framework.util.Logger;

import share.Share;

/**

 * session过滤器

 * @author: Lyan

 */

public class SessionFilter implements Filter {

    protected FilterConfig filterConfig = null;

    private String notCheckURL = null;

    private String redirectURL = null;

    /**

     * 销毁操作

     */

    public void destroy() {

        notCheckURL = new String();

        redirectURL = new String();

    }

    /**

     * 判断session是否存在,不存在则跳转到登录页面

     */

    public void doFilter(ServletRequest request, ServletResponse response,

            FilterChain chain) throws IOException, ServletException{

        // Session属于HTTP范畴,所以ServletRequest对象需要先转换成HttpServletRequest对象

        HttpServletRequest req = (HttpServletRequest) request;

        HttpServletResponse res = (HttpServletResponse) response;

        HttpSession session = req.getSession();

        

        //如果访问的资源属于放行资源

        if (this.checkRequestURIIntNotFilterList(req)

                //或者是根路径

                || req.getRequestURI().length() == req.getContextPath().length() + 1) {

            chain.doFilter(req, res);

        } else {

            // 如果session不为空,则可以浏览其他页面

            if(session.getAttribute(Globals.KEY_USERINFO) != null){

                chain.doFilter(req, res);

            } else if(req.getRequestURI().indexOf("resourceReader") > -1){        

                chain.doFilter(req, res);

            }else if(req.getRequestURI().indexOf("insertCustomSar") > -1){        

                chain.doFilter(req, res);

            }else {

                Logger.info("!!!!!!!!!!!!!session time out!!!!!!!!!");

                res.setCharacterEncoding("UTF-8");

                res.setContentType("text/html; charset=UTF-8");

                String msg = Share.iAttr().getProperty("sessionTimeOut");

                // 跳转到登陆页

                res.getWriter().write(

                        "<script>alert('" + msg

                                + "');window.top.location.href='"

                                + req.getContextPath() + redirectURL

                                + "'</script>");

            }

        }

    }

    /**

     * 初始化filter,从web.xml中读取参数

     */

    public void init(FilterConfig filterConfig) throws ServletException {

        this.filterConfig = filterConfig;

        redirectURL = filterConfig.getInitParameter("redirectURL");

        String notCheckURLListStr = filterConfig.getInitParameter("notCheckURLList");

        notCheckURL = notCheckURLListStr;

    }

    /**

     * 判断当前url是否可以访问

     * @param request

     * @return boolean

     * @author: zhlong

     * @version: 2013-3-20 下午04:01:40

     */

    private boolean checkRequestURIIntNotFilterList(HttpServletRequest request) {

        String uri = request.getRequestURI();

        String str[] = notCheckURL.split(",");

        for(int i = 0;i<str.length;i++){

            if(uri.toLowerCase().contains(str[i].toLowerCase())){

                return true;

            }

        }

        //return uri.contains(notCheckURL);

        return false;

    }

}

到此也就完成了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息