Securing and customizing pages with Spring

Now that we are up and running with a basic authentication setup, let’s take it a step further. We would like to add our own login page now, instead of using the one that Spring generated for us. Then we will see how to restrict parts of the application for certain users. When we are done with that, we will put the icing on the cake by displaying the username next to each greeting on the screen.

1. Add custom logic to the security-context.xml file

src/main/webapp/WEB-INF/security-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
	xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       	xmlns:security="http://www.springframework.org/schema/security"
       	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/security
           http://www.springframework.org/schema/security/spring-security-3.0.xsd">
 
        <security:global-method-security secured-annotations="enabled" /> 
 
    <!--if you wish to use @PreAuthorize instead... -->
    <!--<security:global-method-security pre-post-annotations="enabled"/> -->
 
	<security:http auto-config="true" access-denied-page="/WEB-INF/jsp/accessdenied.jsp"> 
	   <!-- note that the order matters!! -->
           <security:intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
           <security:intercept-url pattern="/WEB-INF/jsp/restrictedarea.jsp" access="ROLE_ADMIN" />
           <security:intercept-url pattern="/**" access="ROLE_USER" />
           <security:form-login login-page="/login.jsp" 
                             login-processing-url="/loginProcess" 
                             default-target-url="/index.jsp" 
                             authentication-failure-url="/login.jsp?login_error=1"/>
           <security:logout logout-url="/logout" logout-success-url="/login.jsp" />
        </security:http>
 
	<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>
 
	<security:authentication-manager>
		<security:authentication-provider>
				<security:jdbc-user-service data-source-ref="dataSource"/>
		</security:authentication-provider>
	</security:authentication-manager>
 
</beans>

2. Create a custom login page called login.jsp

The login page we have seen was automatically generated by Spring. Let’s now create our own so we can customize it and add styling if we want later.

src/main/webapp/login.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
 
<%@ page import="org.springframework.security.web.authentication.AuthenticationProcessingFilter" %>
<%@ page import="org.springframework.security.web.authentication.AbstractProcessingFilter" %>
<%@ page import="org.springframework.security.core.AuthenticationException" %>
 
<html>
<body>
<h1>Login</h1>
 
	<form name="f" action="<c:url value="/loginProcess" />" method="post">
		<table>
			<tr>
				<td>Username:</td>
				<td><input type="text" 
						 name="j_username" 
						 id="j_username" <c:if test="${not empty param.login_error}">value="<%= session.getAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY) %>"</c:if> /></td>
			</tr>
			<tr>
				<td>Password:</td>
				<td><input type="password" name="j_password" id="j_password" /></td>
			</tr>
			<tr><td colspan="2" align="center"><input name="submit" id="submit" type="submit" value="Login" /></td></tr>
		</table>
	</form>
	<c:if test="${not empty param.login_error}">
		<font color="red">
			Login failure!<br />
			Reason: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %>
		</font>
	</c:if>
 
<sec:authorize access="isAuthenticated()">
   You are currently logged in!
</sec:authorize>
 
</body>
</html>

3. Create a page called accessdenied.jsp

src/main/webapp/WEB-INF/jsp/accessdenied.jsp
<html>
<body>
Sorry, you do not have permission to access this page.<br/>
Click <a href="/springgreetings/logout">here</a> to login as an administrator.
</body>
</html>

4. Create a page called restrictedarea.jsp

src/main/webapp/WEB-INF/jsp/restrictedarea.jsp
<html>
<body>
Congrats!<br/>
It appears you were able to enter the restricted area.<br/>
That means you are an administrator. Lucky you :-) 
</body>
</html>

5. Update index.jsp to link to the new pages

We have created two new pages, restrictedarea.jsp and accessdenied.jsp and we just need to link to the pages now. We will use spring security tags to secure the restricted area so that only users with a role “admin” can go there. Any other users with inferior roles will see an “access denied” page when they attempt to enter the restricted area.

src/main/webapp/index.jsp
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
 
<html>
<body>
<h2>Welcome to Spring Greetings!</h2>
 
<p><a href="/springgreetings/home/addgreeting.html">Add greeting</a><br/>
<a href="/springgreetings/home/greetings.html">Show all greetings</a><br/>
<sec:authorize access="hasRole('ROLE_ADMIN')">
<a href="/springgreetings/restrictedarea.jsp">Restricted area...</a><br/>
</sec:authorize>
<a href="/springgreetings/logout">Logout</a>
 
</body>
</html>

Now redeploy the application to tomcat and see our new custom login page when you enter this url

http://localhost:8080/springgreetings

Now add the username to the greetings…

Now instead of presenting each greeting with “@Anonymous says”, let’s write the username of the user that’s logged in.

First we will modify the Greeting.java class to add the username field

	@Column(name = "USERNAME")
	private String username;
 
	public String getUsername() {
		return username;
	}
 
	public void setUsername(String username) {
		this.username = username;
	}

Then we just need to modify this method in GreetingController.java to fetch the user details (adding the imports org.springframework.security.core.userdetails.UserDetails AND org.springframework.security.core.context.SecurityContextHolder)

	@RequestMapping(value = "/greetings.html", method = RequestMethod.POST)
	public String addGreetingAndShowAll(@ModelAttribute("greetingform") GreetingForm greetingForm,
			Map<String, Object> model) {		
 
		logger.info("entering addGreetingAndShowAll()");
 
		Greeting greeting = greetingForm.getGreeting();
		greeting.setGreetingDate(new Date());
		UserDetails userDetails = (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		greeting.setUsername(userDetails.getUsername());
		greetingService.addGreeting(greeting);  
 
		List<Greeting> greetings = greetingService.getAllGreetings();
		model.put("greetinglist", greetings);
 
	    	String selectedColorCode=greetingForm.getColor().getColorCode(); 
	    	if(selectedColorCode.equals("")) //if no color selected, then make default white
	    		selectedColorCode="FFFFFF";
	    	model.put("colorcode", selectedColorCode);
 
	    	// This will resolve to /WEB-INF/jsp/greetings.jsp
	    	return "greetings";
	}

And in greetings.jsp we replace this line of code

<br/><b>@Anonymous</b> says<br/>

with this one

<br/><b>@<c:out value="${greeting.username}" /></b> says<br/>

Before redeploying the application to tomcat, we need to drop the table from the Database Manager to avoid errors with the missing column. So we login to the database manager and execute “DROP TABLE GREETINGS;” and then the table will be newly generated when we redeploy and restart the application.

Securing methods in java classes using annotations…

Inside GreetingController.java it is possible to secure methods as well. Here is how:

Since we defined in security-context.xml that annotations are enabled

 <security:global-method-security secured-annotations="enabled" />

then we can secure methods in java classes as follows:

	@Secured("ROLE_ADMIN")
	@RequestMapping(value = "/greetings.html", method = RequestMethod.POST)
	public String addGreetingAndShowAll(@ModelAttribute("greetingform") GreetingForm greetingForm,
			Map<String, Object> model) {		
 
			.....
	}

We just need to add a default constructor to GreetingController.java for some reason,

public GreetingController() {}

otherwise we get this error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘greetingController’ defined in file [/usr/local/tomcat/webapps/springgreetings/WEB-INF/classes/com/bitbybit/web/controller/GreetingController.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.bitbybit.web.controller.GreetingController]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given

Here is the source code.


0 Comments

Post a Comment

(required):