简体   繁体   中英

Implementing reCaptcha with Apache Shiro

I have a webapp that is secured with Apache Shiro and is all working fine. Now I want to add Goolge's reCaptcha to my login page and can't figure out how to do that.

The captcha itself is added and working fine, the part I need help with is how to integrate it with Shiro now to do the verification before continuing on to authenticate the user.

From what I've gathered from the docs this would be done with a filter chain.

So I put this into my shiro.ini :

[main]
authc.failureKeyAttribute = shiroLoginFailure
authc.loginUrl = /login.jsp
authc.successUrl = /LogIn
logout.redirectUrl = /login.jsp
captcha = path.to.my.class.VerifyUserFilter

...

[urls]
/login.jsp = captcha,authc
/* = authc

I know how to implement the logic to verify the captcha. What I don't know is what that VerifyUserFilter class needs to look like aside from the actual verify logic to make this work. Essentially all it needs to do is post to the Google API, get the response, parse it and based on the result pass the request to the next filter or stop if the verif failed.

Before I posted this I decided to give it one last try and spent a couple more hours trying different things and eventually got it to work! I still decided to post this question anyway, along with my own answer below as I wasn't able to find much help when researching this question and also to see if what I did is correct. If there is a better or more proper way to do this I'd like to know.

Here is how I solved this.

First I set up my shiro.ini as follows (relevant parts):

[main]
authc.failureKeyAttribute = shiroLoginFailure
authc.loginUrl = /login.jsp
authc.successUrl = /LogIn
logout.redirectUrl = /login.jsp
captcha = path.to.my.class.VerifyUserFilter

...

[urls]
/login.jsp = captcha,authc
/* = authc 

Then I created my VerifyUserFilter class by extending the FormAuthenticationFilter class and overriding the doFilterInternal method which reads the parameters, makes the call to the Google API to verify the response and then based on the results either redirects to the login page if the verif failed or moves on to the next item in the filter chain - authc in this case.

Here is the complete implementation of my VerifyUserFilter class:

import org.apache.shiro.web.util.WebUtils;

public class VerifyUserFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {

    @Override
    public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(VerifyUserFilter.class);
        if (isLoginSubmission(request, response)){   
            String user =  WebUtils.getCleanParam(request, "username");
            String verifId = WebUtils.getCleanParam(request, "g-recaptcha-response");
            log.debug("Verif ID: " + verifId);
            if (verifId == null || "".equals(verifId)) {                
                log.warn("User " + user + " missed the captcha challenge and is returned to login page.");
                saveRequestAndRedirectToLogin(request, response);                
            } else {
                // Now do the verification of the captcha
                final String url = "https://www.google.com/recaptcha/api/siteverify";
                final String secret = "6Lxxxxxxxxxxxxxx";
                // Send the POST request to the Google API
                URL obj = new URL(url);
                HttpsURLConnection con = (HttpsURLConnection)obj.openConnection();
                con.setRequestMethod("POST");
                String params = "secret=" + secret + "&response=" + verifId;
                con.setDoOutput(true);
                DataOutputStream wr = new DataOutputStream(con.getOutputStream());
                log.debug("Sending POST to " + url);
                wr.writeBytes(params);
                wr.flush();
                wr.close();
                // Get the response code
                int respCode = con.getResponseCode();
                log.debug("Response code: " + respCode);
                // Read in the response data
                BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
                String line;
                StringBuilder input = new StringBuilder();
                log.debug("Reading response data...");
                while ((line = in.readLine()) != null){
                    input.append(line);
                }
                in.close();
                log.debug("Reponse data: " + input);
                // Parse the json received to determine if the verif was successful
                JsonReader jr = Json.createReader(new StringReader(input.toString()));
                JsonObject jo = jr.readObject();
                jr.close();                
                if (jo.getBoolean("success")){                  
                    log.debug("User " + user + " is good to go, not a robot!");
                    // Move on to the next filter
                    chain.doFilter(request, response);
                } else {
                    // User did not solve the captcha, return to login page
                    log.warn("User " + user + " failed the captcha challenge and is returned to login page.");
                    saveRequestAndRedirectToLogin(request, response);                    
                }
            }                  
        } else {
            log.debug("Not a login attempt, carry on.");
            chain.doFilter(request, response);
        }
    }              
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM