Reputation: 628
First off I have googled this extensively and while it appears that there is supposedly a fix in place I cannot successfully reference an injected @Bean
inside of a PermissionEvaluator
:
In that issue's comments section Rob Winch provides a work around suggestion
to work around this issue, you can proxy your permissionEvaluator using LazyInitTargetSource
That being said, I am having trouble implementing the annotation-based JavaConfig version of the posted XML. I am using Spring Boot 1.0.0.BUILD-SNAPSHOT and spring-boot-starter-security.
I have a class to configure method security as follows:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new MyPermissionEvaluator());
expressionHandler.setParameterNameDiscoverer(new SimpleParameterDiscoverer());
return expressionHandler;
}
}
And the start of a PermissionEvaluator
:
public class MyPermissionEvaluator implements PermissionEvaluator {
private static final Logger LOG = LoggerFactory.getLogger(MyPermissionEvaluator.class);
@Autowired
private UserRepository userRepo;
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
if (authentication == null || !authentication.isAuthenticated()) {
return false;
}
if (permission instanceof String) {
switch((String) permission) {
case "findUser":
return handleUserPermission(authentication, targetDomainObject);
default:
LOG.error("No permission handler found for permission: " + permission);
}
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
throw new RuntimeException("Id-based permission evaluation not currently supported.");
}
private boolean handleUserPermission(Authentication auth, Object targetDomainObject) {
if (targetDomainObject instanceof Long) {
boolean hasPermission = userRepo.canFind((Long) targetDomainObject);
return hasPermission;
}
return false;
}
}
What needs to be done so that I can get a reference to my UserRepository
from inside the PremissionEvaluator
? I've attempted various workarounds w/ no success. It seems that nothing can be @Autowired
into the PermissionEvaluator
...
Upvotes: 7
Views: 7797
Reputation: 2682
As per @Dave Syer suggestion issue was using new keyword changing new MyPermissionEvaluator() to @Autowired will solve the issue as shown below.
changing below code
From:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new MyPermissionEvaluator());
expressionHandler.setParameterNameDiscoverer(new SimpleParameterDiscoverer());
return expressionHandler;
}
}
To:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Autowired
private MyPermissionEvaluator myPermissionEvaluator;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(myPermissionEvaluator);
expressionHandler.setParameterNameDiscoverer(new SimpleParameterDiscoverer());
return expressionHandler;
}
}
with the above change below code will start working as expected.
@Autowired
private UserRepository userRepo;
Upvotes: 2
Reputation: 1
Solutions from Dave Syer & SchonWieder works for me. As alternative I want to show, how I solved this problem before. I inject Permision Evaluator in MethodSecurityConfig like anonymous class.
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{
@Autowired DataSource dataSource;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new PermissionEvaluator(){
@Override
public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) {
JdbcTemplate template = new JdbcTemplate(dataSource);
...
if (count==1){
return true;
} else {
return false;
}
}
@Override
public boolean hasPermission(Authentication arg0, Serializable arg1, String arg2, Object arg3) {
// TODO Auto-generated method stub
return false;
}
});
return expressionHandler;
}
}
Upvotes: 0
Reputation: 213
I had the same issue and the answer from Dave Syer worked perfectly for me. To respond to the comment from jasonfungsing, to pull the PermissionEvaluator into a Bean I annotated my custom class with @Component and @Autowired the DAO into it:
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator{
private CustomRepository customRepository;
@Autowired
public void setCustomRepository(CustomRepository customRepository) {
this.customRepository = customRepository;
}
@Override
public boolean hasPermission(Authentication authentication, Object target, Object permission) {
if (target instanceof ...
Then in my GlobalMethodSecurityConfiguration overriding class I created a private instance variable of my PermissionEvaluator class, @Autowired the PermissionEvaluator into it and used this instance in my setPermissionEvaluator method call (thus avoiding a "new" call):
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{
private DataSource datasource;
private CustomPermissionEvaluator customPermissionEvaluator;
@Autowired
public void setCustomPermissionEvaluator(CustomPermissionEvaluator customPermissionEvaluator) {
this.customPermissionEvaluator = customPermissionEvaluator;
}
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(customPermissionEvaluator);
return expressionHandler;
}
I did not need to use the @LAZY or @SCOPE annotations.
Upvotes: 12
Reputation: 58124
Nothing can be autowired into an object that is created with new ...()
(unless you are using @Configurable
and AspectJ). So you almost certainly need to pull your PermissionEvaluator
out into a @Bean
. If you need to make it a lazy proxy as well (because of the ordering sensitivity of Spring Security initialization), then you should add @Lazy @Scope(proxyMode=INTERFACES)
(or TARGET_CLASS
if that suits you better).
Upvotes: 13