Pages

March 2, 2011

Integrating Spring Security 3 with Wicket

In an older post I spoke about integrating Spring Security 3 with Extjs (here), now I will speak about integrating Spring Security 3 with Wicket.
Tools:
- Wicket 1.4.15
- Maven 3.0.2
- Spring Security 3.0.5
- IDE: Netbeans 6.9.1
- Server: Tomcat VI
How to start?
Just create a simple wicket application with maven and add the following dependencies for the Spring framework:


        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.0.5.RELEASE</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>3.0.5.RELEASE</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>3.0.5.RELEASE</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>3.0.5.RELEASE</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>3.0.5.RELEASE</version>
            <type>jar</type>
        </dependency>

The wicket application class should extends AuthenticatedWebApplication.
After that you should add the following code in web.xml:


    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext-security.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

The Spring XML configuration file:



        
    <http create-session="never" auto-config="true" >
        <remember-me/>
        <intercept-url pattern="/**"/>
    </http>


    <!--
    Usernames/Passwords are
        moez/moez
        test/test
    -->
    <authentication-manager alias="authenticationManager" >
        <authentication-provider>
            <password-encoder hash="md5"/>
            <user-service>
                <user name="moez" password="f69471ca2c42621b8b8e731b2b4446e4" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
                <user name="test" password="098f6bcd4621d373cade4e832627b4f6" authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
    <global-method-security secured-annotations="enabled" />
</beans:beans>

The authenticated websession:

public class SpringWicketWebSession extends AuthenticatedWebSession {


    private static final Logger logger = Logger.getLogger(SpringWicketWebSession.class);

    @SpringBean(name = "authenticationManager")
    private AuthenticationManager authenticationManager;

    public SpringWicketWebSession(Request request) {
        super(request);
        injectDependencies();
        ensureDependenciesNotNull();
    }

    public static SpringWicketWebSession getSpringWicketWebSession() {
        return (SpringWicketWebSession) Session.get();
    }

    private void ensureDependenciesNotNull() {
        if (authenticationManager == null) {
            throw new IllegalStateException("Requires an authentication");
        }
    }

    private void injectDependencies() {
        InjectorHolder.getInjector().inject(this);
    }

    @Override
    public boolean authenticate(String username, String password) {
        boolean authenticated = false;
        try {
            Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
            SecurityContextHolder.getContext().setAuthentication(authentication);
            authenticated = authentication.isAuthenticated();
        } catch (AuthenticationException e) {
            logger.warn("User "+username+" failed to login. Reason: ", e);
            authenticated = false;
        }
        return authenticated;
    }

    @Override
    public Roles getRoles() {
        Roles roles = new Roles();
        getRolesIfSignedIn(roles);
        return roles;
    }

    private void getRolesIfSignedIn(Roles roles) {
        if (isSignedIn()) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            addRolesFromAuthentication(roles, authentication);
        }
    }

    private void addRolesFromAuthentication(Roles roles, Authentication authentication) {
        for (GrantedAuthority authority : authentication.getAuthorities()) {
            roles.add(authority.getAuthority());
        }
    }

    

}

source code here

8 comments:

  1. Thanks for the write up. You are more or less doing the same thing as described in the Wicket / Spring Security 3 Wiki.

    There is one difference - in SpringWicketWebSession you have a getSpringWicketWebSession() method. Is this necessary? Where is it used?

    I am having a problem (I posted on the mailing list) where a line in the getRolesIfSignedIn() method is failing:

    SecurityContextHolder.getContext().getAuthentication()

    It sometimes returns null. Any idea why this might be happening? I am at a loss. It is almost completely random but seems to happen more after loading a page with a Panel in it. But not always.

    I only need to put the @AuthorizeInstantiation tag in the Pages, correct? Not in the Forms or Panels contained within the Pages.

    I have been trying to get this working for over a day. And help much appreciated.

    Thanks,
    Bob

    ReplyDelete
  2. Solved.

    See this thread.

    It seems you need a Spring filter, *before* the Wicket filter.

    Bob

    ReplyDelete
  3. No, it's ok for me. You can try with my web.xml

    ReplyDelete
  4. Well it's ok with you because you only have one page. My Login page worked fine as well, as did many Links. But eventually, once you start really navigating, loading Pages with Forms and Panels, it's only a matter of time before a npi shows up.

    I was using exactly your web.xml and had this problem. Sticking a Spring filter in the mix fixed it. Fyi.

    Cheers,
    Bob

    ReplyDelete
  5. I added another page SuccessPage and always it's ok.
    I posted the new code.

    ReplyDelete
  6. Well, as I said, for me it worked for all Links to all Pages. The problems started happening once I had Forms and Panels.

    And it didn't always happen, it was completely random. The only real way to test it would be to make a page with a Form, and when you click on submit show a List of 'things' (Users, whatever) in a Panel, and click on one of those 'things' to see its details.

    if *that* all works consistently, after clicking many times, then I'd be convinced. But I am pretty sure it will fail.

    If not ... well, I have no idea why, but you're a lucky man!

    Bob

    ReplyDelete
  7. Thanks for this post. I was experiencing the same intermittent problem as @syg6 and the filter ordering fixed the problem.

    ReplyDelete
  8. Hi folks,

    has anybody got the remember-me feature to work? does it require aditional code? from my logs i can see that the server never sets a cookie when i log in.

    Any help appreciated.

    Oliver

    ReplyDelete