zoukankan      html  css  js  c++  java
  • Struts2学习笔记(十九) 验证码

    概述

    验证码也是我们在web应用中经常要用到的功能。基本思路就是,我们在服务端动态的生一成张图片,然后将它输出到客户端。图片上包含一些字符信息,我们将这些字符信息事先保存在session中,那么客户端在看到图片之后,将图片上的字符输出到表单中,然后将表单提交。我们接收到表单数据之后,对表单中提交的验证码与session中保存的验证码进行比对,如果相同,那么验证通过。否则,验证失败!采取一些处理。

    验证码的主要作用就是用在用户登录上,能有效的防止客户端多次发送登录请求来暴力破解。由于验证码信息是以图片的形式呈现的,因此要想通过程序来识别这些字符还是不太容易的。当然验证码也不是绝对安全的,但是相对来说它的实现比较容易,安全性也相对较高,所以使用的非常广泛。

    使用jsp/servlet实现图片验证码

    这里我们主要是学习验证码的实现原理,因此就不做的过于复杂了。其实有很多开源的验证码生成库,他们做的比较精密,集成也比较方便,因此有需要的话可以使用第三方的库。为了生成验证码的部分代码能够被重用,我将它单独提取成为一个类。

    public class AuthCode {
    
        private ByteArrayInputStream input;
        private ByteArrayOutputStream output;
        private String code;// 验证码
        private int codeNum;// 验证码字符数量
    
        private int width;
        private int height;
    
        // 构造器
        private AuthCode(int width, int height, int codeNum) {
            this.width = width;
            this.height = height;
            this.codeNum = codeNum;
    
            if (width < 15 * codeNum + 6) {
                this.width = 13 * codeNum + 6;
            }
            if (height < 20) {
                this.height = 20;
            }
    
            buildImage();
        }
    
        // 以字符串形式返回验证码
        public String getCode() {
            return code;
        }
    
        // 以输入流的形式返回验证图片
        public ByteArrayInputStream getIamgeAsInputStream() {
            return input;
        }
    
        // 以输出流的形式返回验证图片
        public ByteArrayOutputStream getImageAsOuputStream() {
            return output;
        }
    
        // 创建默认大小的验证码
        public static AuthCode createInstance() {
            return new AuthCode(85, 20, 4);
        }
    
        // 创建指定大小的验证码
        public static AuthCode createInstance(int width, int height, int codeNum) {
            return new AuthCode(width, height, codeNum);
        }
    
        // 生成验证码图片
        private void buildImage() {
    
            BufferedImage image = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            // 获取图形上下文
            Graphics g = image.getGraphics();
            // 生成随机类
            Random random = new Random();
            // 设定背景色
            g.setColor(getRandColor(200, 250));
            g.fillRect(0, 0, width, height);
            // 设定字体
            g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
            
            // 随机产生150条干扰线,使图象中的认证码不易被其它程序探测到
            g.setColor(getRandColor(160, 200));
            for (int i = 0; i < 150; i++) {
                int x = random.nextInt(width);
                int y = random.nextInt(height);
                int xl = random.nextInt(12);
                int yl = random.nextInt(12);
                g.drawLine(x, y, x + xl, y + yl);
            }
    
            // 取随机产生的认证码
            String codes = "ABCDEFGHJKLMNOPQRSTUVWXYZ23456789";
            String sRand = "";
            for (int i = 0; i < codeNum; i++) {
                String rand = codes.charAt(random.nextInt(codes.length())) + "";
                sRand += rand;
                
                // 将认证码显示到图象中
                g.setColor(new Color(20 + random.nextInt(110), 20 + random
                        .nextInt(110), 20 + random.nextInt(110)));
                
                // 将字符串绘制到图片上
                g.drawString(rand, i * (width / codeNum) + 6, (int)((height+12)/2));
            }
    
            /* 验证码赋值 */
            this.code = sRand;
            
            // 图象生效
            g.dispose();
    
            try {
                output = new ByteArrayOutputStream();
                ImageOutputStream imageOut = ImageIO
                        .createImageOutputStream(output);
                ImageIO.write(image, "JPEG", imageOut);
                imageOut.close();
                input = new ByteArrayInputStream(output.toByteArray());
            } catch (Exception e) {
                System.out.println("验证码图片产生出现错误:" + e.toString());
            }
    
        }
    
        // 获取随机颜色
        private Color getRandColor(int fc, int bc) {
            Random random = new Random();
            if (fc > 255)
                fc = 255;
            if (bc > 255)
                bc = 255;
    
            int r = fc + random.nextInt(bc - fc);
            int g = fc + random.nextInt(bc - fc);
            int b = fc + random.nextInt(bc - fc);
    
            return new Color(r, g, b);
        }
    
    }
    然后我们定义一个Servlet专门用于输出验证码:
    public class AuthCodeServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
    
            AuthCode code = AuthCode.createInstance();
    
            req.getSession().setAttribute("authCode", code.getCode());
    
            resp.getOutputStream()
                    .write(code.getImageAsOuputStream().toByteArray());
    
        }
    }

    index.jsp

    <html>
        <head>
            <title>Auth Code</title>
            <script type="text/javascript">
            function changeImage(obj){
                obj.src = "AuthCodeServlet?="+Math.random();
            }
            
        </script>
        </head>
    
        <body>
    
            <form action="LoginServlet" method="post">
                username:
                <input type="text" name="userName" />
                <br>
                password:
                <input type="password" name="password" />
                <br />
                authcode:
                <input type="text" name="authCode" />
                <img src="AuthCodeServlet" onclick="changeImage(this)" />
                <br>
                <input type="submit" value="submit" />
            </form>
    
    
        </body>
    </html>

    LoginServlet.java

    public class LoginServlet extends HttpServlet {
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
    
            String userName = req.getParameter("userName");
            String password = req.getParameter("password");
            String authCode = req.getParameter("authCode");
    
            String code = (String) req.getSession().getAttribute("authCode");
    
            if (authCode != null && authCode.toUpperCase().equals(code)
                    && "hello".equals(userName) && "world".equals(password)) {
                resp.getWriter().println("login success! authCode:"+authCode);
            }else{
                resp.getWriter().println("login failed! authCode:"+authCode);
            }
    
        }
    }

    测试:

    使用Struts2实现图片验证码

    Struts2中有一种名为”stream”类型的Result,我们在学习文件下载的时候已经学习过了。我们已经知道只要我们给它提供一个输入流,只需要设置好相应的ContentType,那么它就会自动将这个输入流输出到浏览器中。那么在这里我们依然可以利用它的这个特性。在上一个例子中的验证码生成类中,提供了一个获取输入流的方法,因此这里我还是使用上面的验证码生成类AuthCode。

    AuthCodeAction.java

    public class AuthCodeAction extends ActionSupport implements SessionAware {
    
        private Map<String, Object> session;
    
        public void setSession(Map<String, Object> session) {
            this.session = session;
        }
    
        public InputStream getInputStream() {
    
            AuthCode code = AuthCode.createInstance();
            session.put("authCode", code.getCode());
    
            return code.getIamgeAsInputStream();
    
        }
    
        @Override
        public String execute() throws Exception {
    
            return SUCCESS;
        }
    
    }

    LoginAction.java

    public class LoginAction extends ActionSupport implements SessionAware {
    
        private String userName;
        private String password;
        private String authCode;
    
        private Map<String, Object> session;
    
        public void setSession(Map<String, Object> session) {
            this.session = session;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getAuthCode() {
            return authCode;
        }
    
        public void setAuthCode(String authCode) {
            this.authCode = authCode;
        }
    
        public Map<String, Object> getSession() {
            return session;
        }
    
        @Override
        public String execute() throws Exception {
    
            String code = (String) session.get("authCode");
    
            if (authCode != null & authCode.toUpperCase().equals(code)) {
                return SUCCESS;
            }
            return INPUT;
        }
    
    }

    index.jsp基本保持不变,略了。

    struts.xml

    <package name="default"namespace="/"extends="struts-default">

                       <action name="authcode" class="action.AuthCodeAction">

                                <result name="success" type="stream">

                                         <param name="contentType">image/jpeg</param>

                                </result>

                       </action>

                       <action name="login" class="action.LoginAction">

                                <result>success.jsp</result>

                                <result name="input">index.jsp</result>

                       </action>

             </package>

    测试:

    验证码这块相对来说还算是比较简单。但是如果要做一些比较复杂的验证码的话还是要花不少心思的。这块本来跟Struts2没多大关系,不过既然刚好学到了,也就归如到Struts2中吧。

    如有不妥之处,还请路过的老鸟们指正!小弟感激不尽。



  • 相关阅读:
    hdu 5961 传递(暴力搜索)
    hdu 3577 Fast Arrangement(线段树区间修改,求区间最小值)
    hdu 5898 odd-even number(数位dp)
    Python-编码
    Golang-教程
    Python-待
    Python_每日习题_0006_斐波那契数列
    计算机网络
    Python_老男孩练习题1
    Python_内置函数2_44
  • 原文地址:https://www.cnblogs.com/jdluojing/p/3212423.html
Copyright © 2011-2022 走看看