I have a spring boot application for which I secure methods using spring security annotations as shown below.
public interface UserService {
@PreAuthorize("hasRole('ADMIN')")
List<User> findAllUsers();
@PostAuthorize ("returnObject.type == authentication.name")
User findById(int id);
@PreAuthorize("hasRole('ADMIN')")
void updateUser(User user);
@PreAuthorize("hasRole('ADMIN') AND hasRole('DBA')")
void deleteUser(int id);
}
The problem that I have here is that there are implementations which are called via @RabbitListener
methods which does not have any security context because it's not a web request.
I can expect the clients to pass the usernames via message headers sp I could use message.getMessageProperties().getHeaders().get("username")
to get the username for that request.
But still I believe there won't be a straightforward approach as the method level annotations won't be evaluated because they are not under security context.
Is there any approach via which I can establish a security context for a spring amqp messages?
Just add the context...
@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class So49957413Application extends GlobalAuthenticationConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(So49957413Application.class, args);
}
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("foo").password("bar").roles("baz");
}
@Autowired
private Foo foo;
@RabbitListener(queues = "foo")
public void listen(Message in) {
try {
SecurityContext ctx = SecurityContextHolder.createEmptyContext();
ctx.setAuthentication(
new UsernamePasswordAuthenticationToken(in.getMessageProperties().getHeaders().get("user"), "bar"));
SecurityContextHolder.setContext(ctx);
this.foo.method1();
try {
this.foo.method2();
}
catch (AccessDeniedException e) {
System.out.println("Denied access to method2");
}
}
finally {
SecurityContextHolder.clearContext();
}
}
@Bean
public Foo foo() {
return new Foo();
}
public static class Foo {
@PreAuthorize("hasRole('baz')")
public void method1() {
System.out.println("in method1");
}
@PreAuthorize("hasRole('qux')")
public void method2() {
System.out.println("in method2");
}
}
}
and
in method1
Denied access to method2
It would be better to add/remove the security context in an advice, in the listener container's advice chain, to avoid mixing the security code with your listener logic.