java - 添加 global-method-security 命名空间时,Spring 看不到 @Transactional

标签 java spring hibernate spring-mvc spring-security

我创建了一个服务,负责通过 dao 与数据库联系。我用了@Transactional处理事务的注释。

@Service("aclService")
public class HibernateAclServiceImpl implements HibernateAclService{

private final Log logger = LogFactory.getLog(HibernateAclServiceImpl.class);
@Autowired
private AclObjectIdentityDao objectIdentityDao ;
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
@Autowired
private AclCache aclCache;
@Autowired
private PermissionGrantingStrategy grantingStrategy;
@Autowired
private AclAuthorizationStrategy aclAuthorizationStrategy;

private final Field fieldAces = FieldUtils.getField(AclImpl.class, "aces");

@Override
@Transactional
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
    AclObjectIdentity aclObjectIdentity = objectIdentityDao
            .get((Long) parentIdentity.getIdentifier());
    List<ObjectIdentity> list = new ArrayList<ObjectIdentity>(
            aclObjectIdentity.getChildren().size());
    for (AclObjectIdentity aoid : aclObjectIdentity.getChildren()) {
        final ObjectIdentity oid = new ObjectIdentityImpl(aoid.getObjectClass().getClazz());
        list.add(oid);
    }
    return list;
}

@Override
@Transactional
public Acl readAclById(ObjectIdentity object) throws NotFoundException {
    final Map<ObjectIdentity, Acl> objects = readAclsById(Arrays.asList(object), null);
    return objects.get(object);
}

@Override
@Transactional
public Acl readAclById(ObjectIdentity object, List<Sid> sids)
        throws NotFoundException {
    Map<ObjectIdentity, Acl> objects = readAclsById(Arrays.asList(object), sids);
    return objects.get(object);
}


@Override
@Transactional
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects)
        throws NotFoundException {
    return readAclsById(objects, null);
}

@Override
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects,
        List<Sid> sids) throws NotFoundException {
    Map<ObjectIdentity, Acl> result = new HashMap<ObjectIdentity, Acl>();
    Set<Long> objectsToLoad = new HashSet<Long>();

    for (int i = 0; i < objects.size(); i++) {
        final ObjectIdentity oid = objects.get(i);
        boolean aclFound = false;

        if (result.containsKey(oid)) {
            aclFound = true;
        }

        if (!aclFound) {
            Acl acl = aclCache.getFromCache(oid);

            if (acl != null) {
                if (acl.isSidLoaded(sids)) {
                    result.put(acl.getObjectIdentity(), acl);
                    aclFound = true;
                } else {
                    throw new IllegalStateException(
                            "Error: SID-filtered element detected when implementation does not perform SID filtering "
                                    + "- have you added something to the cache manually?");
                }
            }
        }
        if (!aclFound) {
            objectsToLoad.add((Long) oid.getIdentifier());
        }
    }

    if (objectsToLoad.size() > 0) {
        lookupAcl(result, objectsToLoad);
    }
    return result;
}
public void lookupAcl(Map<ObjectIdentity, Acl> map, Set<Long> objects){
    final List<AclObjectIdentity> aoids = objectIdentityDao.getList(objects);
    final Map<Long, Long> parents = new HashMap<Long, Long>();
    for(AclObjectIdentity aoid : aoids){
        if(aoid.isEntriesInheriting()){
            parents.put(aoid.getId(), aoid.getParent().getId());
        }
    }
    if(parents.size() > 0){
        lookupAcl(map, (Set<Long>)parents.values());
    }
    for(AclObjectIdentity aoid : aoids){
        if(map.containsKey(aoid.getId()))
            continue;
        final Acl parentAcl = map.get(parents.get(aoid.getId()));
        final Acl acl = new AclImpl(new ObjectIdentityImpl(aoid.getObjectClass().getClazz(), aoid.getId()), aoid.getId(), aclAuthorizationStrategy, grantingStrategy, parentAcl, null, aoid.isEntriesInheriting(), new PrincipalSid(aoid.getOwnerSid().getSid()));



        List<AccessControlEntryImpl> aces = new ArrayList<AccessControlEntryImpl>(aoid.getAclEntries().size());
        for(AclEntry aclEntry : aoid.getAclEntries()){
            final Permission permission = permissionFactory.buildFromMask(aclEntry.getMask());
            aces.add(new AccessControlEntryImpl(aclEntry.getId(), acl, new PrincipalSid(aclEntry.getSid().getSid()), permission, aclEntry.isGranting(), aclEntry.isAuditSuccess(), aclEntry.isAuditFailure()));
        }
        setAces((AclImpl) acl, aces);
        aclCache.putInCache((AclImpl) acl);
    }
}

private void setAces(AclImpl acl, List<AccessControlEntryImpl> aces) {
    try {
        fieldAces.set(acl, aces);
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Could not set AclImpl entries", e);
    }
}

这是我的“app-context.xml”文件的一部分

<security:global-method-security  pre-post-annotations="enabled">
    <security:expression-handler ref="expressionHandler" />
</security:global-method-security>
<bean id="expressionHandler"
    class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
    <property name="permissionEvaluator" ref="permissionEvaluator" />
    <property name="roleHierarchy" ref="roleHierarchy" />
</bean>

<bean class="org.springframework.security.acls.AclPermissionEvaluator"
    id="permissionEvaluator">
    <constructor-arg ref="aclService" />
</bean>

现在,当我调用服务的功能时,例如。从 Controller 它抛出一个错误 org.hibernate.HibernateException: No Session found for current thread .但是当我发表评论时,一切都很好(交易没有问题)

<security:global-method-security pre-post-annotations="enabled"> <security:expression-handler ref="expressionHandler" /> </security:global-method-security>

我检查了所有内容,并将有问题的代码缩小到上述部分。有谁知道为什么会这样?

最佳答案

我不确定 global-method-security 是如何在幕后实现的,但是 BeanPostProcessors 有一个鲜为人知的副作用 - 任何直接引用的 bean由 BeanPostProcessor 或由 BPP 引用的内容引用,不符合 AOP 自动代理的条件:

BeanPostProcessors and AOP auto-proxying

Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessors and beans that they reference directly are instantiated on startup, as part of the special startup phase of the ApplicationContext. Next, all BeanPostProcessors are registered in a sorted fashion and applied to all further beans in the container. Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessors nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.

For any such bean, you should see an informational log message: “Bean foo is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)”.

(source)

这意味着如果您在引用 BeanPostProcessor 加载的 bean 中有 @Transactional,则实际上会忽略该注释。

解决方案通常是,如果您需要在必须参照 BeanPostProcessor 加载的 bean 中执行事务行为,则您需要使用非 AOP 事务定义 - 即使用 TransactionTemplate。

您可以将 org.springframework 记录器的日志记录打开到 DEBUG 并验证是否正在为您的 aclService bean 输出此消息。

关于java - 添加 global-method-security 命名空间时,Spring 看不到 @Transactional,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12608212/

相关文章:

java - 如何将 Spring Security .antMatchers() 与多个路径一起使用

java - 错误 org.springframework.web.servlet.DispatcherServlet - 上下文初始化失败 org.springframework.beans.factory.BeanCreationException : Error

java - 将 session 工厂从一项服务传递到另一项服务

hibernate - 如何完全禁用Hibernate缓存? (在Spring 3中,带有注释的Hibernate)

java - Spring beans 和 Hibernate 的 Maven 依赖项

java - 在声明之前分配一个静态变量

java - 使用 Google Guice 删除 web.xml 文件

java - 自定义操作栏停止我的应用程序

java - Activemq 消息速率

java - 通过 Hibernate Search 将 Hibernate Criteria API 与 Lucene 结合起来?