Tuesday, May 15, 2012

Automatic sign in after registration in Spring Security


I have a program that requires user will be automatically signed in after register so they won't be redirected to login page again after registration.

The following codes actually took me quite long time to figure out after read through many internet posts. Eventually I found some codes from stackoverflow.com.
(BTW, I like the name of the site. Perfectly make sense for a programmer.)

    @RequestMapping(value = "login.asp", method = RequestMethod.GET)
    public String doAutoLogin(@RequestParam(value="Email", required=true) String username,
            @RequestParam(value="Password", required=true) String password, HttpServletRequest request) {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);

        Authentication auth = authenticationManager.authenticate(token);

        // Place the new Authentication object in the security context.
        SecurityContextHolder.getContext().setAuthentication(auth);

        //this step is import, otherwise the new login is not in session which is required by Spring Security
        request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());

        return "redirect:home.htm";
    }


To register the authentication manager, put this in the same class:

    @Autowired
    @Qualifier("authenticationManager")
    protected AuthenticationManager authenticationManager;

In the security.xml, you need to set the alias:



    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="registerUserDetailsService">
            <password-encoder hash="md5" />
        </authentication-provider>
        <authentication-provider user-service-ref="registerUserDetailsService"/> <!-- this provider is used to login user from other system, which has encrypted password. -->
    </authentication-manager>

The second auth provider is almost exactly same as the first, except it doesn't require password encoder. I have this provider because we have a link from other system that has encrypted password, so I cannot have it encrypt again here. Otherwise, the password will be encrypted twice so that use cannot login from that link.
Spring will go through all those providers to validate username and password. As long as one of them passes, user will be able to sign in


Thursday, April 26, 2012

An alternative to Spring Security ACL

Spring Security ACL might be good, but the drawback is obvious too:
1. You need to save permission along with saving each business object
2. Need annotation on each method
3. Once you decide to change the permission setup, there is huge work to modify production data; and it is problematic and dangerous
4. You probably have the permission structure in place already. For example, Contract is created by UserA, so only UserA can change it. You will save duplicate info in ACL. Why not just reuse existing structure you already have.

So, I'd like to go back to the traditional Filter. However, I also want to utilize the power of Spring Security. It actually is easy. All you need is to add some configuration below Spring Security configuration in web.xml. The trick here is DelegatingFilterProxy actually access your bean name.

    <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>

    <filter>
        <filter-name>aclSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>myUserAccessFilter</param-value>
        </init-param>
    </filter>

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

myUserAccessFilter is just a bean you defined in the Spring applicationContext.xml:

    <bean id="myUserAccessFilter" class="com.foo.MyUserAccessFilter">
        <property name="myUserSerive" ref="myUserService" />
    </bean>

You just need to implement your MyUserAccessFilter like this:

package com.foo;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

public class MyUserAccessFilter implements Filter{
    protected final Log logger = LogFactory.getLog(getClass());
    
    @Autowired
    private MyUserService myUserService;
    
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try{
            User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            String[] contractIds = request.getParameterValues("contractId");

            checkUser(contractIds, currentUser);
            
        } catch(AccessDeniedException e){
            throw e;
        } catch(Exception ignored){
            logger.error(ignored, ignored);
        }
        
        chain.doFilter(request, response);
        
    }
}

Followers