0

    Web中如何生成验证码的方法

    2023.07.31 | admin | 155次围观

    验证码的生成,这个在Web登陆系统中,我像大家都很常见了,所以我这里简单的介绍两种方法来实现这样的操作。

    情况一:纯粹通过JSP来进行实现

    方法一:

    描述:纯粹通过JSP来完成验证码的实现

    验证码显示的JSP代码:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    
    
    
    
    验证码
    
    
    
    刷新
    
    

    验证码生成的JSP代码:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ page import="java.util.Random"%>
    <%@ page import="java.io.OutputStream"%>
    <%@ page import="java.awt.Color"%>
    <%@ page import="java.awt.Font"%>
    <%@ page import="java.awt.Graphics"%>
    <%@ page import="java.awt.image.BufferedImage"%>
    <%@ page import="javax.imageio.ImageIO"%>
    <%
    	//设置验证码宽度
    	int width = 60;
    	//设置验证码高度
    	int height = 32;
    	//创建一个image对象
    	BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    	//创建一个画板
    	Graphics g = image.getGraphics();
    	//设置背景颜色
    	g.setColor(new Color(0xDCDCDC));
    	//填充一个矩形
    	g.fillRect(0, 0, width, height);
    	//画矩形框的边框颜色
    	g.setColor(Color.black);
    	//绘制一个矩形
    	g.drawRect(0, 0, width - 1, height - 1);
    	//创建一个随机对象,用于获取产生随机验证码
    	Random rdm = new Random();
    	String hash1 = Integer.toHexString(rdm.nextInt());
    	System.out.print(hash1);
    	// 为了使验证码不易于区分,所以这里就产生50个随机的圆点,来进行迷惑
    	for (int i = 0; i < 50; i++) {
    		int x = rdm.nextInt(width);
    		int y = rdm.nextInt(height);
    		//绘制椭圆
    		g.drawOval(x, y, 0, 0);
    	}
    	// 截取产生的验证码的前四位(这个根据自己需要进行设置)
    	String capstr = hash1.substring(0, 4);
    	//将生成的验证码存入session
    	session.setAttribute("validateCode", capstr);
    	//绘制验证码的颜色
    	g.setColor(new Color(0, 100, 0));
    	//设置字体
    	g.setFont(new Font("Candara", Font.BOLD, 24));
    	g.drawString(capstr, 8, 24);
    	g.dispose();
    	//输出图片
    	response.setContentType("image/jpeg");
    	out.clear();
    	out = pageContext.pushBody();
    	OutputStream strm = response.getOutputStream();
    	ImageIO.write(image, "jpeg", strm);
    	strm.close();
    %>

    情况一:基于标准的Servlet的形式开发模式(非框架)

    方法二:

    Servlert代码:

    package hnu.scw.properce;
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.font.FontRenderContext;
    import java.awt.geom.Rectangle2D;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    /*
     * 演示验证码的生成到html中来进行显示 
     * 
     */
    import java.io.IOException;
    import java.util.Random;
    import javax.imageio.ImageIO;
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    public class ServletYanZhengMa extends HttpServlet {
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		ByteArrayOutputStream output = new ByteArrayOutputStream();
    		String code = drawImg(output);
                    //在这里还可以将验证码的信息保存到session中,方便进行登陆的时候判断是否正确
    		try {
    			ServletOutputStream out = response.getOutputStream();
    			output.writeTo(out);
    		} catch (IOException e) {
    			e.printStackTrace();
    		} 
    	}
    	
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		doGet(request, response);
    	}
    	
           //绘制验证码
    	private String drawImg(ByteArrayOutputStream output) {
    		String code = "";
    		for (int i = 0; i < 4; i++) {
    			code += randomChar();
    		}
    		int width = 70;
    		int height = 25;
    		BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    		Font font = new Font("Times New Roman", Font.PLAIN, 20);
    		Graphics2D g = bi.createGraphics();
    		g.setFont(font);
    		Color color = new Color(66, 2, 82);
    		g.setColor(color);
    		g.setBackground(new Color(226, 226, 240));
    		g.clearRect(0, 0, width, height);
    		FontRenderContext context = g.getFontRenderContext();
    		Rectangle2D bounds = font.getStringBounds(code, context);
    		double x = (width - bounds.getWidth()) / 2;
    		double y = (height - bounds.getHeight()) / 2;
    		double ascent = bounds.getY();
    		double baseY = y - ascent;
    		g.drawString(code, (int) x, (int) baseY);
    		g.dispose();
    		try {
    			ImageIO.write(bi, "jpg", output);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		return code;
    	}
            //生成四位数的验证码(这里就是在字母和数字之间产生,如果你需要更多的符号,那么就自己添加在String中就可以了哦)
    	private char randomChar() {
    		Random r = new Random();
    		String s = "ABCDEFGHJKLMNPRSTUVWXYZ0123456789";
    		return s.charAt(r.nextInt(s.length()));
    	}
    }

    Html代码:(页面简单点哈,能明白意思就可以了!!)

    
    
    
    
    Insert title here
    
    
    
       
    账 号

    密 码

    验证码    看不清楚,换一张

    备注:请注意我写的注释代码,这个地方非常非常重要!!!!!!!!!!!!!!!

    方法三:(类似方法一,但是生成的方式有所变化,可以根据需要进行选择)

    Servlert代码:

    package hnu.scw.properce;
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.image.BufferedImage;
    /*
     * 演示验证码的生成到html中来进行显示 
     * 
     */
    import java.io.IOException;
    import java.util.Random;
    import javax.imageio.ImageIO;
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    public class ServletYanZhengMa extends HttpServlet {
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		 getYanZhengMa(response);    //调用方法
    	      
    	}
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		doGet(request, response);
    	}
    	
    	/*
    	 * 生成验证码
    	 */
    	private void getYanZhengMa(HttpServletResponse response) {
    		//设置一下颜色数组,为了验证码更加难辨认
    		Color[] yanse = {Color.RED ,Color.BLACK ,Color.GREEN , Color.BLUE};
    		
    		//初始化验证码的宽和高
    		int width = 100 ;
    		int height = 60;
    		//初始化图片缓冲器
    		BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) ;
    		//初始化画笔
    		Graphics gp  = bi.getGraphics(); //这里是用图片来进行初始化,否则不知道是画在哪里
    		//设置画笔颜色
    		gp.setColor(Color.RED); 
     		//画一个矩形的外框
    		gp.drawRect(0, 0, width, height);
    		//设置矩形填充的颜色
    		gp.setColor(Color.YELLOW);
    		//填充背景(下面是从1,1开始填充,如果从0,0开始,那么外框也会被修改了,后面-2也是同理)
    		gp.fillRect(1, 1, width-2, height-2);
    		//生成要填充的内容
    		Random random  = new Random();
    		//设置验证码的干扰线(这是为了效果来进行开发的,可以没有)
    		gp.setColor(Color.BLUE);
    		//画30条干扰线
    		for(int i=0 ; i<30 ;i++){
    			gp.drawLine(random.nextInt(width), random.nextInt(height), random.nextInt(width), random.nextInt(height));
    		    //下面的是画点
    			//gp.drawOval(random.nextInt(width), random.nextInt(height), 2, 2);
    		}
    		//设置填充的内容的颜色
    		gp.setColor(Color.RED);
    		//设置填充内容的大小
    		gp.setFont(new Font("幼圆", Font.BOLD + Font.ITALIC, 20));
    		//如果填充的内容要为中文的时候,需要这样处理
    		String content ="全国我觉得嘎斯的获奖情况大手笔的吧区隔化akshldeqw";
    		//要把上面的转为unicode编码,因为如果浏览器不支持中文的话就会出现乱码,如果在国内的话则没有什么影响
    		//用在线工具转也可以,但最好用代码来进行转变,因为以后开发肯定是动态的一种方式最好
    		//content ="\u5168\u56fd\u6211\u89c9\u5f97\u560e\u65af\u7684\u83b7\u5956\u60c5\u51b5\u5927\u624b\u7b14\u7684\u5427\u533a\u9694\u5316\u0061\u006b\u0073\u0068\u006c\u0064\u0065\u0071\u0077";
    		for(int i =0 ; i < 4 ;i++){
    			//gp.setColor(yanse[random.nextInt(3)]);设置填充的颜色随即化(为了验证码更复杂)
    			char c =content.charAt(random.nextInt(content.length()));
    			gp.drawString(c + "", 20 + 20*i, 30); //开始填充内容和位置
    		}
    		
    		//如果想要验证码是数字的,则填充的内容为数字(生成四个数字)
    //		for(int i=0 ; i<4 ;i++){
    //			int number = random.nextInt(10);  //生成10以内的数字
    //			gp.drawString(number + "", 20 + 20*i, 30); //开始填充内容和位置
    //		}
    		
    		//设置不要缓存
    		response.setHeader("Expires", -1+"");
    		response.setHeader("Cache-control", "no-cache");
    		response.setHeader("Pragma", "no-cache");
    		
    		//将验证码显示到对应的位置
    		try {
    			ImageIO.write(bi, "jpg", response.getOutputStream());
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }

    Html代码:

    
    
    
    
    Insert title here
    
    
    
       
    账 号

    密 码

    验证码    看不清楚,换一张

    总结:上面就是三种方法了,是不是很简单,其实不是想象的那么难。。当然,我这没有把登陆写进去,这个自己添加就可以了,只是注意一点就是在生成验证码的时候,要把验证码的字符保存到session中,否则在登陆判断验证码是否正确就没什么好方法啦。。。当然,你有其他的解决办法都可以哦~~

    下面利用一个相对完整的Demo来进行演示(包含了登陆的判断了哦!!!)

    情况二;基于SpringMVC框架

    生成验证码的controller类的代码:

    package com.mbfw.controller.system.secCode;
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Graphics2D;
    import java.awt.font.FontRenderContext;
    import java.awt.geom.Rectangle2D;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.util.Random;
    import javax.imageio.ImageIO;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import com.mbfw.util.Const;
    /**
     * 类名称:SecCodeController 作者单位:
     * 
     * @version
     */
    @Controller
    @RequestMapping("/code")
    public class SecCodeController {
    	@RequestMapping
    	public void generate(HttpServletResponse response) {
    		ByteArrayOutputStream output = new ByteArrayOutputStream();
    		String code = drawImg(output);
    		Subject currentUser = SecurityUtils.getSubject();
    		Session session = currentUser.getSession();
    		session.setAttribute(Const.SESSION_SECURITY_CODE, code);
    		try {
    			ServletOutputStream out = response.getOutputStream();
    			output.writeTo(out);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    	private String drawImg(ByteArrayOutputStream output) {
    		String code = "";
    		for (int i = 0; i < 4; i++) {
    			code += randomChar();
    		}
    		int width = 70;
    		int height = 25;
    		BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    		Font font = new Font("Times New Roman", Font.PLAIN, 20);
    		Graphics2D g = bi.createGraphics();
    		g.setFont(font);
    		Color color = new Color(66, 2, 82);
    		g.setColor(color);
    		g.setBackground(new Color(226, 226, 240));
    		g.clearRect(0, 0, width, height);
    		FontRenderContext context = g.getFontRenderContext();
    		Rectangle2D bounds = font.getStringBounds(code, context);
    		double x = (width - bounds.getWidth()) / 2;
    		double y = (height - bounds.getHeight()) / 2;
    		double ascent = bounds.getY();
    		double baseY = y - ascent;
    		g.drawString(code, (int) x, (int) baseY);
    		g.dispose();
    		try {
    			ImageIO.write(bi, "jpg", output);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		return code;
    	}
    	private char randomChar() {
    		Random r = new Random();
    		String s = "ABCDEFGHJKLMNPRSTUVWXYZ0123456789";
    		return s.charAt(r.nextInt(s.length()));
    	}
    }

    备注:其实这个和在标准的Servlet中的形式是一样的,只是用了SpringMVC的模式来编写而已,所以知道Servlet的方法,用其他的框架来写都会了!!!

    jsp代码:(这里写个比较完整的)

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    	pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%
    	String path = request.getContextPath();
    %>
    
    
    
    ${pd.SYSNAME}
    
    
    
    
    
    
    
    
    
    
    	

    记住密码

    备注:这里面的css和JS的引用,我没有写出来了,都是比较简单的,主要是用了jQuery还有Bootstap这两个插件的内容。如果只是想测试的话,只是样式不好看而已,都是次要的,关键在于功能可以就好了哈!!!

    验证登陆的controller层代码(用shiro框架的权限认证方法,但是可以省略,根据需要进行自己修改):

    /**
    	 * 请求登录,验证用户
    	 */
    	@RequestMapping(value = "/login_login", produces = "application/json;charset=UTF-8")
    	@ResponseBody
    	public Object login() throws Exception {
    		Map map = new HashMap();
    		PageData pd = new PageData();
    		pd = this.getPageData();
    		
    		String errInfo = "";
    		String KEYDATA[] = pd.getString("KEYDATA").replaceAll("qq123456789mbfw", "").replaceAll("QQ123456789mbfw", "").split(",mbfw,");
    		boolean ismobile = false;
    		if(pd.getString("ismobile")!=null && pd.getString("ismobile").toString().equals("true"))
    			ismobile = true;
    		
    		if (null != KEYDATA && KEYDATA.length == 3) {
    			// shiro管理的session
    			Subject currentUser = SecurityUtils.getSubject();
    			Session session = currentUser.getSession();
    			String sessionCode = (String) session.getAttribute(Const.SESSION_SECURITY_CODE); // 获取session中的验证码
    			String code = KEYDATA[2];
    			if (null == code || "".equals(code)) {
    				errInfo = "nullcode"; // 验证码为空
    			} else {
    				String USERNAME = KEYDATA[0];
    				String PASSWORD = KEYDATA[1];
    				pd.put("USERNAME", USERNAME);
    				if ((Tools.notEmpty(sessionCode) && sessionCode.equalsIgnoreCase(code)) || ismobile) {
    					String passwd = new SimpleHash("SHA-1", USERNAME, PASSWORD).toString(); // 密码加密
    					pd.put("PASSWORD", passwd);
    					pd = userService.getUserByNameAndPwd(pd);
    					if (pd != null) {
    						pd.put("LAST_LOGIN", DateUtil.getTime().toString());
    						userService.updateLastLogin(pd);
    						User user = new User();
    						user.setUSER_ID(pd.getString("USER_ID"));
    						user.setUSERNAME(pd.getString("USERNAME"));
    						user.setPASSWORD(pd.getString("PASSWORD"));
    						user.setNAME(pd.getString("NAME"));
    						user.setRIGHTS(pd.getString("RIGHTS"));
    						user.setROLE_ID(pd.getString("ROLE_ID"));
    						user.setLAST_LOGIN(pd.getString("LAST_LOGIN"));
    						user.setIP(pd.getString("IP"));
    						user.setSTATUS(pd.getString("STATUS"));
    						user.setUser_Permission((Integer)pd.get("user_Permission"));
    						user.setSuperior_organization_name(pd.getString("superior_organization_name"));
    						user.setOrganization_name(pd.getString("organization_name"));
    						session.setAttribute(Const.SESSION_USER, user);
    						session.setAttribute(Const.IS_MOBILE, ""+ismobile);
    						session.removeAttribute(Const.SESSION_SECURITY_CODE);
    						// shiro加入身份验证
    						Subject subject = SecurityUtils.getSubject();
    						UsernamePasswordToken token = new UsernamePasswordToken(USERNAME, PASSWORD);
    						try {
    							subject.login(token);
    						} catch (AuthenticationException e) {
    							errInfo = "身份验证失败!";
    						}
    					} else {
    						errInfo = "usererror"; // 用户名或密码有误
    					}
    				} else {
    					errInfo = "codeerror"; // 验证码输入有误
    				}
    				if (Tools.isEmpty(errInfo)) {
    					errInfo = "success"; // 验证成功
    				}
    			}
    		} else {
    			errInfo = "error"; // 缺少参数
    		}
    		map.put("result", errInfo);
    		return AppUtil.returnObject(new PageData(), map);
    	}

    验证通过后,显示系统的主界面controller层代码:

    @RequestMapping(value = "/main/index")
    	public ModelAndView login_index() {
    		ModelAndView mv = new ModelAndView();
    		mv.setViewName("jsp/login_success.jsp");
    		return mv;
    	}

    登陆成功后显示的界面html代码----login_success.jsp

    
    
    
    
    Insert title here
    
    
      

    欢迎你

    总结:好了,上面三种方法应该能够满足需要验证码的这个需求了,并且也给了一个完整的Demo进行演示。总的来说不是很难验证码背景图片下载,但是想着用得多,所以我这就写篇文章来记录一下好了。如果有更好的方法验证码背景图片下载,可以留言的哦!!我都会关注的啦。。大家一起。。加油加油。。

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    标签: web开发
    发表评论