驗(yàn)證碼邏輯
以前在項(xiàng)目中也做過驗(yàn)證碼,生成驗(yàn)證碼的代碼網(wǎng)上有很多,也有一些第三方的jar包也可以生成漂亮的驗(yàn)證碼。驗(yàn)證碼邏輯很簡(jiǎn)單,就是在登錄頁放一個(gè)image標(biāo)簽,src指向一個(gè)controller,這個(gè)controller返回把生成的圖片以輸出流返回給頁面,生成圖片的同時(shí)把圖片上的文本放在session,登錄的時(shí)候帶過來輸入的驗(yàn)證碼,從session中取出,兩者對(duì)比。這位老師講的用spring security集成驗(yàn)證碼,大體思路和我說的一樣,但更加規(guī)范和通用些。
spring security是一系列的過濾器鏈,所以在這里驗(yàn)證碼也聲明為過濾器,加在過濾器鏈的 登錄過濾器之前,然后自定義一個(gè)異常類,來響應(yīng)驗(yàn)證碼的錯(cuò)誤信息。
代碼結(jié)構(gòu):
驗(yàn)證碼代碼放在core項(xiàng)目,在browser項(xiàng)目做一下配置。
主要代碼:
1,imagecode:
首先是imagecode類,封裝驗(yàn)證碼圖片、文本、過期時(shí)間
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
|
package com.imooc.security.core.validate.code; import java.awt.image.bufferedimage; import java.time.localdatetime; import java.time.localtime; /** * 驗(yàn)證碼 * classname: imagecode * @description: 驗(yàn)證碼 * @author lihaoyang * @date 2018年3月1日 */ public class imagecode { private bufferedimage image; private string code; private localdatetime expiretime; //過期時(shí)間點(diǎn) /** * * <p>description: </p> * @param image * @param code * @param expiretn 多少秒過期 */ public imagecode(bufferedimage image, string code, int expiretn) { super (); this .image = image; this .code = code; //過期時(shí)間=當(dāng)前時(shí)間+過期秒數(shù) this .expiretime = localdatetime.now().plusseconds(expiretn); } public imagecode(bufferedimage image, string code, localdatetime expiretime) { super (); this .image = image; this .code = code; this .expiretime = expiretime; } /** * 驗(yàn)證碼是否過期 * @description: 驗(yàn)證碼是否過期 * @param @return true 過期,false 沒過期 * @return boolean true 過期,false 沒過期 * @throws * @author lihaoyang * @date 2018年3月2日 */ public boolean isexpired(){ return localdatetime.now().isafter(expiretime); } public bufferedimage getimage() { return image; } public void setimage(bufferedimage image) { this .image = image; } public string getcode() { return code; } public void setcode(string code) { this .code = code; } public localdatetime getexpiretime() { return expiretime; } public void setexpiretime(localdatetime expiretime) { this .expiretime = expiretime; } } |
verifycode:生成驗(yàn)證碼的工具類,在這里 http://www.cnblogs.com/lihaoyang/p/7131512.html 當(dāng)然也可以使用第三方j(luò)ar包,無所謂。
validatecodeexception:封裝驗(yàn)證碼異常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/** * @title: validatecodeexception.java * @package com.imooc.security.core.validate.code * @description: todo * @author lihaoyang * @date 2018年3月2日 */ package com.imooc.security.core.validate.code; import org.springframework.security.core.authenticationexception; /** * classname: validatecodeexception * @description: 驗(yàn)證碼錯(cuò)誤異常,繼承spring security的認(rèn)證異常 * @author lihaoyang * @date 2018年3月2日 */ public class validatecodeexception extends authenticationexception { /** * @fields serialversionuid : todo */ private static final long serialversionuid = 1l; public validatecodeexception(string msg) { super (msg); } } |
validatecodefilter:驗(yàn)證碼過濾器
邏輯:繼承onceperrequestfilter 保證過濾器每次只會(huì)被調(diào)用一次(不太清楚為什么),注入認(rèn)證失敗處理器,在驗(yàn)證失敗時(shí)調(diào)用。
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
|
package com.imooc.security.core.validate.code; import java.io.ioexception; import javax.servlet.filterchain; import javax.servlet.servletexception; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import org.apache.commons.lang.stringutils; import org.springframework.security.web.authentication.authenticationfailurehandler; import org.springframework.social.connect.web.httpsessionsessionstrategy; import org.springframework.social.connect.web.sessionstrategy; import org.springframework.web.bind.servletrequestbindingexception; import org.springframework.web.bind.servletrequestutils; import org.springframework.web.context.request.servletwebrequest; import org.springframework.web.filter.onceperrequestfilter; /** * 處理登錄驗(yàn)證碼過濾器 * classname: validatecodefilter * @description: * onceperrequestfilter:spring提供的工具,保證過濾器每次只會(huì)被調(diào)用一次 * @author lihaoyang * @date 2018年3月2日 */ public class validatecodefilter extends onceperrequestfilter{ //認(rèn)證失敗處理器 private authenticationfailurehandler authenticationfailurehandler; //獲取session工具類 private sessionstrategy sessionstrategy = new httpsessionsessionstrategy(); @override protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain filterchain) throws servletexception, ioexception { //如果是 登錄請(qǐng)求 則執(zhí)行 if (stringutils.equals( "/authentication/form" , request.getrequesturi()) &&stringutils.equalsignorecase(request.getmethod(), "post" )){ try { validate( new servletwebrequest(request)); } catch (validatecodeexception e) { //調(diào)用錯(cuò)誤處理器,最終調(diào)用自己的 authenticationfailurehandler.onauthenticationfailure(request, response, e); return ; //結(jié)束方法,不再調(diào)用過濾器鏈 } } //不是登錄請(qǐng)求,調(diào)用其它過濾器鏈 filterchain.dofilter(request, response); } /** * 校驗(yàn)驗(yàn)證碼 * @description: 校驗(yàn)驗(yàn)證碼 * @param @param request * @param @throws servletrequestbindingexception * @return void * @throws validatecodeexception * @author lihaoyang * @date 2018年3月2日 */ private void validate(servletwebrequest request) throws servletrequestbindingexception { //拿出session中的imagecode對(duì)象 imagecode imagecodeinsession = (imagecode) sessionstrategy.getattribute(request, validatecodecontroller.session_key); //拿出請(qǐng)求中的驗(yàn)證碼 string imagecodeinrequest = servletrequestutils.getstringparameter(request.getrequest(), "imagecode" ); //校驗(yàn) if (stringutils.isblank(imagecodeinrequest)){ throw new validatecodeexception( "驗(yàn)證碼不能為空" ); } if (imagecodeinsession == null ){ throw new validatecodeexception( "驗(yàn)證碼不存在,請(qǐng)刷新驗(yàn)證碼" ); } if (imagecodeinsession.isexpired()){ //從session移除過期的驗(yàn)證碼 sessionstrategy.removeattribute(request, validatecodecontroller.session_key); throw new validatecodeexception( "驗(yàn)證碼已過期,請(qǐng)刷新驗(yàn)證碼" ); } if (!stringutils.equalsignorecase(imagecodeinsession.getcode(), imagecodeinrequest)){ throw new validatecodeexception( "驗(yàn)證碼錯(cuò)誤" ); } //驗(yàn)證通過,移除session中驗(yàn)證碼 sessionstrategy.removeattribute(request, validatecodecontroller.session_key); } public authenticationfailurehandler getauthenticationfailurehandler() { return authenticationfailurehandler; } public void setauthenticationfailurehandler(authenticationfailurehandler authenticationfailurehandler) { this .authenticationfailurehandler = authenticationfailurehandler; } } |
validatecodecontroller:生成驗(yàn)證碼control
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
|
package com.imooc.security.core.validate.code; import java.io.ioexception; import javax.imageio.imageio; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import org.springframework.social.connect.web.httpsessionsessionstrategy; import org.springframework.social.connect.web.sessionstrategy; import org.springframework.web.bind.annotation.getmapping; import org.springframework.web.bind.annotation.restcontroller; import org.springframework.web.context.request.servletwebrequest; /** * 驗(yàn)證碼control * classname: validatecodecontroller * @description: todo * @author lihaoyang * @date 2018年3月1日 */ @restcontroller public class validatecodecontroller { public static final string session_key = "session_key_image_code" ; //獲取session private sessionstrategy sessionstrategy = new httpsessionsessionstrategy(); @getmapping ( "/verifycode/image" ) public void createcode(httpservletrequest request,httpservletresponse response) throws ioexception{ imagecode imagecode = createimagecode(request, response); sessionstrategy.setattribute( new servletwebrequest(request), session_key, imagecode); imageio.write(imagecode.getimage(), "jpeg" , response.getoutputstream()); } private imagecode createimagecode(httpservletrequest request, httpservletresponse response) { verifycode verifycode = new verifycode(); return new imagecode(verifycode.getimage(),verifycode.gettext(), 60 ); } } |
browsersecurityconfig里進(jìn)行過濾器配置:
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
|
package com.imooc.security.browser; import org.springframework.beans.factory.annotation.autowired; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; import org.springframework.security.config.annotation.web.builders.httpsecurity; import org.springframework.security.config.annotation.web.configuration.websecurityconfigureradapter; import org.springframework.security.crypto.bcrypt.bcryptpasswordencoder; import org.springframework.security.crypto.password.passwordencoder; import org.springframework.security.web.authentication.authenticationfailurehandler; import org.springframework.security.web.authentication.authenticationsuccesshandler; import org.springframework.security.web.authentication.usernamepasswordauthenticationfilter; import com.imooc.security.core.properties.securityproperties; import com.imooc.security.core.validate.code.validatecodefilter; @configuration //這是一個(gè)配置 public class browsersecurityconfig extends websecurityconfigureradapter{ //讀取用戶配置的登錄頁配置 @autowired private securityproperties securityproperties; //自定義的登錄成功后的處理器 @autowired private authenticationsuccesshandler imoocauthenticationsuccesshandler; //自定義的認(rèn)證失敗后的處理器 @autowired private authenticationfailurehandler imoocauthenticationfailurehandler; //注意是org.springframework.security.crypto.password.passwordencoder @bean public passwordencoder passwordencoder(){ //bcryptpasswordencoder implements passwordencoder return new bcryptpasswordencoder(); } //版本二:可配置的登錄頁 @override protected void configure(httpsecurity http) throws exception { //驗(yàn)證碼過濾器 validatecodefilter validatecodefilter = new validatecodefilter(); //驗(yàn)證碼過濾器中使用自己的錯(cuò)誤處理 validatecodefilter.setauthenticationfailurehandler(imoocauthenticationfailurehandler); //實(shí)現(xiàn)需要認(rèn)證的接口跳轉(zhuǎn)表單登錄,安全=認(rèn)證+授權(quán) //http.httpbasic() //這個(gè)就是默認(rèn)的彈框認(rèn)證 // http.addfilterbefore(validatecodefilter, usernamepasswordauthenticationfilter. class ) //把驗(yàn)證碼過濾器加載登錄過濾器前邊 .formlogin() //表單認(rèn)證 .loginpage( "/authentication/require" ) //處理用戶認(rèn)證browsersecuritycontroller //登錄過濾器usernamepasswordauthenticationfilter默認(rèn)登錄的url是"/login",在這能改 .loginprocessingurl( "/authentication/form" ) .successhandler(imoocauthenticationsuccesshandler) //自定義的認(rèn)證后處理器 .failurehandler(imoocauthenticationfailurehandler) //登錄失敗后的處理 .and() .authorizerequests() //下邊的都是授權(quán)的配置 // /authentication/require:處理登錄,securityproperties.getbrowser().getloginpage():用戶配置的登錄頁 .antmatchers( "/authentication/require" , securityproperties.getbrowser().getloginpage(), //放過登錄頁不過濾,否則報(bào)錯(cuò) "/verifycode/image" ).permitall() //驗(yàn)證碼 .anyrequest() //任何請(qǐng)求 .authenticated() //都需要身份認(rèn)證 .and() .csrf().disable() //關(guān)閉csrf防護(hù) ; } } |
登陸頁:登陸頁做的比較粗糙,其實(shí)驗(yàn)證碼可以在驗(yàn)證碼input失去焦點(diǎn)的時(shí)候做校驗(yàn),還可以做個(gè)點(diǎn)擊圖片刷新驗(yàn)證碼功能,這里就不做了。
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
|
<body> demo 登錄頁. <br> <form action= "/authentication/form" method= "post" > <table> <tr> <td>用戶名:</td> <td><input type= "text" name= "username" /></td> <td></td> </tr> <tr> <td>密碼:</td> <td><input type= "password" name= "password" /></td> <td></td> </tr> <tr> <td>驗(yàn)證碼:</td> <td> <input width= "100" type= "text" name= "imagecode" /> </td> <td> <img src= "/verifycode/image" /> </td> </tr> <tr> <td colspan= "2" align= "right" ><button type= "submit" >登錄</button></td> </tr> </table> </form> </body> |
訪問 :http://localhost:8080/demo-login.html
響應(yīng)自定義的異常信息
大體功能已經(jīng)沒問題了。但是不夠通用,比如驗(yàn)證碼圖片的寬高、過期時(shí)間、過濾的url、驗(yàn)證碼成邏輯都是寫死的。這些可以做成活的,現(xiàn)在把驗(yàn)證碼做成一個(gè)過濾器的好處體現(xiàn)出來了。我們可以配置需要過濾的url,有時(shí)候可能不只是登陸頁需要驗(yàn)證碼,這樣更加通用。
1,通用性改造 之 驗(yàn)證碼基本參數(shù)可配
做成可配置的,那個(gè)應(yīng)用引用該模塊,他自己配置去,不配置就使用默認(rèn)配置。而且,配置既可以在請(qǐng)求url中聲明,也可以在應(yīng)用中聲明,老師的確是老師,代碼通用性真好!
想要實(shí)現(xiàn)的效果是,在application.properties里做這樣的配置:
1
2
3
4
|
#驗(yàn)證碼 圖片寬、高、字符個(gè)數(shù) imooc.security.code.image.width = 100 imooc.security.code.image.height = 30 imooc.security.code.image.length = 6 |
然后就能控制驗(yàn)證碼的效果,因?yàn)轵?yàn)證碼還分圖片驗(yàn)證碼、短信驗(yàn)證碼,所以多做了一級(jí).code.image,這就用到了springboot的自定義配置文件,需要聲明對(duì)應(yīng)的java類:
需要在securityproperties里聲明code屬性:
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
|
package com.imooc.security.core.properties; import org.springframework.boot.context.properties.configurationproperties; import org.springframework.context.annotation.configuration; /** * 自定義配置項(xiàng) * classname: securityproperties * @description: 自定義配置項(xiàng) * 這個(gè)類會(huì)讀取application.properties里所有以imooc.security開頭的配置項(xiàng) * * imooc.security.browser.loginpage = /demo-login.html * 其中的browser的配置會(huì)讀取到browserproperties中去 * 這是以點(diǎn)分割的,一級(jí)一級(jí)的和類的屬性對(duì)應(yīng) * @author lihaoyang * @date 2018年2月28日 */ @configurationproperties (prefix= "imooc.security" ) public class securityproperties { private browserproperties browser = new browserproperties(); private validatecodeproperties code = new validatecodeproperties(); public browserproperties getbrowser() { return browser; } public void setbrowser(browserproperties browser) { this .browser = browser; } public validatecodeproperties getcode() { return code; } public void setcode(validatecodeproperties code) { this .code = code; } } |
validatecodeproperties:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.imooc.security.core.properties; /** * 驗(yàn)證碼配置 * classname: validatecodeproperties * @description: 驗(yàn)證碼配置,驗(yàn)證碼有圖片驗(yàn)證碼、短信驗(yàn)證碼等,所以再包一層 * @author lihaoyang * @date 2018年3月2日 */ public class validatecodeproperties { //默認(rèn)配置 private imagecodeproperties image = new imagecodeproperties(); public imagecodeproperties getimage() { return image; } public void setimage(imagecodeproperties image) { this .image = image; } } |
imagecodeproperties:
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
|
package com.imooc.security.core.properties; /** * 圖片驗(yàn)證碼配置類 * classname: imagecodeproperties * @description: 圖片驗(yàn)證碼配置類 * @author lihaoyang * @date 2018年3月2日 */ public class imagecodeproperties { //圖片寬 private int width = 67 ; //圖片高 private int height = 23 ; //驗(yàn)證碼字符個(gè)數(shù) private int length = 4 ; //過期時(shí)間 private int expirein = 60 ; public int getwidth() { return width; } public void setwidth( int width) { this .width = width; } public int getheight() { return height; } public void setheight( int height) { this .height = height; } public int getlength() { return length; } public void setlength( int length) { this .length = length; } public int getexpirein() { return expirein; } public void setexpirein( int expirein) { this .expirein = expirein; } } |
請(qǐng)求級(jí)的配置,如果請(qǐng)求里帶的有驗(yàn)證碼的參數(shù),就用請(qǐng)求里的:
在validatecodecontroller的createimagecode方法做控制,判斷請(qǐng)求參數(shù)是否有這些參數(shù),有的話,傳給驗(yàn)證碼生成類verifycode,在生成的時(shí)候就能動(dòng)態(tài)控制了。
1
2
3
4
5
6
7
8
9
10
|
private imagecode createimagecode(httpservletrequest request, httpservletresponse response) { //先從request里讀取有沒有長(zhǎng)、寬、字符個(gè)數(shù)參數(shù),有的話就用,沒有用默認(rèn)的 int width = servletrequestutils.getintparameter(request, "width" ,securityproperties.getcode().getimage().getwidth()); int height = servletrequestutils.getintparameter(request, "height" ,securityproperties.getcode().getimage().getheight()); int charlength = this .securityproperties.getcode().getimage().getlength(); verifycode verifycode = new verifycode(width,height,charlength); return new imagecode(verifycode.getimage(),verifycode.gettext(), this .securityproperties.getcode().getimage().getexpirein()); } |
verifycode:
1
2
3
4
5
6
|
public verifycode( int w, int h, int charlength) { super (); this .w = w; this .h = h; this .charlength = charlength; } |
實(shí)驗(yàn):在demo項(xiàng)目做應(yīng)用級(jí)配置
登錄表單做請(qǐng)求級(jí)配置
1
|
<img src= "/verifycode/image?width=200" /> |
訪問:
長(zhǎng)度為請(qǐng)求級(jí)帶的參數(shù)200,高為30,字符為配置的6個(gè)。
2,通用性改造 之 驗(yàn)證碼攔截的接口可配置
先要的效果就是再application.properties
里能動(dòng)態(tài)配置需要攔截的接口:
imagecodeproperties
新增一個(gè)屬性:private string url
; //攔截的url,來匹配上圖的配置。
核心,驗(yàn)證碼過濾器需要修改:
1,在攔截器里聲明一個(gè)set集合,用來存儲(chǔ)配置文件里配置的需要攔截的urls。
2,實(shí)現(xiàn)initializingbean接口,目的: 在其他參數(shù)都組裝完畢的時(shí)候,初始化需要攔截的urls的值,重寫afterpropertiesset方法來實(shí)現(xiàn)。
3,注入securityproperties,讀取配置文件
4,實(shí)例化antpathmatcher工具類,這是一個(gè)匹配器
5,在browser項(xiàng)目的browsersecurityconfig里設(shè)置調(diào)用一下afterpropertiesset方法。
6,在引用該模塊的demo項(xiàng)目的application.properties里配置要過濾的url
validatecodefilter:
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
|
/** * 處理登錄驗(yàn)證碼過濾器 * classname: validatecodefilter * @description: * 繼承onceperrequestfilter:spring提供的工具,保證過濾器每次只會(huì)被調(diào)用一次 * 實(shí)現(xiàn) initializingbean接口的目的: * 在其他參數(shù)都組裝完畢的時(shí)候,初始化需要攔截的urls的值 * @author lihaoyang * @date 2018年3月2日 */ public class validatecodefilter extends onceperrequestfilter implements initializingbean{ //認(rèn)證失敗處理器 private authenticationfailurehandler authenticationfailurehandler; //獲取session工具類 private sessionstrategy sessionstrategy = new httpsessionsessionstrategy(); //需要攔截的url集合 private set<string> urls = new hashset<>(); //讀取配置 private securityproperties securityproperties; //spring工具類 private antpathmatcher antpathmatcher = new antpathmatcher(); @override public void afterpropertiesset() throws servletexception { super .afterpropertiesset(); //讀取配置的攔截的urls string[] configurls = stringutils.splitbywholeseparatorpreservealltokens(securityproperties.getcode().getimage().geturl(), "," ); for (string configurl : configurls) { urls.add(configurl); } //登錄的請(qǐng)求一定攔截 urls.add( "/authentication/form" ); } @override protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain filterchain) throws servletexception, ioexception { /** * 可配置的驗(yàn)證碼校驗(yàn) * 判斷請(qǐng)求的url和配置的是否有匹配的,匹配上了就過濾 */ boolean action = false ; for (string url:urls){ if (antpathmatcher.match(url, request.getrequesturi())){ action = true ; } } if (action){ try { validate( new servletwebrequest(request)); } catch (validatecodeexception e) { //調(diào)用錯(cuò)誤處理器,最終調(diào)用自己的 authenticationfailurehandler.onauthenticationfailure(request, response, e); return ; //結(jié)束方法,不再調(diào)用過濾器鏈 } } //不是登錄請(qǐng)求,調(diào)用其它過濾器鏈 filterchain.dofilter(request, response); } //省略無關(guān)代碼,,, } |
browsersecurityconfig:
配置url:
1
2
|
#驗(yàn)證碼攔截的接口配置 imooc.security.code.image.url = /user,/user/* |
測(cè)試:/user /user/1 被攔截了
訪問登錄頁,不寫驗(yàn)證碼:
和預(yù)期一致。至此,動(dòng)態(tài)配置攔截接口完成
3,驗(yàn)證碼的生成邏輯可配置
寫的比較好的程序,一般都開放接口,可以讓用戶去自定義實(shí)現(xiàn),如果不實(shí)現(xiàn)就用默認(rèn)的實(shí)現(xiàn),下面來做這件事,使驗(yàn)證碼的生成可以自己實(shí)現(xiàn)。如果要想把驗(yàn)證碼的生成邏輯做成可配置的,就不能只寫一個(gè)圖片驗(yàn)證碼生成器的類了,需要把驗(yàn)證碼生成提取成一個(gè)接口validatecodegenerator,一個(gè)生成驗(yàn)證碼的方法generator()。因?yàn)轵?yàn)證碼還有圖片驗(yàn)證碼、短信驗(yàn)證碼等,這樣,我們?cè)谧约旱尿?yàn)證模塊里做一個(gè)默認(rèn)的實(shí)現(xiàn),如圖片驗(yàn)證碼的實(shí)現(xiàn)imagecodegenerator,在imagecodegenerator里我們不在該類上加@component注解。然后使用寫一個(gè)驗(yàn)證碼bean的配置類validatecodebeanconfig,這個(gè)配置類配置各種需要的驗(yàn)證碼實(shí)現(xiàn)類bean如圖片驗(yàn)證碼實(shí)現(xiàn)imagecodegenerator、短信驗(yàn)證碼等,他們返回類型都是validatecodegenerator,使用@conditionalonmissingbean(name="imagecodegenerator")注解,可以判斷如果當(dāng)前spring容器有名字為imagecodegenerator的bean時(shí),就使用,沒有的話再配置,這樣如果別人引用了你的該模塊,如果別人自己實(shí)現(xiàn)了驗(yàn)證碼生成validatecodegenerator接口,他們配置了實(shí)現(xiàn)類的name為imagecodegenerator,就用他們自己的實(shí)現(xiàn),這樣就做到了程序的可擴(kuò)展性。
主要代碼:
代碼生成器接口validatecodegenerator:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.imooc.security.core.validate.code; import org.springframework.web.context.request.servletwebrequest; /** * 驗(yàn)證碼生成接口 * classname: validatecodegenerator * @description: todo * @author lihaoyang * @date 2018年3月2日 */ public interface validatecodegenerator { /** * 圖片驗(yàn)證碼生成接口 * @description: todo * @param @param request * @param @return * @return imagecode * @throws * @author lihaoyang * @date 2018年3月2日 */ imagecode generator(servletwebrequest request); } |
圖片驗(yàn)證碼生成器實(shí)現(xiàn)imagecodegenerator:
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
|
package com.imooc.security.core.validate.code; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.component; import org.springframework.web.bind.servletrequestutils; import org.springframework.web.context.request.servletwebrequest; import com.imooc.security.core.properties.securityproperties; /** * 圖片驗(yàn)證碼生成類 * classname: imagecodegenerator * @description: todo * @author lihaoyang * @date 2018年3月2日 */ public class imagecodegenerator implements validatecodegenerator { @autowired private securityproperties securityproperties; @override public imagecode generator(servletwebrequest request) { //先從request里讀取有沒有長(zhǎng)、寬、字符個(gè)數(shù)參數(shù),有的話就用,沒有用默認(rèn)的 int width = servletrequestutils.getintparameter(request.getrequest(), "width" ,securityproperties.getcode().getimage().getwidth()); int height = servletrequestutils.getintparameter(request.getrequest(), "height" ,securityproperties.getcode().getimage().getheight()); int charlength = this .securityproperties.getcode().getimage().getlength(); verifycode verifycode = new verifycode(width,height,charlength); return new imagecode(verifycode.getimage(),verifycode.gettext(), this .securityproperties.getcode().getimage().getexpirein()); } public securityproperties getsecurityproperties() { return securityproperties; } public void setsecurityproperties(securityproperties securityproperties) { this .securityproperties = securityproperties; } } |
validatecodebeanconfig:
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
|
package com.imooc.security.core.validate.code; import org.springframework.beans.factory.annotation.autowired; import org.springframework.boot.autoconfigure.condition.conditionalonmissingbean; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; import com.imooc.security.core.properties.securityproperties; /** * 配置驗(yàn)證碼生成接口validatecodegenerator的實(shí)際實(shí)現(xiàn)類的bean * classname: validatecodebeanconfig * @description: * 配置驗(yàn)證碼生成接口validatecodegenerator的實(shí)際實(shí)現(xiàn)類的bean * 如圖片驗(yàn)證碼的實(shí)現(xiàn)、短信驗(yàn)證碼的實(shí)現(xiàn) * @author lihaoyang * @date 2018年3月5日 */ @configuration public class validatecodebeanconfig { @autowired private securityproperties securityproperties; /** * @description: * @conditionalonmissingbean注解意思是當(dāng)spring容器不存在imagecodegenerator時(shí)才給配置一個(gè)該bean * 作用是使程序更具可擴(kuò)展性,該配置類是配置在core模塊,這就意味著,如果引用該模塊的項(xiàng)目 * 如果有一個(gè)自己的實(shí)現(xiàn),實(shí)現(xiàn)了validatecodegenerator接口,定義了自己的實(shí)現(xiàn),名字也叫imagecodegenerator時(shí), * 就用應(yīng)用級(jí)別的實(shí)現(xiàn),沒有的話就用這個(gè)默認(rèn)實(shí)現(xiàn)。 * @param @return * @return validatecodegenerator * @throws * @author lihaoyang * @date 2018年3月5日 */ @bean @conditionalonmissingbean (name= "imagecodegenerator" ) public validatecodegenerator imagecodegenerator(){ imagecodegenerator codegenerator = new imagecodegenerator(); codegenerator.setsecurityproperties(securityproperties); return codegenerator; } } |
這樣,如果哪個(gè)模塊引用了這個(gè)驗(yàn)證碼模塊,他自定義了實(shí)現(xiàn),如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package com.imooc.code; import org.springframework.stereotype.component; import org.springframework.web.context.request.servletwebrequest; import com.imooc.security.core.validate.code.imagecode; import com.imooc.security.core.validate.code.validatecodegenerator; @component ( "imagecodegenerator" ) public class demoimagecodegenerator implements validatecodegenerator { @override public imagecode generator(servletwebrequest request) { system.err.println( "demo項(xiàng)目實(shí)現(xiàn)的生成驗(yàn)證碼,,," ); return null ; } } |
這樣validatecodebeanconfig在配置驗(yàn)證碼bean時(shí),就會(huì)使用使用者自定義的實(shí)現(xiàn)。
完整代碼放在了github:https://github.com/lhy1234/spring-security
總結(jié)
以上所述是小編給大家介紹的spring security 圖片驗(yàn)證碼功能的實(shí)例代碼,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)服務(wù)器之家網(wǎng)站的支持!
原文鏈接:https://www.cnblogs.com/lihaoyang/archive/2018/03/05/8491792.html