Integrating servlet container authentication with Wicket

First of all the difference between authentication and authorization should be mentioned. It is that authentication determines who is currently using software and authorization determines if user has access to certain resource. In the case of integrating servlet container authentication with Wicket, servlet container will authenticate user and Wicket will authorize him.
There might be several ways to do it. I found the following way most convenient. The idea is to define security constraint for the login page only while using Wicket authorization to redirect user to that login page. So that when user goes to a page that requires authorization Wicket redirects user to the login page which activates servlet container authentication. User logges in. User name and user roles are available from HttpServletRequest and can be put in Wicket session to use them for Wicket authorization. To make it more concrete here is some code.

MyApplication.java

public final class MyApplication extends WebApplication
{
...
    @Override
    protected void init()
    {
        // setting page that Wicket will display if user has no rights to access a page
        getApplicationSettings().setAccessDeniedPage( LoginPage.class );
        // setting authorization strategy (you can use any strategy you like)
        getSecuritySettings().setAuthorizationStrategy( new RoleAuthorizationStrategy( new MyRoleCheckingStrategy() ) );
        // mounting login page so that it can be referred to in the security constraint
        mountBookmarkablePage( "/login", LoginPage.class );
    }

    /**
     * Overriding newWebRequest so that to store take user information from
     * servletRequest and put it into wicket session.
     */
    @Override
    protected WebRequest newWebRequest( final HttpServletRequest servletRequest )
    {
        final WebRequest webRequest = super.newWebRequest( servletRequest );
        final Session session = getSessionStore().lookup( webRequest );
        if( session != null )
        {
            /* Save user info into session. */
            ( ( MySession )session ).takeUserFromRequest( servletRequest );
        }
        return webRequest;
    }
...
}

web.xml

    <security-constraint>
        <display-name>Constraint name</display-name>
        <web-resource-collection>
            <web-resource-name>Login page</web-resource-name>
<!-- restricting access to login page-->
            <url-pattern>/app/login</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>*</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>Default</realm-name>
        <form-login-config>
            <!--  The page on which servlet container will go in case user access URL defined in security constraint -->
            <form-login-page>/app/login</form-login-page>
            <!--  The page on which servlet container will go in case it failed to authenticate user. You can set any page you like. -->
            <form-error-page>/app</form-error-page> 
        </form-login-config>
    </login-config>

If you want to login before you go to any secured page (like login panel on every page), servlet container authentication can cause problems because a secure page must be accessed before user can authenticate. There's common workaround that is used in the LoginPage. If it's not your case LoginPage class can be empty and LoginPage.html can post directly to j_security_check.

LoginPage.html

<form action="/app/login" id="">
    Login:<input name="username" type="text" value=""/><br/>
    Password:<input name="password" type="password" value=""/><br/>
    <input type="submit" value="Login"/>
</form>

LoginPage.java

public final class LoginPage extends BasePage
{
    public LoginPage()
    {
        if( ( ( MySession )getSession() ).isUserLoggedIn())
        {
            // redirect to hide username and password from URL after user is logged in
            setRedirect( true );
            setResponsePage( SamplePage.class );
        }
        else
        {
            redirectToSecurityCheck();
        }
    }

    /**
     * Common servlet login workaround
     */
    private void redirectToSecurityCheck()
    {
        final Map parametersMap = ( ( WebRequestCycle )RequestCycle.get() ).getWebRequest().getHttpServletRequest().getParameterMap();
        if( parametersMap.containsKey( "username" ) && parametersMap.containsKey( "password" ) )
        {
            // getting parameters from POST request
            final String userName = ( ( String[] )parametersMap.get( "username" ) )[ 0 ];
            final String userPassword = ( ( String[] )parametersMap.get( "password" ) )[ 0 ];

            // if POST parameters are ok, redirect them to j_security_check
            if( ( userName != null ) && ( userPassword != null ) )
            {
                getRequestCycle().setRedirect( false );
                getRequestCycle().setRequestTarget( EmptyRequestTarget.getInstance() );

                // NOTE: Posting username and password to j_security_check like this will 
                // display username and password in access logs. Be careful!
                getResponse().redirect(
                        "/j_security_check?j_username=" + userName + "&j_password=" + userPassword );
            }
        }
    }
} care

And the main advice on this kind of integration is to avoid it if you can, especially because posting to j_security_check directly like above will result in the username and password being displayed in the access logs. You should use wicket authentication or write the form that post directly to j_security_check.

  • No labels