Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>To programmatically inject the Spring Security functionality into existing beans, you may need to use a Spring Security applicaton context and register your beans there:</p> <pre><code>@Test public void testSpringSecurity() throws Exception { InMemoryXmlApplicationContext ctx = initSpringAndSpringSecurity(); // Creates new instance IMyService secured = (IMyService) ctx.getAutowireCapableBeanFactory() .initializeBean(new MyService(), "myService"); assertTrue(AopUtils.isAopProxy(secured)); fakeSecurityContext("ROLE_USER"); secured.getCustomers(); // Works: @Secured("ROLE_USER") fakeSecurityContext("ROLE_FOO"); try { secured.getCustomers(); // Throws AccessDenied Exception fail("AccessDeniedException expected"); } catch (AccessDeniedException expected) { } } private InMemoryXmlApplicationContext initSpringAndSpringSecurity() { InMemoryXmlApplicationContext ctx = new InMemoryXmlApplicationContext( "&lt;b:bean id='authenticationManager' class='org.springframework.security.MockAuthenticationManager' /&gt; " + "&lt;b:bean id='accessDecisionManager' class='org.springframework.security.vote.UnanimousBased'&gt;&lt;b:property name='decisionVoters'&gt;&lt;b:list&gt;&lt;b:bean class='org.springframework.security.vote.RoleVoter'/&gt;&lt;/b:list&gt;&lt;/b:property&gt;&lt;/b:bean&gt;" + "&lt;b:bean id='objectDefinitionSource' class='org.springframework.security.annotation.SecuredMethodDefinitionSource' /&gt; " + "&lt;b:bean id='autoproxy' class='org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator'/&gt;" + "&lt;b:bean id='methodSecurityAdvisor' class='org.springframework.security.intercept.method.aopalliance.MethodDefinitionSourceAdvisor' autowire='constructor'/&gt;" + "&lt;b:bean id='securityInterceptor' class='org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor'&gt;&lt;b:property name='authenticationManager' ref='authenticationManager' /&gt;&lt;b:property name='accessDecisionManager' ref='accessDecisionManager' /&gt;&lt;b:property name='objectDefinitionSource' ref='objectDefinitionSource' /&gt;&lt;/b:bean&gt;"); return ctx; } </code></pre> <p>I used an in-memory application context, as the <code>MethodDefinitionSourceAdvisor</code> states in its documentation that auto-proxying is only enabled for <code>ApplicationContext</code>s. Thus, i belief that you need an app context for auto-proxying, if you don't use a separate <code>ProxyFactoryBean</code> for each service object. But since you're using the <code>@Secured</code> annotation, i suspect that this is the same as auto-proxying.</p> <p>The <code>fakeSecurityContext()</code> just set an <code>Authentication</code> object with the given role into the <code>SecurityContextHolder</code> for testing purposes.</p> <p>You can do it with Spring Core functionality on your own. Assume you've got a service which returns a List of Customers and the current user may only view Customers with up to a specific limit of revenue. The following test case will be our start:</p> <pre><code>@Test public void testSecurity() throws Exception { ClassPathXmlApplicationContext appCtx = new ClassPathXmlApplicationContext( "spring.xml"); IMyService service = (IMyService) appCtx.getBean("secured", IMyService.class); assertEquals(1, service.getCustomers().size()); } </code></pre> <p>This is the original service implementation:</p> <pre><code>public class MyService implements IMyService { public List&lt;Customer&gt; getCustomers() { return Arrays.asList(new Customer(100000), new Customer(5000)); } } </code></pre> <p>Configure your service object in <code>spring.xml</code> and add the method interceptor:</p> <pre><code>&lt;bean id="service" class="de.mhaller.spring.MyService"&gt;&lt;/bean&gt; &lt;bean id="securityInterceptor" class="de.mhaller.spring.MyServiceInterceptor"&gt; &lt;property name="revenueLimit" value="50000"/&gt; &lt;/bean&gt; &lt;bean id="secured" class="org.springframework.aop.framework.ProxyFactoryBean"&gt; &lt;property name="targetName" value="service" /&gt; &lt;property name="interceptorNames"&gt; &lt;list&gt; &lt;value&gt;securityInterceptor&lt;/value&gt; &lt;/list&gt; &lt;/property&gt; &lt;/bean&gt; </code></pre> <p>The security interceptor implementation:</p> <pre><code>public class MyServiceInterceptor implements MethodInterceptor { private int revenueLimit = 10000; public void setRevenueLimit(int revenueLimit) { this.revenueLimit = revenueLimit; } @SuppressWarnings("unchecked") public Object invoke(MethodInvocation mi) throws Throwable { List&lt;Customer&gt; filtered = new ArrayList&lt;Customer&gt;(); List&lt;Customer&gt; result = (List&lt;Customer&gt;) mi.proceed(); for (Customer customer : result) { if (customer.isRevenueBelow(revenueLimit)) { filtered.add(customer); } } return filtered; } } </code></pre> <p>The advantage of using such an approach is that you not only can check declaratively for roles of the current user, but also enforce corporate policies in a dynamic way, e.g. limit the returned Objects based on business values.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload