| |
| |
| 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} |