Introduction
An example of how to modify the SpringSurf quickstart application to authenticate users against an Alfresco server, ready to retrieve data from Alfresco REST APIs.For simplicity in this article, I have renamed the spring-surf-application-quickstart-1.0.0-RC2.war to ssqs.war - so all referenced URLs and files will use the shorter name. Also we will edit the deployed files directly to quickly show what you would add or change based on the SpringSurf example WAR. Obviously you wouldn't do this normally, but it keeps things simple - as the interesting parts of the article are what you need to do to get something useful working against Alfresco!The article also provides information on what happens under the covers during user login - you can skip the detail in those parts if you wish.Modifying the Sample Application
1. Test initial deployment.
Download spring-surf-application-quickstart-1.0.0-RC2.war file and rename it to ssqs.war.Deploy the ssqs.war to TomCat.Start the server and check it is working by navigating to http://YOURSERVER/ssqsYou will see a home page with a number of components on, including a component with the message Welcome to Alfresco Surf!2. Surf User Factory and Remote Configuration.
Stop Tomcat.Open file: \ssqs\WEB-INF\surf.xmlAll SpringSurf apps have this config file by default. It is where you can set a number of important boot configuration options, including the developer/production mode flag I mentioned in a previous post, plus other important framework defaults. The change here is to uncomment the following section: <!-- User factory for Alfresco 3.3 -->
<user-factory>webframework.factory.user.alfresco</user-factory>
This informs SpringSurf to make use of the Spring bean with the given ID when it needs to create a User instance. This bean is part of the SpringWebScripts project and will make a remote call back to an Alfresco Repository to the /api/login REST API to authenticate a user. The Alfresco UserFactory makes use of the 'alfresco' endpoint. The 'alfresco' endpoint is also part of SpringWebScripts configuration and is already wired up to the 'alfresco' connector which in turn uses the 'alfresco-ticket' authenticator. It is those objects that perform the hard work of authenticating and maintaining user credential information. If you are interested the default configuration for those objects can be found here:\SpringSurf\spring-webscripts\spring-webscripts\src\main\resources\org\springframework\extensions\webscripts\spring-webscripts-config.xml
Like all SpringSurf configuration, this can be overridden in your surf.xml file. It is a simple matter to set the location of your Alfresco repository by overriding the endpoint configuration, so add the following in surf.xml as a new child element: <config evaluator='string-compare' condition='Remote'>
<remote>
<endpoint>
<id>alfresco</id>
<name>Alfresco - user access</name>
<description>Access to Alfresco Repository WebScripts that require user authentication</description>
<connector-id>alfresco</connector-id>
<endpoint-url>http://ALFRESCOSERVER/alfresco/s</endpoint-url>
<identity>user</identity>
</endpoint>
</remote>
</config>
Edit ALFRESCOSERVER value with the server name and port as appropriate. The default install location is usually localhost:8080.3. Page Authentication.
The sample app currently does not have any pages that require authentication to view, so we'll change that now.Open file: ssqs\WEB-INF\pages\home\home.xmlEdit the authentication element as follows:<authentication>user</authentication>
This informs SpringSurf that a valid authenticated user (not the default user instance of 'guest') is required to view this page. When the SpringSurf page dispatcher processes this information during the home page url request, it will automatically perform a redirect to the 'login' page-type (more on this below). This will begin the authentication process.4. Login template.
Open file: \ssqs\WEB-INF\templates\sample\login.ftlThis is the example login template. The important parts to note here if you want to modify or create your own are the folllowing:<form accept-charset='UTF-8' method='post' action='${url.context}/dologin'>
The 'action' element for this Form references the SpringMVC controller with the id 'dologin'. This is a SpringSurf controller bean that is wired to attempt a login based on a POST request, and that POST must contain two fields as defined in our Form:<input name='username' type='text' />
<input name='password' type='password' />
These are the mandatory values it expects. Once the mandatory parameters are confirmed, the controller class will retrieve the appropriate UserFactory (as we configured above) and ask it to perform authentication. As mentioned above, this starts the process of the AlfrescoUserFactory authenticating via the 'alfresco' endpoint and it's associated authenticator class. If everything was successful, then a new User instance will be placed in the session with the Alfresco authentication information (usually a ticket value, but might be a cookie or similar) maintained by the framework for the life of that User instance, generally until their web session expires. We'll discuss it in detail shortly, but this means any further remote calls made by that user via the same 'alfresco' endpoint, such as Alfresco REST APIs, will automatically use the authentication information retrieved during login. This is how it works! And how Alfresco Share and other applications hide away the messy business of authentication from WebScript UI component writers - they simply work with a nice clean remoting API.If you are interested, the dologin controller is implemented by the class: org.springframework.extensions.surf.mvc.LoginController.It also has two other useful parameters 'success' and 'failure' which provide the redirect URLs SpringSurf should use after a login attempt. In Alfresco Share we redirect to the user dashboard page after a successful login attempt.Finally, edit the following form values so that the correct pages are shown after a successful or failed login to our application:<input name='success' type='hidden' value='${url.context}/home' />
<input name='failure' type='hidden' value='${url.context}/type/login' />
This ensures the 'home' page is shown on success, otherwise the 'login' page-type is shown again to allow retry.5. Login challenge page.
Start TomCat.Navigate to the root of the example app as before.You will see that our simple login form has appeared! As expected SpringSurf has detected that as the guest user we are not authenticated to the appropriate level to view the home page.How did SpringSurf know what login page to display? There are a number of ways to inform the framework as to what page you would like to use to authenticate users. In our surf.xml config for this example application, see the following section: <!-- Set up our sample login and logout pages -->
<page-type>
<id>login</id>
<page-instance-id>sample/login</page-instance-id>
</page-type>
<page-type>
<id>logout</id>
<page-instance-id>sample/logout</page-instance-id>
</page-type>
This configuration informs SpringSurf of the page instances to use for the 'special' login and logout page-types. A 'page-type' is really just a well known page that the framework may reference by id. So in this case the 'login' page-type has been defined as 'sample/login' and if you look in \ssqs\WEB-INF\templates\sample you will see two FreeMarker templates that implement those pages. Wondering why there are no page XML definition files in the \ssqs\WEB-INF\pages folder? The answer is that if no specific definition files are provided then SpringSurf will automatically work out what page id->page definition->template instance->template would be required and find those objects by declaration i.e. using the ID as the file name to look for. This saves having to create lots of little XML definition files that point to each other and eventually point to a concrete template file. So having a template with the same name as the page id is enough.Login as a valid Alfresco user!Use something like admin/admin or any user you have already created in your Alfresco server.The home page will appear! This means our authentication configuration has worked, and we can now create components that get data from Alfresco. Exciting.If you want to force a user logout, there is another SpringMVC controller called dologout that implements this. So navigate to http://YOURSERVER/ssqs/dologout and you will no longer be authenticated and the user session invalidated.6. Remote calls to Alfresco REST APIs
We are now ready to make remote APIs call to Alfresco! Any component bound into a page that has been marked as requiring user authentication can now safely make a remote call. You can use the CMIS APIs or the various Alfresco Share APIs if you are looking for site specific data. In my next post we look at the remote APIs in more detail, for now take a look at the various CMIS examples or examples in the Share components.