先給大家展示下效果圖:
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; /** * 驗證碼生成器類,可生成數(shù)字、大寫、小寫字母及三者混合類型的驗證碼。 支持自定義驗證碼字符數(shù)量; 支持自定義驗證碼圖片的大小; 支持自定義需排除的特殊字符; * 支持自定義干擾線的數(shù)量; 支持自定義驗證碼圖文顏色 */ public class ValidateCode { /** * 驗證碼類型為僅數(shù)字 0~9 */ public static final int TYPE_NUM_ONLY = 0 ; /** * 驗證碼類型為僅字母,即大寫、小寫字母混合 */ public static final int TYPE_LETTER_ONLY = 1 ; /** * 驗證碼類型為數(shù)字、大寫字母、小寫字母混合 */ public static final int TYPE_ALL_MIXED = 2 ; /** * 驗證碼類型為數(shù)字、大寫字母混合 */ public static final int TYPE_NUM_UPPER = 3 ; /** * 驗證碼類型為數(shù)字、小寫字母混合 */ 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 * 驗證碼類型,參見本類的靜態(tài)屬性 * @param length * 驗證碼長度,大于0的整數(shù) * @param exChars * 需排除的特殊字符(僅對數(shù)字、字母混合型驗證碼有效,無需排除則為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) { // 僅數(shù)字 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 ; // 數(shù)字、大寫字母、小寫字母混合 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 ; // 數(shù)字、大寫字母混合 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 ; // 數(shù)字、小寫字母混合 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 * 圖片中干擾線的條數(shù) * @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 * 驗證碼類型,參見本類的靜態(tài)屬性 * @param length * 驗證碼字符長度,大于0的整數(shù) * @param exChars * 需排除的特殊字符 * @param width * 圖片寬度 * @param height * 圖片高度 * @param interLine * 圖片中干擾線的條數(shù) * @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; } /** * 產(chǎn)生隨機顏色 * * @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(文本驗證碼,圖片寬度,圖片高度,干擾線的條數(shù),字符的高低位置是否隨機,圖片顏色,字體顏色,干擾線顏色) */ @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的攔截地址內(nèi).不然后會報空指針 Subject sub = SecurityUtils.getSubject(); // 用戶輸入的賬號和密碼,,存到UsernamePasswordToken對象中..然后由shiro內(nèi)部認證對比, // 認證執(zhí)行者交由ShiroDbRealm中doGetAuthenticationInfo處理 // 當以上認證成功后會向下執(zhí)行,認證失敗會拋出異常 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" ; } |
注意:
登錄方法里面一些參數(shù)的定義:
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 = "用戶已經(jīng)被鎖定不能登錄,請與管理員聯(lián)系!" ; String LOGIN_ERROR_CODE_100003 = "100003" ; String LOGIN_ERROR_MESSAGE_MAXERROR = "登錄失敗次數(shù)過多,鎖定10分鐘!" ; String LOGIN_ERROR_CODE_100004 = "100004" ; String LOGIN_ERROR_MESSAGE_FORCELOGOUT = "您已經(jīng)被管理員強制退出,請重新登錄" ; } |
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" /> <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> |
以上所述是小編給大家介紹的Java中SSM+Shiro系統(tǒng)登錄驗證碼的實現(xiàn)方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://blog.csdn.net/rickiyeat/article/details/55050440