How to authenticate a user via HTTP request?

3

I am trying to authenticate users in a VBulletin forum by using a desktop application using package components swing , the code is working but I believe this is not the best way to do it.

Logic

I used the Live HTTP Headers plugin in Firefox to find the attributes to be sent in the login form. They are:

vb_login_md5password --> A senha em MD5
vb_login_md5password_utf --> Idem ao anterior
vb_login_username --> Nome de usuário

So I created a String with these parameters, so:

String query = "do=login&url=index.php" // url de login
              + "&vb_login_md5password=" + password // a senha já em MD5
              + "&vb_login_md5password_utf=" + password // a senha novamente
              + "&s=&securitytoken=guest&vb_login_username=" + username // o nome de usuário
              + "&vb_login_password="; // !importante

Next I make a request for the login page with a HttpURLConnection and I get the code HTML from the next page - which can be an error page or the correct page if the authentication is correct. So I look for information that only a logged in user could see, in the case it is a message written "Welcome {user name}" at the beginning of the code HTML .

The problem ...

... is that all the HTML source code of the page is downloaded so that it can be searched inside it for String "Bem Vindo" that is in the beginning, that is, it is downloading a lot of information besides required. I do not believe this is the best way to do it, I'm already thinking about performance, some forums can have many sub-forums and this download of code followed by word search can be very slow.

Another problem is that a user can change the forum language. Assuming he is using the English version (defined in preferences in the forum) my logic would be useless since there would be a word "Welcome" and "Welcome". So, even if the username and password were correct, authentication would not occur - in the application, because of logic.

I did some research and found JSoup , but it serves to extract and manipulate information from an HTML code (for example, ID) and it's not what I'm looking for.

PS : I do not need more information about the user. It is quite simple, just to know if he is a member of a certain forum, i.e. if he has an account. Authenticated = you have an account, you are a member. You did not authenticate = not a member.

How can I authenticate a user to VBulletin forums through an HTTP connection?

    
asked by anonymous 04.01.2015 / 07:35

2 answers

1

After several attempts I found a solution using HtmlUnit even with a documentation I was able to authenticate a user in a VBulletin forum. Before sharing the code, I would like to inform you:

Form and submit :

For those interested, you can get any page element by id or name with HtmlUnit . In the login form, both% username and password% have a defined <input> , the problem is that there is no way to get the name button because it does not exist, probably the form is submitted of a Javascript file. To solve this problem I created a " fake " button and inserted it inside the login form (it is possible to do this with HtmlUnit), so I could fire submit of submit . >

Authentication:

After sending the request I created a form to get the cookies next to the response returned by the server. Thus, that problem of scanning all the HTML code of the page and "(...) searching for a word that only a logged in user could see (...)" has been resolved. As the authentication is done by cookie account, it will always be valid regardless of whether the user is using the forum in Portuguese or English.

The following is the code for the class:

import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.*;
import com.gargoylesoftware.htmlunit.util.Cookie;
import java.io.IOException;

public class VBulletinAuth {

    // Elementos no HTML
    private final String
    LOGIN_FORM_ID = "navbar_loginform",   // formulário de login
    LOGIN_USER_NAME = "vb_login_username",// input de username
    LOGIN_PASS_NAME = "vb_login_password",// input de senha
    LOGIN_REMEMBER_NAME = "cookieuser";   // checkbox para manter os cookies


    // Homepage em que exista um formulário de login
    private final String
    HOMEPAGE = "http://www.forum.com.br/forum";

    // cookie para verificar se o usuário logou
    private final String
    SESSION_HASH = "bb_sessionhash";

    private final WebClient webClient;
    private CookieManager cookies;

    public VBulletinAuth() {
        webClient = new WebClient(BrowserVersion.CHROME);
        webClient.getOptions().setCssEnabled(false);
        webClient.getOptions().setJavaScriptEnabled(false);
    }

    private boolean hasAccount(String user, String pass){

        try {
            // Requisição GET para obter a página de login
            HtmlPage homePage = webClient.getPage(HOMEPAGE);

            // Procurando pelo formulário de login
            HtmlForm loginForm = null;
            for(HtmlForm form : homePage.getForms())
                if(form.getId().equals(LOGIN_FORM_ID))
                    loginForm = form;

            // Obtendo os inputs do formulário  e inserindo os dados de login
            HtmlTextInput usernameInput = loginForm.getInputByName(LOGIN_USER_NAME);
            HtmlPasswordInput passwordInput = loginForm.getInputByName(LOGIN_PASS_NAME);
            HtmlCheckBoxInput rememberMeInput = loginForm.getInputByName(LOGIN_REMEMBER_NAME);

            usernameInput.setValueAttribute(user);
            passwordInput.setValueAttribute(pass);
            rememberMeInput.setChecked(true);

            // Criando o botão 'fake' de submit e inserindo ele dentro do formulário de login
            HtmlElement fakeSubmitButton = (HtmlElement) homePage.createElement("button");
            fakeSubmitButton.setAttribute("type", "submit");
            loginForm.appendChild(fakeSubmitButton);

            // criando uma instância de cookie manager para obter os cookies
            cookies = new CookieManager();
            cookies = webClient.getCookieManager();
            cookies.setCookiesEnabled(true);
            webClient.setCookieManager(cookies);

            // Envia o formulário
            fakeSubmitButton.click();

            // Verifica se foi criado o cookie de sessao
            for(Cookie cookie : cookies.getCookies())
                if(cookie.getName().equals(SESSION_HASH))
                    return true;

        } catch(IOException | FailingHttpStatusCodeException e){
            // tratamento de exceções
        }
        return false;
    }

    // TESTE:
    public static void main(String[] args) {
        VBulletinAuth auth = new VBulletinAuth();

        if(auth.hasAccount("renan", "12345")){
            // é membro do fórum
        }

    }
}
    
09.01.2015 / 10:23
3

The "right" way to do this would be to use a Web Service that returns only the required data. However, in a brief survey of the VBulletin website and documentation, I did not find just one reference. So let's do as it's already done, that is, by inspecting the HTML code.

As for the language problem, it is appropriate to use a parser like JSoup or Jericho to correctly identify the elements of the page and then check some HTML tags that are common across all languages.

For example, let's assume that the welcome message is displayed inside a tag, like this:

<p id="welcome" class="welcome-title">Bem vindo, Usuário</p>

So, instead of looking for the text "Welcome," it might be best to check the tag with class .welcome-title or id welcome .

    
05.01.2015 / 20:41