blob: 4f1655cecdd97bb45a1019176d0874672124016b [file] [log] [blame]
The first step in implementing a security policy is assigning a trusted identity to our users, which means that we must authenticate them. Web applications usually adopt a form-based authentication with a login form that asks user for a unique username and the relative password:
!wikipedia-login-form.png!
Wicket supports form-based authentication with session class @AuthenticatedWebSession@ and application class @AuthenticatedWebApplication@, both placed inside package @org.apache.wicket.authroles.authentication@.
h3. AuthenticatedWebSession
Class AuthenticatedWebSession comes with the following set of public methods to manage user authentication:
* *authenticate(String username, String password)*: this is an abstract method that must be implemented by every subclass of @AuthenticatedWebSession@. It should contain the actual code that checks for user's identity. It returns a boolean value which is true if authentication has succeeded or false otherwise.
* *signIn(String username, String password)*: this method internally calls authenticate and set the flag signedIn to true if authentication succeeds.
* *isSignedIn()*:getter method for flag signedIn.
* *signOut()*: sets the flag signedIn to false.
* *invalidate()*: calls signOut and invalidates session.
{warning}
Remember that signOut does not discard any session-relative data. If we want to get rid of these data, we must invoke method invalidate instead of signOut.
{warning}
Another abstract method we must implement when we use @AuthenticatedWebSession@ is getRoles which is inherited from parent class @AbstractAuthenticatedWebSession@. This method can be ignored for now as it will be discussed later when we will talk about role-based authorization.
h3. AuthenticatedWebApplication
Class AuthenticatedWebApplication provides the following methods to support form-based authentication:
* *getWebSessionClass()*: abstract method that returns the session class to use for this application. The returned class must be a subclass of @AbstractAuthenticatedWebSession@.
* *getSignInPageClass()*: abstract method that returns the page to use as sign in page when a user must be authenticated.
* *restartResponseAtSignInPage()*: forces the current response to restart at the sign in page. After we have used this method to redirect a user, we can make her/him return to the original page calling @Componet@'s method @continueToOriginalDestination()@.
The other methods implemented inside @AuthenticatedWebApplication@ will be introduced when we will talk about authorizations.
h3. A basic example of authentication
Project @BasicAuthenticationExample@ is a basic example of form-based authentication implemented with classes @AuthenticatedWebSession@ and @AuthenticatedWebApplication@.
The homepage of the project contains only a link to page @AuthenticatedPage@ which can be accessed only if user is signed in. The code of @AuthenticatedPage@ is this following:
{code}
public class AuthenticatedPage extends WebPage {
@Override
protected void onConfigure() {
super.onConfigure();
AuthenticatedWebApplication app = (AuthenticatedWebApplication)Application.get();
//if user is not signed in, redirect him to sign in page
if(!AuthenticatedWebSession.get().isSignedIn())
app.restartResponseAtSignInPage();
}
@Override
protected void onInitialize() {
super.onInitialize();
add(new Link("goToHomePage") {
@Override
public void onClick() {
setResponsePage(getApplication().getHomePage());
}
});
add(new Link("logOut") {
@Override
public void onClick() {
AuthenticatedWebSession.get().invalidate();
setResponsePage(getApplication().getHomePage());
}
});
}
}
{code}
Page @AuthenticatedPage@ checks inside onConfigure if user is signed in and if not, it redirects her/him to the sign in page with method @restartResponseAtSignInPage@. The page contains also a link to the homepage and another link that signs out user.
The sign in page is implemented in class @SignInPage@ and contains the form used to authenticate users:
{code}
public class SignInPage extends WebPage {
private String username;
private String password;
@Override
protected void onInitialize() {
super.onInitialize();
StatelessForm form = new StatelessForm("form"){
@Override
protected void onSubmit() {
if(Strings.isEmpty(username))
return;
boolean authResult = AuthenticatedWebSession.get().signIn(username, password);
//if authentication succeeds redirect user to the requested page
if(authResult)
continueToOriginalDestination();
}
};
form.setDefaultModel(new CompoundPropertyModel(this));
form.add(new TextField("username"));
form.add(new PasswordTextField("password"));
add(form);
}
}
{code}
The form is responsible for handling user authentication inside its method onSubmit. The username and password are passed to @AuthenticatedWebSession@'s method @signIn(username, password)@ and if authentication succeeds, the user is redirected to the original page with method @continueToOriginalDestination@.
The session class and the application class used in the project are reported here:
*Session class:*
{code}
public class BasicAuthenticationSession extends AuthenticatedWebSession {
public BasicAuthenticationSession(Request request) {
super(request);
}
@Override
public boolean authenticate(String username, String password) {
//user is authenticated if both username and password are equal to 'wicketer'
return username.equals(password) && username.equals("wicketer");
}
@Override
public Roles getRoles() {
return null;
}
}
{code}
*Application class:*
{code}
public class WicketApplication extends AuthenticatedWebApplication{
@Override
public Class<HomePage> getHomePage(){
return HomePage.class;
}
@Override
protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass(){
return BasicAuthenticationSession.class;
}
@Override
protected Class<? extends WebPage> getSignInPageClass() {
return SignInPage.class;
}
}
{code}
The authentication logic inside authenticate has been kept quite trivial in order to make the code as clean as possible. Please note also that session class must have a constructor that accepts an instance of class @Request@.
h3. Redirecting user to an intermediate page
Method @restartResponseAtSignInPage@ is an example of redirecting user to an intermediate page before allowing him to access to the requested page. This method internally throws exception @org.apache.wicket.RestartResponseAtInterceptPageException@ which saves the URL of the requested page into session metadata and then redirects user to the page passed as constructor parameter (the sign in page).
Component's method @redirectToInterceptPage(Page)@ works in much the same way as @restartResponseAtSignInPage@ but it allows us to specify which page to use as intermediate page:
{code}
redirectToInterceptPage(intermediatePage);
{code}
{note}
Since both @restartResponseAtSignInPage@ and @redirectToInterceptPage@ internally throw an exception, the code placed after them will not be executed.
{note}