添加 global-method-security 命名空间时 Spring 看不到 @Transactional
Posted
技术标签:
【中文标题】添加 global-method-security 命名空间时 Spring 看不到 @Transactional【英文标题】:Spring doesn't see @Transactional when global-method-security namespace is added 【发布时间】:2012-09-18 11:04:49 【问题描述】:我创建了一个服务,负责通过 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>
现在当我调用服务的功能时,例如。从控制器它会引发错误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>
我检查了所有内容,并将有问题的代码缩小到上述代码。有人知道为什么会这样吗?
【问题讨论】:
您的<tx:annotation-driven transaction-manager="txManager"/>
配置是否正确?查看documentation 了解详细的事务管理器配置。
这取决于你的意思是正确的。我已将其配置为默认值 <tx:annotation-driven/> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory" />
【参考方案1】:
我不确定global-method-security
是如何在幕后实现的,但BeanPostProcessors
有一个鲜为人知的副作用——任何由BeanPostProcessor
直接引用的bean,或由BPP 引用的东西引用, 不符合 AOP 自动代理的条件:
BeanPostProcessors 和 AOP 自动代理
实现 BeanPostProcessor 接口的类是特殊的并且被容器区别对待。它们直接引用的所有 BeanPostProcessor 和 bean 都在启动时实例化,作为 ApplicationContext 的特殊启动阶段的一部分。接下来,所有 BeanPostProcessor 都以排序方式注册并应用于容器中的所有其他 bean。因为 AOP 自动代理是作为 BeanPostProcessor 本身实现的,所以 BeanPostProcessor 和它们直接引用的 bean 都没有资格进行自动代理,因此没有将切面编织到其中。
对于任何此类 bean,您应该会看到一条信息性日志消息:“Bean foo 不符合所有 BeanPostProcessor 接口的处理条件(例如:不符合自动代理条件)”。
(source)
这意味着如果您在引用 BeanPostProcessor 加载的 bean 中有 @Transactional
,则该注释将被有效地忽略。
解决方案通常是,如果您需要在必须引用 BeanPostProcessor 加载的 bean 中的事务行为,则需要使用非 AOP 事务定义 - 即使用 TransactionTemplate。
您可以将 org.springframework
记录器上的日志记录设置为 DEBUG
并验证是否正在为您的 aclService
bean 输出此消息。
【讨论】:
以上是关于添加 global-method-security 命名空间时 Spring 看不到 @Transactional的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security应用开发(19)基于方法的授权AOP
TZ_10_spring-sucrity 服务器和页面的权限控制
Java SSM 项目实战 day08 方法级别的权限操作 服务器端的权限控制(JSR-250注解)(支持表达式的注解)(@Secured)以及页面端的权限控制