Problem:
I have implemented the following application event listener and it can catch authentication (both cases success and failure) and authorization ( fail). However, while authorization is successful, the even does not be triggered. I traced the code and figured out publishAuthorizationSuccess in AbstractSecurityInterceptor class is always false so it doesn’t publish AuthorizedEvent.
Environment:
Run it on JUnit
The execution sequence of my program:
Run MySampleApp -> SomeService -> ResourcePatternBaseVoter -> AbstractSecurityInterceptor -> SecurityAuditor (not triggered when authorized successfully)
My code and config are shown as follows:
MySampleApp.class
public class MySampleApp{
@Test
public void test2() {
Authentication authentication = providerManager
.authenticate(new UsernamePasswordAuthenticationToken("admin", "admin"));
SecurityContextHolder.getContext().setAuthentication(authentication);
logger.debug(someService1.someMethod6());
}
SomeService.java
@Service
public class SomeService1 {
@Secured("rpb:reports:a.b.c:create")
public String someMethod6() {
return String.valueOf(Math.random());
}
ResourcePatternBaseVoter.java
@Component
public class ResourcePatternBaseVoter implements org.springframework.security.access.AccessDecisionVoter<Object> {
private static final Log logger = LogFactory.getLog(ResourcePatternBaseVoter.class);
@Autowired
private ResourcePatternBaseAuthorizer resourcePatternBaseAuthorizer;
@Override
public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith("rpb:")) {
logger.debug("support attribute: " + attribute.getAttribute());
return true;
} else {
logger.debug("not support attribute: " + attribute.getAttribute());
return false;
}
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
@Override
public int vote(Authentication authentication, Object secureObject, Collection<ConfigAttribute> attributes) {
/* doSomething */
return ACCESS_GRANTED;
}
}
SecurityAuditor.java
@Component
public class SecurityAuditor implements ApplicationListener<AuthorizedEvent> {
@Override
public void onApplicationEvent(AuthorizedEvent event) {
logger.info("Here");
}
myAcl.xml
<bean id="methodAccessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
<bean class="com.ibm.gbsc.ty.acl.rpb.ResourcePatternBaseVoter" />
</list>
</constructor-arg>
</bean>
AbstractSecurityInterceptor.class
if (publishAuthorizationSuccess) {
publishEvent(new AuthorizedEvent(object, attributes, authenticated));
}
This article got me started, but that bean does not exist in Spring Security 4.1.3 anymore. However, I found it hidden inside FilterChainProxy.
Not sure how ugly this hack is, but works:
@Configuration
@EnableWebSecurity
@EnableJpaAuditing
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ApplicationContext applicationContext;
@EventListener
public void handle(ContextRefreshedEvent event) {
FilterChainProxy proxy = applicationContext.getBean(FilterChainProxy.class);
for (Filter f : proxy.getFilters("/")) {
if (f instanceof FilterSecurityInterceptor) {
((FilterSecurityInterceptor)f).setPublishAuthorizationSuccess(true);
}
}
}
...
}
Then my listener finally receives the AuthorizedEvent:
@Component
public class AppEventListener implements ApplicationListener {
private static final Logger logger = LoggerFactory.getLogger(AppEventListener.class);
@Override
@EventListener(value = {AuthorizedEvent.class})
public void onApplicationEvent(ApplicationEvent event)
{
if (event instanceof InteractiveAuthenticationSuccessEvent) {
Authentication auth = ((InteractiveAuthenticationSuccessEvent)event).getAuthentication();
logger.info("Login success: " + auth.getName() + ", details: " + event.toString());
} else if (event instanceof AbstractAuthenticationFailureEvent) {
logger.error("Login failed: " + event.toString());
} else if (event instanceof AuthorizedEvent) {
Authentication auth = ((AuthorizedEvent)event).getAuthentication();
logger.debug("Authorized: " + auth.getName() + ", details: " + event.toString());
} else if (event instanceof AuthorizationFailureEvent) {
Authentication auth = ((AuthorizationFailureEvent)event).getAuthentication();
logger.error("Authorization failed: " + auth.getName() + ", details: " + event.toString());
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With