Thursday, November 29, 2012

Spring Security Example

Spring Security Example

Here's a quick overview of how to use Spring Security. Since we are working on a Maven project, first add the following dependencies to the pom.xml.

pom.xml

<properties>
 <org.springframework-version>3.1.2.RELEASE</org.springframework-version>
</properties>

<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-core</artifactId>
 <version>${org.springframework-version}</version>
</dependency>
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-web</artifactId>
 <version>${org.springframework-version}</version>
</dependency>
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-config</artifactId>
 <version>${org.springframework-version}</version>
</dependency>
Next, it's time to create the context files related to spring security. You can work on top of root-context.xml, but I created a security-context.xml file.

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.1.xsd">

    <security:http auto-config="true" use-expressions="true">
        <security:intercept-url pattern="/favicon.ico" access="permitAll" />
        <security:intercept-url pattern="/resources/**" access="permitAll" />
        <security:intercept-url pattern="/signin" access="permitAll" />&nbsp;
        <security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        <security:form-login login-page="/signin"/>
        <security:logout  logout-success-url="/"/>
    </security:http>

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref="defaultAuthenticationProvider"/>
    </security:authentication-manager>
</beans>

In the above configuration file, we are specifying the location of the login form and the path to return after logout. To log out, you can see it in the View page and make a call like below to perform the logout function in spring security.

<a href="${requestContext.contextPath}/j_spring_security_logout">Sign-out</a>

To login, put j_spring_security_check in the action attribute of the form tag.

<form action="j_spring_security_check" method="POST"></form>

The authentication-manager in the context file allows you to inject the authentication-provider with the authentication method of your choice. (I named it defaultAuthenticationProvider, but don't let the default name fool you into thinking it's a spring-given provider^^;) Let's take a look at the defaultAuthenticationProvider implementation. Since this is an example, I've kept things as simple as possible.

public class DefaultAuthenticationProvider implements AuthenticationProvider {

 private static final Logger logger = LoggerFactory.getLogger(DefaultAuthenticationProvider.class);
 private static final Set setAdmin;
 static {
 setAdmin = new HashSet();
 setAdmin.add("admin");
 }

 private boolean isAdmin(Object principal) {
  return setAdmin.contains(principal);
 }

 @Override
 public Authentication authenticate(Authentication authentication) throws AuthenticationException {

  Object principal = authentication.getPrincipal();
  Object password = authentication.getCredentials();

  if(!isAuthenticatedUser(principal, password)) {
     throw new BadCredentialsException("Username does not match for " + principal);
  }

  Collection roles = new ArrayList();
  roles.add(new SimpleGrantedAuthority("ROLE_USER"));

  if(isAdmin(principal)) {
  roles.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
  }
  return new UsernamePasswordAuthenticationToken(principal, password, roles); 
 }

 @Override
 public boolean supports(Class clazz) {
  return clazz.equals(UsernamePasswordAuthenticationToken.class);
 }
}

Above, the logic for Authentication and Authorization depends on your own configuration. The ROLE for Authorization can be arbitrarily specified and saved multiple times, so you can use it in various ways. And in the contextConfigLocation of web.xml, specify the path to the xml containing the spring security context settings as shown below.

web.xml

<context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/spring/root-context.xml,  /WEB-INF/spring/security-context.xml</param-value>
</context-param>

<filter>
 <filter-name>springSecurityFilterChain</filter-name>
 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
 <filter-name>springSecurityFilterChain</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Now, if you fire up Tomcat and access the site, you'll be directed to /signin and see that the page that should have been called upon successful sign is called.

Other information

1. let's see how to get the token information stored in the SecurityContext in the View section.

@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
 SecurityContext securityContext = SecurityContextHolder.getContext();
 model.addAttribute("securityContext", securityContext);
 return "index";
}

First of all, when you take it out of the Controller, use the following code... (below is Freemarker)

<p>${securityContext.getAuthentication().getPrincipal()}</p>

2. Automatically sign in after signing up on top of Spring Security

    @Autowired
    protected AuthenticationManager authenticationManager;

    @RequestMapping(value = "/signup", method = RequestMethod.POST)
    public String signUp(Model model, @ModelAttribute Member member, HttpServletRequest request) {
 memberService.signUp(member);
 authenticateUserAndSetSession(member, request);
 return "redirect:/";
    }

    private void authenticateUserAndSetSession(Member member,
     HttpServletRequest request)
    {
 UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(member.getName(), member.getPassword());

 request.getSession();

 token.setDetails(new WebAuthenticationDetails(request));
 Authentication authenticatedUser = authenticationManager.authenticate(token);

 SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
    }