One error correction experience --- spring authentication management

Source: Internet
Author: User

First, describe the cause of the error: An exception is thrown when the spring Security interceptor is used to verify the verification code.

Throw new runtimeexception ("CAPTCHA validation failed due to exception", CSE );

After the front-end submits data, you can jump to the following method:

Package COM. david studio. GBP. core. security. jcaptcha; import Org. acegisecurity. CAPTCHA. captchaserviceproxy; import Org. apache. log4j. logger; import COM. octo. CAPTCHA. service. captchaservice; import COM. octo. CAPTCHA. service. captchaserviceexception;/*** call the captchaservice class, jcaptcha verification process ***/public class jcaptchaserviceproxyimpl implements captchaserviceproxy {/*** logger for this class */Private Static final logger = logger. getlogger (jcaptchaserviceproxyimpl. class); Private captchaservice jcaptchaservice; Public Boolean validatereponseforid (string ID, object response) {If (logger. isdebugenabled () {logger. debug ("validating CAPTCHA response");} Try {Boolean ishuman = false; ishuman = jcaptchaservice. validateresponseforid (ID, response ). booleanvalue (); If (ishuman) {If (logger. isdebugenabled () {logger. debug ("CAPTCHA passed") ;}} else {If (logger. isdebugenabled () {logger. debug ("CAPTCHA failed") ;}return ishuman;} catch (captchaserviceexception CSE) {// fixes known bug in jcaptchalogger. warn ("CAPTCHA validation failed due to exception", CSE); throw new runtimeexception ("CAPTCHA validation failed due to exception", CSE);} public void submit (captchaservice jcaptchaservice) {This. jcaptchaservice = jcaptchaservice ;}}

Setting the breakpoint debug and modify statement cannot be executed smoothly

 jcaptchaService.validateResponseForID(id, response).booleanValue();

I checked the information on the Internet. The function of this method is to verify the verification code based on the sessionid of httpsession. The principle is as follows. The verification code generated on the page is generated through the configuration in spring, check the Configuration:

<bean id="security.filter.manager" class="org.acegisecurity.util.FilterChainProxy"><property name="filterInvocationDefinitionSource"><value>PATTERN_TYPE_APACHE_ANT/**=security.filter.channel,security.filter.sessionIntegration,security.filter.logout,security.filter.thsso,security.filter.jcaptcha,security.filter.jcaptchachannel,security.filter.formAuth,security.filter.requestWrap,security.filter.exceptionTranslation,security.filter.filterInvocation</value></property></bean>

This is a filter chain. The following filter operations are performed during logon,

Security. filter. channel, security. filter. sessionintegration, security. filter. logout, security. filter. thsso, security. filter. jcaptcha, security. filter. jcaptchachannel, security. filter. formauth, security. filter. requestwrap, security. filter. predictiontranslation, security. filter. filterinvocation

Generally, the configuration sequence cannot be changed because these configurations define a set of authentication mechanisms for user login.

After reading the naming conventions, it involves the verification code Filtering: security. Filter. jcaptcha

Check the reference configuration of this verification code:

<! -- Jcaptacha filter --> <bean id = "security. filter. jcaptcha "class =" org. acegisecurity. CAPTCHA. captchavalidationprocessingfilter "> <property name =" captchaservice "ref =" security. CAPTCHA. serviceproxy "/> <property name =" captchavalidationparameter "value =" j_captcha_response "/> </bean> <bean id =" security. CAPTCHA. serviceproxy "class =" com. david studio. GBP. core. security. jcaptcha. jcaptchaserviceproxyimpl "> <property name =" jcaptchaservice "ref =" security. CAPTCHA. service "/> </bean> <bean id =" security. CAPTCHA. service "class =" com. octo. CAPTCHA. service. image. defaultmanageableimagecaptchaservice "> <constructor-Arg type =" com. octo. CAPTCHA. service. captchastore. captchastore "Index =" 0 "> <Bean class =" com. octo. CAPTCHA. service. captchastore. fasthashmapcaptchastore "/> </constructor-Arg> <constructor-Arg type =" com. octo. CAPTCHA. engine. captchaengine "Index =" 1 "> <Bean class =" com. david studio. GBP. core. security. jcaptcha. captchaengine "/> </constructor-Arg> <constructor-Arg Index =" 2 "> <value> 180 </value> </constructor-Arg> <constructor-Arg Index = <value> 100000 </value> </constructor-Arg> <constructor-Arg Index = "4"> <value> 75000 </value> </constructor- arg> </bean>

Repeated reference through bean configuration.

At first, I thought securitycontext was not created. I checked the configuration and created it:

<! -- Session integration filter. The user identity information is automatically stored in the session. --> <Bean id = "security. filter. sessionintegration "class =" org. acegisecurity. context. httpsessioncontextintegrationfilter "> <property name =" context "value =" org. acegisecurity. CAPTCHA. captchasecuritycontextimpl "/> </bean>

Take a closer look at the role of this method:

 jcaptchaService.validateResponseForID(id, response).booleanValue();

The ID is the ID of the httpsession, and the response is the entered Verification Code obtained from the page. When this method is called, find the corresponding verification code based on the httpsession ID, if there is sessionid and the verification code corresponding to sessionid is the same as the entered Verification Code (response here), true is returned, that is, the user has passed the verification.

I have a question: how is the verification code generated? How can I bind it to httpsession? In fact, this theory is feasible. when a user visits the page for the first time, a sessionid is generated, and the page generates a verification code. The following describes how to generate the verification code. It is just to draw an image and display it on the page as it is left. The user has a corresponding verification code corresponding to sessionid during access.

If the verification code is unclear, click another one. Because the browser is not closed, sessionid is still the sessionid. You only need to update the value of the generated verification code, in this way, a sessionid and a verification code are bound. This process occurs when the verification code is generated.

If the user submits the login information again and the sessionid is not changed, the verification code is the newly generated Verification Code and is bound with the sessionid, so that you can call:

Jcaptchaservice. validateresponseforid (ID, response ). booleanvalue (); this condition is used to verify the verification code. Of course, there are many filters before the verification, such as user name and password verification. Form a set of chain authentication!

However, there is another question: how is this sessionid bound to the Verification code? How is it stored?

Let's take a look at the memory:

When calling this code, the memory contains the sessionid and response verification code values:

The following figure shows the memory status in the thread generated by the Verification Code:

The memory status shows that it is consistent with the configuration file. First, Com. David studio. GBP. Core. Security. jcaptcha. jcaptchaserviceproxyimpl is called.

This proxy implementation class calls com. Octo. CAPTCHA. Service. image. defamanagmanageableimagecaptchaservice.

This class is the verification code generation class: Check the source code of the spring class as follows:

23   public class DefaultManageableImageCaptchaService extends AbstractManageableImageCaptchaService   24           implements ImageCaptchaService {   25       /**   26        * Construct a new ImageCaptchaService with a {@link FastHashMapCaptchaStore} and a {@link DefaultGimpyEngine}   27        *  minGuarantedStorageDelayInSeconds = 180s   28        *  maxCaptchaStoreSize = 100000   29        *  captchaStoreLoadBeforeGarbageCollection=75000   30        */   31       public DefaultManageableImageCaptchaService() {   32           super(new FastHashMapCaptchaStore(), new DefaultGimpyEngine(), 180,   33                   100000, 75000);   34       }

The input parameters are described accordingly, and this class inherits

AbstractManageableImageCaptchaService

Continue to look into this class to see exactly:

This class really has the method we want:

  127       /**  128        * Method to validate a response to the challenge corresponding to the given ticket and remove the coresponding  129        * captcha from the store.  130        *  131        * @param ID the ticket provided by the buildCaptchaAndGetID method  132        * @return true if the response is correct, false otherwise.  133        * @throws CaptchaServiceException if the ticket is invalid  134        */  135       public Boolean validateResponseForID(String ID, Object response)  136               throws CaptchaServiceException {  137           if (!store.hasCaptcha(ID)) {  138               throw new CaptchaServiceException("Invalid ID, could not validate unexisting or already validated captcha");  139           } else {  140               Boolean valid = store.getCaptcha(ID).validateResponse(response);  141               store.removeCaptcha(ID);  142               return valid;  143           }  144       }

This is to determine whether there is a verification code. If the store does not have the corresponding sessionid, an exception is thrown:

throw new CaptchaServiceException("Invalid ID, could not validate unexisting or already validated captcha");

This sessionid does not exist at all. If this sessionid is available, it follows another logic:

 Boolean valid = store.getCaptcha(ID).validateResponse(response);

Store. getcaptcha (ID) is used to obtain the verification code that matches the sessionid, and then the vilidateresponse method is called for verification. If it is the same as the entered verification code, the verification is passed.

After the verification is passed, the sessionid is deleted. If you log on again and enter the verification code in the same logic, it is advantageous to delete this ID:

The reason is as follows: if you do not delete it, as there are too many login users, the value of hashmap will increase, so that the speed and efficiency will be impressed when verification is performed later, if the sessionid is deleted, the hashmap in this store only stores the sessionid and verification code that are currently being logged on! This greatly improves the efficiency. If 0.1 million people log on simultaneously, it is not a problem!

By calling this method, we can see how sessionid is bound to the Verification Code and stored in hashmap! Let's go to the source code for verification:

  18   /**   19    * Simple store based on a HashMap   20    */   21   public class MapCaptchaStore implements CaptchaStore {   22      23       Map store;   24      25       public MapCaptchaStore() {   26           this.store = new HashMap();   27       }   28      29       /**   30        * Check if a captcha is stored for this id   31        *   32        * @return true if a captcha for this id is stored, false otherwise   33        */   34       public boolean hasCaptcha(String id) {   35           return store.containsKey(id);   36       }   37      38       /**   39        * Store the captcha with the provided id as key. The key is assumed to be unique, so if the same key is used twice   40        * to store a captcha, the store will return an exception   41        *   42        * @param id      the key   43        * @param captcha the captcha   44        *   45        * @throws CaptchaServiceException if the captcha already exists, or if an error occurs during storing routine.   46        */   47       public void storeCaptcha(String id, Captcha captcha) throws CaptchaServiceException {   48   //        if (store.get(id) != null) {   49   //            throw new CaptchaServiceException("a captcha with this id already exist. This error must " +   50   //                    "not occurs, this is an implementation pb!");   51   //        }   52           store.put(id, new CaptchaAndLocale(captcha));   53       }

The above is the implementation class mapcaptchastore of the captchastore interface, which defines a hashmap and stores the sessionid and CAPTCHA key-value pairs through the storecaptcha (string ID, CAPTCHA) method, this is the method called when the logon page is generated. The hascaptcha (string ID) method and

 69       /**   70        * Retrieve the captcha for this key from the store.   71        *   72        * @return the captcha for this id   73        *   74        * @throws CaptchaServiceException if a captcha for this key is not found or if an error occurs during retrieving   75        *                                 routine.   76        */   77       public Captcha getCaptcha(String id) throws CaptchaServiceException {   78           Object captchaAndLocale = store.get(id);   79           return captchaAndLocale!=null?((CaptchaAndLocale) captchaAndLocale).getCaptcha():null;   80       }

But we called

Subclass of mapcaptchastore
Fasthashmapcaptchastore to store information: see the same
Fasthashmapcaptchastore class:
  17   public class FastHashMapCaptchaStore extends MapCaptchaStore {   18       public FastHashMapCaptchaStore() {   19           this.store = new FastHashMap();   20       }   21   }

This is all about this class. Let's take a look at the fasthashmap class:

public class FastHashMap extends HashMap {   67      68       /**   69        * The underlying map we are managing.   70        */   71       protected HashMap map = null;   72      73       /**   74        * Are we currently operating in "fast" mode?   75        */   76       protected boolean fast = false;   77      78       // Constructors   79       // ----------------------------------------------------------------------   80      81       /**   82        * Construct an empty map.   83        */   84       public FastHashMap() {   85           super();   86           this.map = new HashMap();   87       }   88   

This class is an extension of hashmap. There are two operations in it, one is fast non-synchronization, and the other is synchronous operations!

Apparently
Fasthashmapcaptchastore is a hashmap
The verification code is implemented in this class:
 18    * Base implementation of the ImageCaptchaService.   19    *   20    * @author <a href="mailto:mag@jcaptcha.net">Marc-Antoine Garrigue</a>   21    * @version 1.0   22    */   23   public abstract class AbstractManageableImageCaptchaService extends AbstractManageableCaptchaService   24           implements ImageCaptchaService {   25      26       protected AbstractManageableImageCaptchaService(CaptchaStore captchaStore,   27                                                       com.octo.captcha.engine.CaptchaEngine captchaEngine,   28                                                       int minGuarantedStorageDelayInSeconds,   29                                                       int maxCaptchaStoreSize,   30                                                       int captchaStoreLoadBeforeGarbageCollection) {   31           super(captchaStore, captchaEngine,   32                   minGuarantedStorageDelayInSeconds, maxCaptchaStoreSize,   33                   captchaStoreLoadBeforeGarbageCollection);   34       }
 73       protected Object getChallengeClone(Captcha captcha) {   74           BufferedImage challenge = (BufferedImage) captcha.getChallenge();   75           BufferedImage clone = new BufferedImage(challenge.getWidth(), challenge.getHeight(), challenge.getType());   76      77           clone.getGraphics().drawImage(challenge, 0, 0, clone.getWidth(), clone.getHeight(), null);   78           clone.getGraphics().dispose();   79      80      81           return clone;   82       }

In this class, only one type is defined, and CAPTCHA is also an interface.

You can go to the memory to see the hashmap with wood
The key and value in hashtable are clearly displayed in the memory, which proves that the verification code is successfully generated.
But why is an error reported for each verification?
Later, I had no choice but to check whether the result was sent to sessionid in hashmap. The result is different, that is, it is not in hashmap. Why? Didn't sessionid be put into each verification code generation?
Why is it different? The reason is actually very simple, that is, when you click to log on, the server assigns another sessionid, which is different from the previous sessionid. The corresponding Verification Code cannot be found in hashmap.
In principle, the server will assign a non-repeated sessionid to the user during the first access. If the server session does not time out, the user will not be assigned a sessionid, reducing the pressure on the server, it also brings a friendly experience. But why are my two sessiids different? Later, the fiddler2 software (which is powerful enough to obtain and even modify the content sent to the form) can be used to view the locally stored cookie, but the cookie is empty, that is, nodata, khan, it's no wonder that different sessionids are allocated each time. How can the server determine that the user submitted each time is the same? With sessionid, the server will write the sessionid in the cookie on the client. When the user submits the request again, if the server finds the sessionid in the user cookie in the memory and does not time out, the sessionid will not be re-allocated. I have read the IE browser and the cookie is forbidden. It is no wonder that every time it is a new sessionid, the Verification Code cannot be verified. An error is reported.
In the study, we should look at the source code and analyze the source code design concept. The best reference is the source code. You need to develop the habit of reading the source code, even if some do not understand it. I was too dizzy to write.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.