shiro 和spring集合 实现登录时输入验证码并校验(七)

编写实现验证码的主体实现类:CaptchaCode

  1 import java.util.UUID;
  2 
  3 import javax.servlet.http.HttpServletRequest;
  4 import javax.servlet.http.HttpServletResponse;
  5 
  6 import org.apache.commons.lang3.StringUtils;
  7 import org.apache.shiro.cache.Cache;
  8 import org.apache.shiro.cache.CacheManager;
  9 import org.slf4j.Logger;
 10 import org.slf4j.LoggerFactory;
 11 import org.springframework.beans.factory.InitializingBean;
 12 import org.springframework.util.Assert;
 13 
 14 import com.itzixi.common.utils.CookieUtils;
 15 
 16 
 17 /**
 18  * 
 19  * @Title: CaptchaCode.java
 20  * @Description: 验证码实现类
 21  * @date 2017年10月14日 下午12:11:53
 22  * @version V1.0
 23  */
 24 public class CaptchaCode implements InitializingBean {
 25     private final static Logger logger = LoggerFactory.getLogger(CaptchaCode.class);
 26     private static final String DEFAULT_COOKIE_NAME = "itzixi-captcha";
 27     private final static String DEFAULT_CHACHE_NAME = "itzixiCaptchaCache";
 28     private final static int DEFAULT_MAX_AGE = -1; // cookie超时默认为session会话状态
 29     
 30     private CacheManager cacheManager;
 31     private String cacheName;
 32     private String cookieName;
 33     
 34     private Cache<String, String> itzixiCaptchaCache;
 35     
 36     public CaptchaCode() {
 37         this.cacheName = DEFAULT_CHACHE_NAME;
 38         this.cookieName = DEFAULT_COOKIE_NAME;
 39     }
 40     
 41     public CaptchaCode(CacheManager cacheManager) {
 42         this();
 43         this.cacheManager = cacheManager;
 44     }
 45     
 46     public CacheManager getCacheManager() {
 47         return cacheManager;
 48     }
 49     
 50     public void setCacheManager(CacheManager cacheManager) {
 51         this.cacheManager = cacheManager;
 52     }
 53     
 54     public String getCacheName() {
 55         return cacheName;
 56     }
 57     
 58     public void setCacheName(String cacheName) {
 59         this.cacheName = cacheName;
 60     }
 61     
 62     public String getCookieName() {
 63         return cookieName;
 64     }
 65 
 66     public void setCookieName(String cookieName) {
 67         this.cookieName = cookieName;
 68     }
 69     
 70     @Override
 71     public void afterPropertiesSet() throws Exception {
 72         Assert.notNull(cacheManager, "cacheManager must not be null!");
 73         Assert.hasText(cacheName, "cacheName must not be empty!");
 74         Assert.hasText(cookieName, "cookieName must not be empty!");
 75         this.itzixiCaptchaCache = cacheManager.getCache(cacheName);
 76     }
 77     
 78     /**
 79      * 生成验证码
 80      */
 81     public void generate(HttpServletRequest request, HttpServletResponse response) {
 82         // 先检查cookie的uuid是否存在
 83         String cookieValue = CookieUtils.getCookieValue(request, cookieName);
 84         boolean hasCookie = true;
 85         if (StringUtils.isBlank(cookieValue)) {
 86             hasCookie = false;
 87             cookieValue = UUID.randomUUID().toString();
 88         }
 89         String captchaCode = CaptchaUtils.generateCode().toUpperCase();// 转成大写重要
 90         // 不存在cookie时设置cookie
 91         if (!hasCookie) {
 92             CookieUtils.setCookie(request, response, cookieName, cookieValue, DEFAULT_MAX_AGE);
 93         }
 94         // 生成验证码
 95         CaptchaUtils.generate(response, captchaCode);
 96         itzixiCaptchaCache.put(cookieValue, captchaCode);
 97     }
 98     
 99     /**
100      * 仅能验证一次,验证后立即删除
101      * @param request HttpServletRequest
102      * @param response HttpServletResponse
103      * @param userInputCaptcha 用户输入的验证码
104      * @return 验证通过返回 true, 否则返回 false
105      */
106     public boolean validate(HttpServletRequest request, HttpServletResponse response, String userInputCaptcha) {
107         if (logger.isDebugEnabled()) {
108             logger.debug("validate captcha userInputCaptcha is " + userInputCaptcha);
109         }
110         String cookieValue = CookieUtils.getCookieValue(request, cookieName);
111         if (StringUtils.isBlank(cookieValue)) {
112             return false;
113         }
114         String captchaCode = itzixiCaptchaCache.get(cookieValue);
115         if (StringUtils.isBlank(captchaCode)) {
116             return false;
117         }
118         // 转成大写重要
119         userInputCaptcha = userInputCaptcha.toUpperCase();
120         boolean result = userInputCaptcha.equals(captchaCode);
121         if (result) {
122             itzixiCaptchaCache.remove(cookieValue);
123             CookieUtils.deleteCookie(request, response, cookieName);
124         }
125         return result;
126     }
127 }
CaptchaUtils.java
  1 import java.awt.BasicStroke;
  2 import java.awt.Color;
  3 import java.awt.Font;
  4 import java.awt.Graphics2D;
  5 import java.awt.RenderingHints;
  6 import java.awt.geom.QuadCurve2D;
  7 import java.awt.image.BufferedImage;
  8 import java.util.Random;
  9 
 10 import javax.imageio.ImageIO;
 11 import javax.servlet.ServletOutputStream;
 12 import javax.servlet.http.HttpServletResponse;
 13 
 14 /**
 15  * 
 16  * @Title: CaptchaUtils.java
 17  * @Description: 验证码工具类
 18  * @date 2017年10月14日 下午12:13:14
 19  * @version V1.0
 20  */
 21 class CaptchaUtils {
 22     // 默认的验证码大小
 23     private static final int WIDTH = 108, HEIGHT = 40, CODE_SIZE = 4;
 24     // 验证码随机字符数组
 25     protected static final char[] charArray = "3456789ABCDEFGHJKMNPQRSTUVWXY".toCharArray();
 26     // 验证码字体
 27     private static final Font[] RANDOM_FONT = new Font[] {
 28         new Font("nyala", Font.BOLD, 38),
 29         new Font("Arial", Font.BOLD, 32),
 30         new Font("Bell MT", Font.BOLD, 32),
 31         new Font("Credit valley", Font.BOLD, 34),
 32         new Font("Impact", Font.BOLD, 32),
 33         new Font(Font.MONOSPACED, Font.BOLD, 40)
 34     };
 35     
 36     /**
 37      * 生成验证码
 38      */
 39     static void generate(HttpServletResponse response, String vCode) {
 40         BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
 41         response.setHeader("Pragma","no-cache");
 42         response.setHeader("Cache-Control","no-cache");
 43         response.setDateHeader("Expires", 0);
 44         response.setContentType("image/jpeg");
 45         
 46         ServletOutputStream sos = null;
 47         try {
 48             drawGraphic(image, vCode);
 49             sos = response.getOutputStream();
 50             ImageIO.write(image, "JPEG", sos);
 51             sos.flush();
 52         } catch (Exception e) {
 53             throw new RuntimeException(e);
 54         } finally {
 55             IOUtils.closeQuietly(sos);
 56         }
 57     }
 58     
 59     // 生成随机类
 60     private static final Random RANDOM = new Random();
 61     
 62     /**
 63      * 生成验证码字符串
 64      * @return 验证码字符串
 65      */
 66     static String generateCode() {
 67         int count = CODE_SIZE;
 68         char[] buffer = new char[count];
 69         for (int i = 0; i < count; i++) {
 70             buffer[i] = charArray[RANDOM.nextInt(charArray.length)];
 71         }
 72         return new String(buffer);
 73     }
 74     
 75     private static void drawGraphic(BufferedImage image, String code){
 76         // 获取图形上下文
 77         Graphics2D g = image.createGraphics();
 78         g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
 79         // 图形抗锯齿
 80         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 81         // 字体抗锯齿
 82         g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 83         
 84         // 设定背景色,淡色
 85         g.setColor(getRandColor(210, 250));
 86         g.fillRect(0, 0, WIDTH, HEIGHT);
 87         
 88         // 画小字符背景
 89         Color color = null;
 90         for(int i = 0; i < 20; i++){
 91             color = getRandColor(120, 200);
 92             g.setColor(color);
 93             String rand = String.valueOf(charArray[RANDOM.nextInt(charArray.length)]);
 94             g.drawString(rand, RANDOM.nextInt(WIDTH), RANDOM.nextInt(HEIGHT));
 95             color = null;
 96         }
 97         // 取随机产生的认证码(4位数字)
 98         char[] buffer = code.toCharArray();
 99         for (int i = 0; i < buffer.length; i++){
100             char _code = buffer[i];
101             //旋转度数 最好小于45度
102             int degree = RANDOM.nextInt(28);
103             if (i % 2 == 0) {
104                 degree = degree * (-1);
105             }
106             //定义坐标
107             int x = 22 * i, y = 21;
108             //旋转区域
109             g.rotate(Math.toRadians(degree), x, y);
110             //设定字体颜色
111             color = getRandColor(20, 130);
112             g.setColor(color);
113             //设定字体,每次随机
114             g.setFont(RANDOM_FONT[RANDOM.nextInt(RANDOM_FONT.length)]);
115             //将认证码显示到图象中
116             g.drawString("" + _code, x + 8, y + 10);
117             //旋转之后,必须旋转回来
118             g.rotate(-Math.toRadians(degree), x, y);
119         }
120         //图片中间曲线,使用上面缓存的color
121         g.setColor(color);
122         //width是线宽,float型
123         BasicStroke bs = new BasicStroke(3);
124         g.setStroke(bs);
125         //画出曲线
126         QuadCurve2D.Double curve = new QuadCurve2D.Double(0d, RANDOM.nextInt(HEIGHT - 8) + 4, WIDTH / 2, HEIGHT / 2, WIDTH, RANDOM.nextInt(HEIGHT - 8) + 4);
127         g.draw(curve);
128         // 销毁图像
129         g.dispose();
130     }
131 
132     /**
133      * 给定范围获得随机颜色
134      */
135     private static Color getRandColor(int fc, int bc) {
136         if (fc > 255)
137             fc = 255;
138         if (bc > 255)
139             bc = 255;
140         int r = fc + RANDOM.nextInt(bc - fc);
141         int g = fc + RANDOM.nextInt(bc - fc);
142         int b = fc + RANDOM.nextInt(bc - fc);
143         return new Color(r, g, b);
144     }
145 }
IOUtils.java
 1 import java.io.Closeable;
 2 import java.io.IOException;
 3 
 4 /**
 5  * 
 6  * @Title: IOUtils.java
 7  * @Description: 流工具类,继承自Spring
 8  * @date 2017年10月14日 下午12:13:04
 9  * @version V1.0
10  */
11 public class IOUtils extends org.springframework.util.StreamUtils {
12 
13     /**
14      * closeQuietly
15      * @param closeable 自动关闭
16      */
17     public static void closeQuietly(Closeable closeable) {
18         try {
19             if (closeable != null) {
20                 closeable.close();
21             }
22         } catch (IOException ioe) {
23             // ignore
24         }
25     }
26 }

2、增加Bean的配置文件 在spring配置文件中增加:并采用缓存redis实现验证码的缓存存储

1 <!--此Bean 只是增加登录时的验证码处理,不是shiro必须的配置-->
2     <bean class="com.itzixi.web.utils.CaptchaCode">
3         <!--<property name="cacheManager" ref="shiroEhcacheManager"/>-->
4         <property name="cacheManager" ref="shiroRedisCacheManager"></property>
5         <!-- 复用半小时缓存 -->
6         <property name="cacheName" value="cacheCaptcha"/>
7     </bean>

3、增加前端代码实现:

 1  <div class="form-group">
 2             <label class="control-label visible-ie8 visible-ie9">密码</label>
 3             <div id="input-error">
 4                 <input class="form-control form-control-solid placeholder-no-fix form-group" type="text"
 5                        autocomplete="off" placeholder="验证码" name="captcha" required/>
 6                 <a>
 7                     <img id="captcha" alt="验证码" src="<%=request.getContextPath() %>/captcha.action"
 8                          data-src="<%=request.getContextPath() %>/captcha.action?time="
 9                          style="94.5px;height:35px;"/>
10                 </a>
11             </div>
12         </div>

在Controller增加/captcha.action 服务地址方法,调用验证码生成类进行验证码生成并加入到缓存中去 

4、在Controller层增加代码实现 接收,验证动作,如:CenterController.doPostlogin(....)

 1  @RequestMapping(value = "/login", method = RequestMethod.POST)
 2     @ResponseBody
 3     public LeeJSONResult doPostlogin(String username, String password, String captcha,
 4                                      @RequestParam(value = "isRememberMe", defaultValue = "0") Integer isRememberMe,
 5                                      HttpServletRequest request, HttpServletResponse response) {
 6 
 7         if (StringUtils.isBlank(username)) {
 8             return LeeJSONResult.errorMsg("用户名不能为空");
 9         }
10         if (StringUtils.isBlank(password)) {
11             return LeeJSONResult.errorMsg("密码不能为空");
12         }
13         if (!captchaCode.validate(request, response, captcha)) {
14             return LeeJSONResult.errorMsg("验证码错误, 请重新输入...");
15         }
16       return LeeJSONResult.ok();
17   }
原文地址:https://www.cnblogs.com/yinfengjiujian/p/9086907.html