For example, using annotation to configure the roles required:
public interface MyService { @PreAuthorize("hasRole('USER') OR hasRole('ADMIN')") void hello(); @PreAuthorize("hasRole('ADMIN')") void bye(); }
In order to call the
bye()
method the user must have the ADMIN role, for hello()
method also the the USER role is enough.If a secured method is called by a user without the needed roles is raised an exception of type
org.springframework.security.access.AccessDeniedException
.Method security is active even during tests, so we have developed an annotation and a listener that can be used to setup easily user roles when testing (we use TestNG, probably something like this can be done also with Junit).
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface Authenticate { String username() default ""; String[] roles() default {}; }
public class AuthenticationListener implements IInvokedMethodListener { public static final String DEFAULT_USERNAME = "default"; private static final String FAKE_PASSWORD = "fakePassword"; /** * {@inheritDoc} */ @Override public void beforeInvocation(IInvokedMethod method, ITestResult testResult) { if (method.isTestMethod()) { ITestNGMethod testMethod = method.getTestMethod(); Method javaMethod = testMethod.getMethod(); Authenticate userAnnotation = javaMethod.getAnnotation(Authenticate.class); if (userAnnotation != null) { String username = userAnnotation.username(); if (username == null || username.isEmpty()) { username = DEFAULT_USERNAME; } String[] roles = userAnnotation.roles(); // role may be null/empty authenticateUser(username, roles); } } } private void authenticateUser(String username, String... roles) { Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>(); if (roles != null) { for (String role : roles) { grantedAuthorities.add(new GrantedAuthorityImpl(role)); } } UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, FAKE_PASSWORD, grantedAuthorities); SecurityContextHolder.getContext().setAuthentication(token); } /** * {@inheritDoc} */ @Override public void afterInvocation(IInvokedMethod method, ITestResult testResult) { if (method.isTestMethod()) { SecurityContextHolder.clearContext(); } } }
The
Authenticate
annotation allows to set the username and a list of roles that must be used when executing a test method, the AuthenticationListener
take care to configure the authentication token before test execution and to reset the security context after execution.This is an example of a test method that uses the
Authenticate
annotation:@ContextConfiguration("classpath:META-INF/spring/applicationContext*.xml") @Listeners(AuthenticationListener.class) public class MyServiceTest extends AbstractTestNGSpringContextTests { @Autowired private MyService myService; @Test @Authenticate(username = "pippo", roles = {"ADMIN" }) public void testCallHelloAsAdmin() { myService.hello(); } @Test @Authenticate(username = "pippo", roles = {"USER" }) public void testCallHelloAsUser() { myService.hello(); } @Test @Authenticate(username = "pippo", roles = {"ADMIN" }) public void testCallByeAsAdmin() { myService.bye(); } @Test(expectedExceptions = AccessDeniedException.class) @Authenticate(username = "pippo", roles = {"USER" }) public void testCallByeAsUser() { myService.bye(); } }
See also:
The demo project on GitHub
Spring Security
TestNG Listeners
Annotations
Hi Sinossi,
RispondiEliminaThank you very much for this wonderful post which has saved me. I have been struggling cor three days and seen your post and code in github which practically resolved my problem.
Thanks a lot once again.
Best Regards
Antony Raj Savarimuthu