Simple Form Login Page with Apache Sling

In this posts we will show a simple example on how to create a login page with Apache Sling and also define which paths will ask for the user to login in the site using our login page.

In order to execute the example we need to have the Sling Lauchpad running in our machine. For this we can download the binary from the Apache Sling website in Downloads Section.

If you have Docker installed in your machine you can use the Sling docker image.

Once you have you Sling Launchpad running we can now create our project.

Apache Sling provide some Maven Archetypes to create Sling Based projects. We are going to use sling-intitial-content-archetype. The complete list of the available archetypes can be found here.

By using eclipse we need to go:

File -> New.. -> Project -> Maven -> Maven Project

New Project

Then in the archetype selection choose the sling-intitial-content-archetype.

Archetype Selection

Then enter the groupid and artifactid for your project.

Create Login Page

For the login page I’ve used a Bootstratp Form.

First we are going to create the rendering script for the login page. For this we will create the following folder structure on src/main/resources/SLING-INF

sling-form-login/login

Inside this folder we create the login.html. This will be the script that will be called when the login page is requested. We can see it below:

  <div class="container">
        <div class="row">
            <div class="col-sm-6 col-md-4 col-md-offset-4">
                <h1 class="text-center login-title">Sign in to continue to Bootsnipp</h1>
                <div class="account-wall">
                    <img class="profile-img" src="https://lh5.googleusercontent.com/-b0-k99FZlyE/AAAAAAAAAAI/AAAAAAAAAAA/eu7opA4byxI/photo.jpg?sz=120"
                        alt="">
                    <form method="POST" action="/j_security_check" autocomplete="off" class="form-signin">
                    <input type="text" class="form-control" placeholder="Username" required autofocus name="j_username">
                    <input type="password" class="form-control" placeholder="Password" required name="j_password">
                    <input type="hidden" name="sling.auth.redirect" value="/content/page/home.html">
                    <button class="btn btn-lg btn-primary btn-block" type="submit">
                        Sign in</button>
                    <label class="checkbox pull-left">
                        <input type="checkbox" value="remember-me">
                        Remember me
                    </label>
                    <a href="#" class="pull-right need-help">Need help? </a><span class="clearfix"></span>
                    </form>
                </div>
                <a href="#" class="text-center new-account">Create an account </a>
            </div>
        </div>
    </div>

The most important parts of this snippet are:

Now we need to create a node in the repository that will use this rendering script. For this we need to create a new file under SLING-INF/content:

login.json 

with the content below:

{
    "jcr:primaryType" : "nt:unstructured",
    "sling:resourceType" : "sling-form-login/login"
}

With that JSON file we are defining a new node in the repository which have nt:unstructured as primary type and sling-form-login/login as resource type. With this configuration we’ve defined that this node we will use the rendering script we defined in the last step.

In order to test if our configurations are ok, we need to deploy our project into Sling Launchpad. Before we deploy it, we need to make some adjustments in our pom.xml. In the maven-bundle-plugin we will have the following config:

<Sling-Initial-Content>
    SLING-INF/apps/sling-form-login;overwrite:=true;uninstall:=true;path:=/apps/sling-form-login,
    SLING-INF/content;overwrite:=true;uninstall:=true;path:=/content
</Sling-Initial-Content>

Once we change it, we can deploy our project by running the following maven command:

mvn clean install -P autoInstallBundle

If everything goes well then we can access the following url:

http://localhost:8080/login.html

And we can see our login page:

Login Page

Create a Content Page

Now we are going to create the page we want to protect using our login screen. This will be a simple page that will display the current user login name.

Basically we are going to use the same steps we followed for the login page.

Create the Rendering Script at /SLING-INF/sling-form-login/page

//page.jsp
<%@ page session="false" %>
<%@ page import="javax.jcr.*,
        org.apache.sling.api.resource.Resource"
%>
<%@ taglib prefix="sling" uri="http://sling.apache.org/taglibs/sling/1.0" %>
<sling:defineObjects />
<%
    String userID = resourceResolver.adaptTo(javax.jcr.Session.class).getUserID();
%>
<html>
    <body>

        <h1>
            Hello, <%=userID%>
        </h1>
    </body>
</html> 

Basically this JSP gets the current user and display its userId.

Then create the node that will use the rendering script at /SLING-INF/content/page/home.json

{
    "jcr:primaryType" : "nt:unstructured",
    "sling:resourceSuperType" : "sling-form-login/page"
}

Then we deploy our project again. If everything is fine than can access the following URL:

http://localhost:8080/content/page/home.html

And see this:

Login Page

If you were logged then you can see your user id.

Set the home.html as a protected page

To define that our content page(home.html) requires authentication, we need to configure the Sling Authentication Service.

We can do this by the Felix Console

http://localhost:8080/system/console/configMgr

Then find the Sling Authentication Service, if you click you can see the details like below:

Sling Authentication Service

We need to set the Authentication Requirements(sling.auth.requirements) by entering the paths that will required authentication.

Another possible way is creating a sling:OsgiConfig node in the repository. This is the way we will use.

For this we will create a node which name is the PID. We can find the PID in the bottom of the above screenshot where we see Persistent Identity (PID), so we are going to create a new node under:

/SLING-INF/sling-form-login/install

And name it as:

org.apache.sling.engine.impl.auth.SlingAuthenticator.json

And its contents will be like below:

{
    "jcr:primaryType": "sling:OsgiConfig",
    "sling.auth.requirements": [
      "+/content/page"
    ]
}

With this configuration, we are saying that all paths starting with /content/page require authentication to access them. We set this by adding the plus(+) sign before the path. If we used the minus(-) we were saying that the path doesn’t require authentication.

Now if we deploy our last change, when we try to access the content page we created we will be redirected to a login screen, in this case the default login page of the system.

Default Login Screen

If we log with admin/admin or any other user you have. It will redirect to our page and display the username:

Logged User Page

Great!! Now let’s take the next step.

Define the login Screen

The default login screen used by Sling is defined in the Apache Sling Form Based Authentication Handler. It has a property where we can set the login form as we can see below:

Form Authentication Handler Config

The property we need to change is Login Form(form.login.form).

To do this we will do something similar as we did for the Authentication Service, we will create a sling:OsgiConfig node using the PID as node name in this case

org.apache.sling.auth.form.FormAuthenticationHandler.json

Its contents can be seen below:

{
    "jcr:primaryType": "sling:OsgiConfig",
    "form.login.form": "/content/login.html"
}

After deploying our last change, if we try to access the home.html page not logged in we are redirected to our custom login page as we can see below:

Form Authentication Handler Config

If we login then we will see the home.html with our userId.

If you are using docker

In order to execute the sling image, first we need to download the image:

docker pull apachesling/sling

Then to create a container using the image:

docker run -d -p 8080:8080 apachesling/sling

Then it will start a container and expose the port 8080. If you are using docker-machine(that’s my case) you can not access localhost once the container is running the docker-machine VM.

You need to find the docker-machine IP. You cand do this using the following command:

docker-machine ip default

Also if you are on docker-machine remember to change you pom.xml to point to your docker-machine IP in order to be able to deploy the project.

That’s it! I hope you enjoyed this post. You can find all the source code in my Github.

Thanks, See you in the next post.

comments powered by Disqus