Showing posts with label Spring Security. Show all posts
Showing posts with label Spring Security. Show all posts

May 31, 2012

Security annotations in Spring Controller methods


There are few things you need to know for securing your controller methods using security annotations.You will need to first configure global-method-security in you context

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

If you method annotations are @Secured set secured-annotations=enabled and if you plan to use @PreAuthorize or @PostAuthorize enable pre-post-annotations=enabled.

The placement of this tag is also important, you need to place this tag in your mvc-context.xml instead of security-context.xml, your mvc-context.xml is the one configured for the DispatcherServlet.

Web.xml

<context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/spring/mvc-context.xml /WEB-INF/spring/security-context.xml</param-value>
       </context-param>
<!-- Processes application requests -->
       <servlet>
              <servlet-name>appServlet</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <init-param>
                     <param-name>contextConfigLocation</param-name>
                     <param-value>/WEB-INF/spring/mvc-context.xml</param-value>
              </init-param>
              <load-on-startup>1</load-on-startup>
       </servlet>


There are suggestions to place global-method-security tag after component-scan tag in your context, but I didn't think it made any difference in my case.

<context:component-scan base-package="com.ironmountain.imconnect" />
<sec:global-method-security    secured-annotations="enabled" pre-post-annotations="enabled"/>



After this, you might hit Add CGLIB to the class path exception during initialization:

Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.


If so, you will need to add dependency for CGLIB.

The reason you need CGLIB is that, by default, Spring uses Java Dynamic Proxies to implement Aspect Oriented Programming support. This requires you to "program to interfaces rather than classes".
But we like to leverage component-scan wiring for controllers using @Controller annotation, which means controller are concrete classes and not implementation classes.
Meaning we need to generate class level proxies for security annotated classes, which requires CGLIB dependency.

So all this got my security annotation to work for access denied scenarios, but for  success scenarios I still got this error after the method was properly executed.

No mapping found for HTTP request with URI [/imchome/app/users] in DispatcherServlet with name 'appServlet'

My controller was declared as
@Controller
@RequestMapping(value = "/users")
public class ManageUsersController implements InitializingBean
{

And I had configured Tuckey’s URLrewriting filter to clean the URL from /imchome/app/users to /imchome/users.

My Controller worked perfectly fine, if I removed the secured annotations.

After hours of debugging, I realized that the problem was that my controller was implementing InitlizingBean interface.
By implementing InitlizingBean interface my controller became an implementation class, which should be wired through interface using spring’s dynamic proxy. Since I was creating the controller using component-scan as a concerete class, it was giving problem.

My solution was to not have my controller implement InitlizingBean.

So to get the security annotation to work (Spring AOP), you either need to have an implementation class created through its interface using Spring’s dynamic proxy or have a concrete class which generates a dynamic class proxy using CGLIB. PS: even if you implement a blank interface, you will need to follow the first option.

Apr 1, 2012

Spring Security - Single Sign-on Emulator


Spring Security comes with Preauthentication framework that allows authentication using an external authenticated system ( Single Sign-On  solutions- IBM’s WebSEAL, CA Siteminder) while still using internal authentication provider for authorization.

Spring’s preauthentication framework reads SSO security tokens (populated by external SSO provider) and populates user identity in its context, authorities and UserDetails are still loaded by authentication providers (JDBC, LDAP..) configured in security configuration.

Usually, enterprise SSO solutions are expensive and their licenses are not always extended to developer’s sandbox. As a result, workarounds are devised for developers that can compromise security, like hard-coding/commenting authentication piece( imagine if that makes it to production).  Also, there might be times when production support requires direct access to the application without going through SSO channel (SSO systems can be buggy too :)).

We implemented a configurable SSO emulator using spring security's filter chain that can mimic external authentication system on developer’s sandboxes. In SSO enabled environment the request is authenticated by external authentication system while on non-SSO environments it is authenticated by internal providers. In both cases the authorization (userdetailsservice) was common and agnostic of the authentication mechanism.


Spring Security makes every incoming request pass through a filter chain, where each filter inspects and validates the request based on it configuration before handing it over to next filter.  Spring allows you to write you own custom filters and place them in the filter chain. You have to be really careful with the sequencing of your filters because there are inter dependencies between them.  I had to spent a lot of time to get the filter sequencing right :)


We configured two custom filters and place them infront of preautheticated filter.

First filter (CustomFormAuthenticationFIlter) inspects the request to check where it is coming from:
  •         If it was coming from SSO system, then assume request authenticated and continue with the filter chain for pre-authentication scenario.
  •         If request was not coming from SSO then invoke spring’s form-login and authenticate using configure authentication provide.

Second filter (SSOAuthenticationTokenPopulatorFilter) inspects the request for the authentication mechanism
  •        If request is authenticated using internal customer authentication provider, then populate the SSO security tokens.


CustomFormAuthenticationFIlter:
public class CustomFormAuthenticationFilter extends
              UsernamePasswordAuthenticationFilter {

       @Override
       public void doFilter(ServletRequest req, ServletResponse res,
                     FilterChain chain) throws IOException, ServletException {             
             
              if (ssoAuthenticationTokenExists) {                    
                     chain.doFilter(req, res);

              } else {                    
                     super.doFilter(req, res, chain); 
              } 
       }      

}

SSOAuthenticationTokenPopulatorFilter: This filter will populate SSO Token if form-based authentication was successful. To populate the custom headers, we created our own custom RequestWrapper (extends HttpServletRequestWrapper), and after setting the custom header, replaced the request in the filter chain.

public class SSOAuthenticationTokenPopulatorFilter extends GenericFilterBean {
protected boolean isHeaderPopulationRequired() {
              Boolean customAuthentication = SecurityContextHolder.getContext()
                           .getAuthentication().getClass().isAssignableFrom(
                                  UsernamePasswordAuthenticationToken.class); 
             
              return customAuthentication;
 }

public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain chain) throws IOException, ServletException {
              if (isHeaderPopulationRequired ()) {

                     request = addCustomHeaders (
                                  (HttpServletRequest) request,
                                  (HttpServletResponse) response);               
              }
              chain.doFilter(request, response);

}


public HttpServletRequest addCustomHeaders(HttpServletRequest request, HttpServletResponse response)
       {             
              AbstractAuthenticationToken principal = (AbstractAuthenticationToken) request.getUserPrincipal();
              request = new CustomRequestWrapper(request);
              String sUserID = principal.getName(); 
             
              ((CustomRequestWrapper) request).addCustomHeader("SSOAuthenticationToken", sUserID);
             
              return request;
       }

}

public class CustomRequestWrapper extends HttpServletRequestWrapper {
       private Map<String, String> customHeaders = new HashMap<String, String>();

       public CustomRequestWrapper (HttpServletRequest request) {
              super(request);
       }

       public void addCustomHeader(String name, String value){
              customHeaders.put(name, value);
       }
       public String getHeader(String name){
              String header = null;
              if (customHeaders.containsKey(name)){
                     header = (String) customHeaders.get(name);
              } else {
                     header = super.getHeader(name);
              }
             
              return header;
       }

}

Now the most important piece, sequencing of filters:
·         customFormAutenticationhFilter
·         ssoAuthenticationTokenPopulatorFilter
·         Pre-authenticated (RequestHeaderAuthenticationFilter)

I sequenced by explicitly specifying before/after position for the filters, but I guess we can directly specify it in filters attribute of intercepting url.

Sample code is available @ https://github.com/romiawasthy/ssoemulator. 


       <sec:http use-expressions="true">
              <sec:intercept-url pattern="/**" access="isAuthenticated()" />
<sec:custom-filter ref="customFormAutenticationhFilter" before="FORM_LOGIN_FILTER" />
              <sec:custom-filter ref="ssoAuthenticationTokenPopulatorFilter"
                     after="ANONYMOUS_FILTER" />

<sec:custom-filter ref="preAuthenticatedFilter"
                     before="EXCEPTION_TRANSLATION_FILTER" />       
              <sec:form-login login-processing-url="/login_security_check"
                     always-use-default-target="false" authentication-failure-url="/spring_security_login?login_error" />
              <sec:logout />      

       </sec:http>

Or simply

<sec:http use-expressions="true">
              <sec:intercept-url pattern="/**" access="customFormAuthenticationFilter, ssoAuthenticationTokenPopulatorFilter, preAuthenticatedFilter " />
             
              <sec:form-login login-processing-url="/login_security_check"
                     always-use-default-target="false" authentication-failure-url="/spring_security_login?login_error" />
              <sec:logout />
       </sec:http>

Now you have a SSO emulator configured.