前言
最近在做一個(gè)spring secutiry的項(xiàng)目,是想通過role來控制訪問。但是在調(diào)試的過程中一直沒有進(jìn)入權(quán)限控制代碼,所以進(jìn)行了一個(gè)debug。發(fā)現(xiàn)控制器邏輯先進(jìn)入了validate的代碼,個(gè)人覺得validate應(yīng)在在權(quán)限校驗(yàn)之后處理。遂開始了這次初探體驗(yàn)。
旅程
- 剛開始,我認(rèn)為這個(gè)validate是配置在filter或interscepor中的,進(jìn)行了一層debug,最后發(fā)現(xiàn)實(shí)際是在解析參數(shù)的時(shí)候的一個(gè)默認(rèn)操作,具體是在
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgumentz中的resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);中處理的。具體如圖:
validere代碼入口
這里在緩存中尋找resolver來處理后續(xù)的操作。
P.S. 擴(kuò)展一下,具體的resolver可以看org.springframework.web.method.support.HandlerMethodArgumentResolver#supportsParameter
- 這里使用
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor來繼續(xù)下面的說明。
進(jìn)入方法以后,熟悉的converter也出現(xiàn)了,Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());這個(gè)將parameter轉(zhuǎn)換成具體的object。
后面就是最關(guān)鍵的獲取WebDataBinder,然后進(jìn)行校驗(yàn)了validateIfApplicable(binder, parameter);
校驗(yàn)
最后回從binder中的result中獲取error來拋出異常,給springmvc的異常處理器來處理。
-
到這里開始校驗(yàn)的初判斷,注意看,這里可以支持Valid開頭的注解(包括JSR303種的
@javax.validation.Valid)、org.springframework.validation.annotation.Validated
image.png
如果滿足上述的注解要求,那么就開始進(jìn)行校驗(yàn)了。 -
具體的binder是
org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder,委托給Validator來做校驗(yàn),這里使用了一個(gè)適配器,預(yù)先將javax.validation.Validator或SpringValidatorAdapter包裝成ValidatorAdapter,擴(kuò)展了org.springframework.validation.Validator的一些功能,支持傳遞一些中間數(shù)據(jù)。當(dāng)然本次運(yùn)行時(shí)使用的是SpringValidatorAdapter。
獲取具體Validator進(jìn)行校驗(yàn) -
再往下探究,我們熟悉的hibernate validator就出現(xiàn)了。原來依然被包裝在了
SpringValidatorAdapter中,其中的一個(gè)屬性targetValidator就是org.hibernate.validator.internal.engine.ValidatorImpl,他實(shí)現(xiàn)了javax.validation.Validator。
出現(xiàn)了!hibernate validator??! 接下來就是hibernate的邏輯了,便不在繼續(xù)探究,spring會(huì)將校驗(yàn)器中返回的
Set<ConstraintViolation>進(jìn)行解析,并將錯(cuò)誤封裝在第四步中的創(chuàng)建的bindingResult中。
衍生
通過研究學(xué)習(xí)整個(gè)流程,逆向校驗(yàn)委托流程為
ValidatorAdapter->SpringValidatorAdapter->ValidatorImpl
繼而在正向驗(yàn)證一下探究了一下Validator的加載流程:
-
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration#mvcValidator通過springmvc自動(dòng)加載校驗(yàn)器 -
org.springframework.boot.autoconfigure.validation.ValidatorAdapter#get通過ValidatorAdapter從上下文中獲取數(shù)據(jù)對(duì)象或直接從配置中獲取對(duì)象 -
org.springframework.boot.autoconfigure.validation.ValidatorAdapter#getExistingOrCreate配置中沒有對(duì)象,就開始從上下文中獲取或創(chuàng)建一個(gè)新的,然后就從上下文中獲取了javax.validation.Validator的子類org.springframework.validation.beanvalidation.LocalValidatorFactoryBean。 -
org.springframework.boot.autoconfigure.validation.ValidatorAdapter#wrap對(duì)獲取到的對(duì)象進(jìn)行包裝使用。
不再探究第三步中org.springframework.validation.beanvalidation.LocalValidatorFactoryBean 的加載過程了,交給讀者探究吧。
總結(jié)
從解析的過程來看,校驗(yàn)參數(shù)發(fā)生在解析參數(shù)的時(shí)間,而不是通過過濾器來處理的,是直接框架集成的功能。所以,可能需要換種方式,考慮如何將鑒權(quán)前置,而不是想辦法將校驗(yàn)參數(shù)后置。好的,這個(gè)坑看看什么時(shí)候填。??????




