服务器之家

服务器之家 > 正文

Java中SSM+Shiro系统登录验证码的实现方法

时间:2020-08-10 19:09     来源/作者:Lovnx

 先给大家展示下效果图:

Java中SSM+Shiro系统登录验证码的实现方法

1、验证码生成类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
import java.util.Random;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Color;
/**
 * 验证码生成器类,可生成数字、大写、小写字母及三者混合类型的验证码。 支持自定义验证码字符数量; 支持自定义验证码图片的大小; 支持自定义需排除的特殊字符;
 * 支持自定义干扰线的数量; 支持自定义验证码图文颜色
 */
public class ValidateCode {
 /**
  * 验证码类型为仅数字 0~9
  */
 public static final int TYPE_NUM_ONLY = 0;
 /**
  * 验证码类型为仅字母,即大写、小写字母混合
  */
 public static final int TYPE_LETTER_ONLY = 1;
 /**
  * 验证码类型为数字、大写字母、小写字母混合
  */
 public static final int TYPE_ALL_MIXED = 2;
 /**
  * 验证码类型为数字、大写字母混合
  */
 public static final int TYPE_NUM_UPPER = 3;
 /**
  * 验证码类型为数字、小写字母混合
  */
 public static final int TYPE_NUM_LOWER = 4;
 /**
  * 验证码类型为仅大写字母
  */
 public static final int TYPE_UPPER_ONLY = 5;
 /**
  * 验证码类型为仅小写字母
  */
 public static final int TYPE_LOWER_ONLY = 6;
 private ValidateCode() {
 }
 /**
  * 生成验证码字符串
  *
  * @param type
  *   验证码类型,参见本类的静态属性
  * @param length
  *   验证码长度,大于0的整数
  * @param exChars
  *   需排除的特殊字符(仅对数字、字母混合型验证码有效,无需排除则为null)
  * @return 验证码字符串
  */
 public static String generateTextCode(int type, int length, String exChars) {
  if (length <= 0)
   return "";
  StringBuffer code = new StringBuffer();
  int i = 0;
  Random r = new Random();
  switch (type) {
  // 仅数字
  case TYPE_NUM_ONLY:
   while (i < length) {
    int t = r.nextInt(10);
    if (exChars == null || exChars.indexOf(t + "") < 0) {// 排除特殊字符
     code.append(t);
     i++;
    }
   }
   break;
  // 仅字母(即大写字母、小写字母混合)
  case TYPE_LETTER_ONLY:
   while (i < length) {
    int t = r.nextInt(123);
    if ((t >= 97 || (t >= 65 && t <= 90)) && (exChars == null || exChars.indexOf((char) t) < 0)) {
     code.append((char) t);
     i++;
    }
   }
   break;
  // 数字、大写字母、小写字母混合
  case TYPE_ALL_MIXED:
   while (i < length) {
    int t = r.nextInt(123);
    if ((t >= 97 || (t >= 65 && t <= 90) || (t >= 48 && t <= 57))
      && (exChars == null || exChars.indexOf((char) t) < 0)) {
     code.append((char) t);
     i++;
    }
   }
   break;
  // 数字、大写字母混合
  case TYPE_NUM_UPPER:
   while (i < length) {
    int t = r.nextInt(91);
    if ((t >= 65 || (t >= 48 && t <= 57)) && (exChars == null || exChars.indexOf((char) t) < 0)) {
     code.append((char) t);
     i++;
    }
   }
   break;
  // 数字、小写字母混合
  case TYPE_NUM_LOWER:
   while (i < length) {
    int t = r.nextInt(123);
    if ((t >= 97 || (t >= 48 && t <= 57)) && (exChars == null || exChars.indexOf((char) t) < 0)) {
     code.append((char) t);
     i++;
    }
   }
   break;
  // 仅大写字母
  case TYPE_UPPER_ONLY:
   while (i < length) {
    int t = r.nextInt(91);
    if ((t >= 65) && (exChars == null || exChars.indexOf((char) t) < 0)) {
     code.append((char) t);
     i++;
    }
   }
   break;
  // 仅小写字母
  case TYPE_LOWER_ONLY:
   while (i < length) {
    int t = r.nextInt(123);
    if ((t >= 97) && (exChars == null || exChars.indexOf((char) t) < 0)) {
     code.append((char) t);
     i++;
    }
   }
   break;
  }
  return code.toString();
 }
 /**
  * 已有验证码,生成验证码图片
  *
  * @param textCode
  *   文本验证码
  * @param width
  *   图片宽度
  * @param height
  *   图片高度
  * @param interLine
  *   图片中干扰线的条数
  * @param randomLocation
  *   每个字符的高低位置是否随机
  * @param backColor
  *   图片颜色,若为null,则采用随机颜色
  * @param foreColor
  *   字体颜色,若为null,则采用随机颜色
  * @param lineColor
  *   干扰线颜色,若为null,则采用随机颜色
  * @return 图片缓存对象
  */
 public static BufferedImage generateImageCode(String textCode, int width, int height, int interLine,
   boolean randomLocation, Color backColor, Color foreColor, Color lineColor) {
  BufferedImage bim = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
  Graphics g = bim.getGraphics();
  // 画背景图
  g.setColor(backColor == null ? getRandomColor() : backColor);
  g.fillRect(0, 0, width, height);
  // 画干扰线
  Random r = new Random();
  if (interLine > 0) {
   int x = 0, y = 0, x1 = width, y1 = 0;
   for (int i = 0; i < interLine; i++) {
    g.setColor(lineColor == null ? getRandomColor() : lineColor);
    y = r.nextInt(height);
    y1 = r.nextInt(height);
    g.drawLine(x, y, x1, y1);
   }
  }
  // 写验证码
  // g.setColor(getRandomColor());
  // g.setColor(isSimpleColor?Color.BLACK:Color.WHITE);
  // 字体大小为图片高度的80%
  int fsize = (int) (height * 0.8);
  int fx = height - fsize;
  int fy = fsize;
  g.setFont(new Font("Default", Font.PLAIN, fsize));
  // 写验证码字符
  for (int i = 0; i < textCode.length(); i++) {
   fy = randomLocation ? (int) ((Math.random() * 0.3 + 0.6) * height) : fy;// 每个字符高低是否随机
   g.setColor(foreColor == null ? getRandomColor() : foreColor);
   g.drawString(textCode.charAt(i) + "", fx, fy);
   fx += fsize * 0.9;
  }
  g.dispose();
  return bim;
 }
 /**
  * 生成图片验证码
  *
  * @param type
  *   验证码类型,参见本类的静态属性
  * @param length
  *   验证码字符长度,大于0的整数
  * @param exChars
  *   需排除的特殊字符
  * @param width
  *   图片宽度
  * @param height
  *   图片高度
  * @param interLine
  *   图片中干扰线的条数
  * @param randomLocation
  *   每个字符的高低位置是否随机
  * @param backColor
  *   图片颜色,若为null,则采用随机颜色
  * @param foreColor
  *   字体颜色,若为null,则采用随机颜色
  * @param lineColor
  *   干扰线颜色,若为null,则采用随机颜色
  * @return 图片缓存对象
  */
 public static BufferedImage generateImageCode(int type, int length, String exChars, int width, int height,
   int interLine, boolean randomLocation, Color backColor, Color foreColor, Color lineColor) {
  String textCode = generateTextCode(type, length, exChars);
  BufferedImage bim = generateImageCode(textCode, width, height, interLine, randomLocation, backColor, foreColor,
    lineColor);
  return bim;
 }
 /**
  * 产生随机颜色
  *
  * @return
  */
 private static Color getRandomColor() {
  Random r = new Random();
  Color c = new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255));
  return c;
 }
}

2、Controller

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
 * 生成验证码
 * @param request
 * @param response
 * @throws IOException
 * @ValidateCode.generateTextCode(验证码字符类型,验证码长度,需排除的特殊字符)
 * @ValidateCode.generateImageCode(文本验证码,图片宽度,图片高度,干扰线的条数,字符的高低位置是否随机,图片颜色,字体颜色,干扰线颜色)
 */
@RequestMapping(value = "validateCode")
public void validateCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
 response.setHeader("Cache-Control", "no-cache");
 String verifyCode = ValidateCode.generateTextCode(ValidateCode.TYPE_NUM_LOWER, 4, null);
 request.getSession().setAttribute("validateCode", verifyCode);
 response.setContentType("image/jpeg");
 BufferedImage bim = ValidateCode.generateImageCode(verifyCode, 90, 30, 5, true, Color.WHITE, Color.BLUE, null);
 ImageIO.write(bim, "JPEG", response.getOutputStream());
}
/**
 * 登录请求
 * @param
 */
@RequestMapping(value = "login", method = RequestMethod.POST, produces = "text/html; charset=utf-8")
public String login(HttpServletRequest request, HttpServletResponse response, UserEntity user) {
 //首先进行验证码验证
 Session session = SecurityUtils.getSubject().getSession();
 String code = (String) session.getAttribute("validateCode");
 String submitCode = WebUtils.getCleanParam(request, "validateCode");
 if (StringUtils.isEmpty(submitCode) || !StringUtils.equals(code,submitCode.toLowerCase())) {
  request.setAttribute("LOGIN_ERROR_CODE", LoginConstant.LOGIN_ERROR_CODE_100000);
  request.setAttribute("LOGIN_ERROR_MESSAGE", LoginConstant.LOGIN_ERROR_MESSAGE_VALIDATECODE);
  return "login";
 }
 // 想要得到 SecurityUtils.getSubject() 的对象..访问地址必须跟shiro的拦截地址内.不然后会报空指针
 Subject sub = SecurityUtils.getSubject();
 // 用户输入的账号和密码,,存到UsernamePasswordToken对象中..然后由shiro内部认证对比,
 // 认证执行者交由ShiroDbRealm中doGetAuthenticationInfo处理
 // 当以上认证成功后会向下执行,认证失败会抛出异常
 UsernamePasswordToken token = new UsernamePasswordToken(user.getAccountName(), user.getPassWord());
 try {
  sub.login(token);
 } catch (LockedAccountException lae) {
  token.clear();
  request.setAttribute("LOGIN_ERROR_CODE", LoginConstant.LOGIN_ERROR_CODE_100002);
  request.setAttribute("LOGIN_ERROR_MESSAGE", LoginConstant.LOGIN_ERROR_MESSAGE_SYSTEMERROR);
  return "login";
 } catch (ExcessiveAttemptsException e) {
  token.clear();
  request.setAttribute("LOGIN_ERROR_CODE", LoginConstant.LOGIN_ERROR_CODE_100003);
  request.setAttribute("LOGIN_ERROR_MESSAGE","账号:" + user.getUserName() + LoginConstant.LOGIN_ERROR_MESSAGE_MAXERROR);
  return "login";
 } catch (AuthenticationException e) {
  token.clear();
  request.setAttribute("LOGIN_ERROR_CODE", LoginConstant.LOGIN_ERROR_CODE_100001);
  request.setAttribute("LOGIN_ERROR_MESSAGE", LoginConstant.LOGIN_ERROR_MESSAGE_USERERROR);
  return "login";
 }
 return "redirect:/index.shtml";
}

注意:

登录方法里面一些参数的定义:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface LoginConstant
{
 String LOGIN_ERROR_CODE_100000 = "100000";
 String LOGIN_ERROR_MESSAGE_VALIDATECODE = "验证码输入错误,请重新输入!";
 String LOGIN_ERROR_CODE_100001 = "100001";
 String LOGIN_ERROR_MESSAGE_USERERROR = "账号或密码错误,请重新输入!";
 String LOGIN_ERROR_CODE_100002 = "100002";
 String LOGIN_ERROR_MESSAGE_SYSTEMERROR = "用户已经被锁定不能登录,请与管理员联系!";
 String LOGIN_ERROR_CODE_100003 = "100003";
 String LOGIN_ERROR_MESSAGE_MAXERROR = "登录失败次数过多,锁定10分钟!";
 String LOGIN_ERROR_CODE_100004 = "100004";
 String LOGIN_ERROR_MESSAGE_FORCELOGOUT = "您已经被管理员强制退出,请重新登录";
}

3、登录jsp(重要代码)

路径信息:

?
1
2
3
4
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path;
%>

js:用于更换验证码图片

?
1
2
3
4
5
<script>
 function reloadValidateCode(){
  $("#validateCodeImg").attr("src","<%=basePath%>/validateCode.shtml?data=" + new Date() + Math.floor(Math.random()*24));
 }
</script>

登录表单里面的标签:

?
1
<img id="validateCodeImg" src="<%=basePath%>/validateCode.shtml" />&nbsp;&nbsp;<a href="#" rel="external nofollow" onclick="javascript:reloadValidateCode();">看不清?</a>

4、Shiro匿名访问配置(不配置无法生成验证码图片)

?
1
2
3
4
5
6
7
8
<!--自定义filterChainDefinitionMap -->
 <bean id="chainDefinitionSectionMetaSource" class="com.collection.shiro.ChainDefinitionSectionMetaSource">
  <property name="filterChainDefinitions">
   <value>
    /validateCode.shtml = anon//添加这行
   </value>
  </property>
 </bean>

以上所述是小编给大家介绍的JavaSSM+Shiro系统登录验证码的实现方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!

原文链接:http://blog.csdn.net/rickiyeat/article/details/55050440

标签:

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享 2020-04-07
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
男生常说24816是什么意思?女生说13579是什么意思?
男生常说24816是什么意思?女生说13579是什么意思? 2019-09-17
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意
沙雕群名称大全2019精选 今年最火的微信群名沙雕有创意 2019-07-07
返回顶部