我正在尝试学习一些Spring Security ACL控件并将其应用于我的Spring Boot项目。因此,我试图重用Spring Security reference中的代码片段,我对项目需求做了一些调整。
我具有以下组件,以便在启动时用一些初始值填充数据库。
<AppBarLayout ... />
<android.support.v4.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:fitsSystemWindows="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_asks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:scrollbars="none"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="@dimen/small_margin"
android:layout_above="@+id/divider"
android:background="@color/red"/>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_bids"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:scrollbars="none"
app:layout_constraintTop_toBottomOf="@+id/divider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
android:layout_below="@+id/divider"
android:layout_marginTop="@dimen/small_margin"
android:background="@color/cyan"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
每次刷新应用程序上下文时都会运行此方法。我可以看到,如果仅运行一种测试方法(或测试类),则该方法将成功运行。这些测试是端到端测试(@Component
public class AppBootstrap {
private Authority adminAuth;
private User admin;
private TimeSheet timeSheetAdmin;
private final JdbcMutableAclService jdbcMutableAclService;
private final PlatformTransactionManager transactionManager;
@Autowired
public AppBootstrap(JdbcMutableAclService jdbcMutableAclService, PlatformTransactionManager transactionManager) {
this.jdbcMutableAclService = jdbcMutableAclService;
this.transactionManager = transactionManager;
}
@Bean
public CommandLineRunner initialAuthorities(AuthorityRepository authorityRepository) {
return args -> {
adminAuth = new Authority(ROLE_ADMIN);
authorityRepository.save(adminAuth);
};
}
@Bean
public CommandLineRunner initialUsers(UserRepository userRepository) {
return args -> {
admin = new User("admin",
"{bcrypt}$2a$08$lDnHPz7eUkSi6ao14Twuau08mzhWrL4kyZGGU5xfiGALO/Vxd5DOi", "admin",
"admin", "admin@example.com", true, getDate(2016, JANUARY, 1));
admin.setAuthorities(asList(adminAuth));
userRepository.save(admin);
};
}
@Bean
public CommandLineRunner initialTimeSheets(TimeSheetRepository timeSheetRepository) {
return args -> {
timeSheetAdmin = new TimeSheet(LocalDate.of(2016, MARCH, 1), admin);
timeSheetRepository.save(timeSheetUser);
};
}
@Bean
public CommandLineRunner initialRights() {
return args -> grantPermission(admin, timeSheetAdmin, ADMINISTRATION);
}
MutableAcl grantPermission(User user, TimeSheet timeSheet, Permission p) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
SecurityContext securityContext = SecurityContextHolder.getContext();
var authorities = user.getAuthorities().stream().map(auth -> auth.getName().toString()).collect(toList());
Authentication authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPass(), AuthorityUtils.createAuthorityList(authorities.toArray(new String[]{})));
securityContext.setAuthentication(authentication);
ObjectIdentity oiTimeSheet = new ObjectIdentityImpl(TimeSheet.class, timeSheet.getId());
Sid sidAdmin = new PrincipalSid(user.getUsername());
MutableAcl acl;
try {
acl = (MutableAcl) jdbcMutableAclService.readAclById(oiTimeSheet);
} catch (NotFoundException nfe) {
acl = transactionTemplate.execute(status -> jdbcMutableAclService.createAcl(oiTimeSheet));
}
acl.insertAce(acl.getEntries().size(), p, sidAdmin, true);
// updating permission
MutableAcl finalAcl = acl;
return transactionTemplate.execute(status -> jdbcMutableAclService.updateAcl(finalAcl));
}
}
)。另外,单独运行我的@RunWith(SpringRunner.class) @SpringBootTest
也没问题。但是,如果我一次运行所有测试,几乎所有@SpringBootApplication
都会失败,并显示以下堆栈跟踪信息
@SpringBootTest
我有几个问题:
谢谢! :-)
答案 0 :(得分:1)
我遇到了同样的问题。
就我而言,ACL缓存脏了。每次测试后,我必须清除缓存,然后所有测试都以绿色运行,即使同时调用也是如此。
我为此创建了一个TestExcutionListener。只需用它注释您的抽象Test类:
@TestExecutionListeners(
value = ClearAclCacheTestExecutionListener.class,
mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public class AbstractTest{...}
public class ClearAclCacheTestExecutionListener extends AbstractTestExecutionListener {
@Autowired
private AclCache aclCache;
@Override
public void beforeTestClass(TestContext testContext) {
testContext.getApplicationContext()
.getAutowireCapableBeanFactory()
.autowireBean(this);
}
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
super.afterTestMethod(testContext);
aclCache.clearCache();
}
}
答案 1 :(得分:0)
由于这个问题并没有那么流行,所以我尝试了另一种似乎很有效的方法:-)
首先,我创建了一个新的ACLService类,以隔离与JdbcMutableAclService的所有交互。
package com.roberto.security.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.acls.model.*;
import org.springframework.stereotype.Service;
/**
* Service class to handle ACL permissions.
*/
@Service
public class ACLService {
private final MutableAclService mutableAclService;
@Autowired
public ACLService(MutableAclService mutableAclService) {
this.mutableAclService = mutableAclService;
}
/**
* Insert an ACL entry
* @param oid represents the model object
* @param recipient represents the principal (user, group of users, etc)
* @param permission quite explicit name...
* @return the new ACL database entry
*/
public MutableAcl addPermission(ObjectIdentity oid, Sid recipient, Permission permission) {
MutableAcl acl;
try {
acl = (MutableAcl) mutableAclService.readAclById(oid);
} catch (NotFoundException nfe) {
acl = mutableAclService.createAcl(oid);
}
acl.insertAce(acl.getEntries().size(), permission, recipient, true);
return mutableAclService.updateAcl(acl);
}
}
然后,我创建了另一个可以正常运行的集成测试,没有IllegalStateException。现在,我只需要从引导类中调用它即可。
package com.roberto.security.service;
import com.roberto.model.TimeSheet;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.*;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static org.junit.Assert.*;
import static org.springframework.test.annotation.DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD;
/**
* This test handles basic interaction between our codebase
* Spring Security ACL and the underlying database model
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class ACLServiceIntegrationTest {
private Authentication authentication;
private ObjectIdentity oid ;
private Sid sid;
private Permission administration = BasePermission.ADMINISTRATION;
@Autowired
private ACLService aclService;
@Autowired
private MutableAclService mutableAclService;
@Autowired
private PermissionEvaluator permissionEvaluator;
@Before
public void setUp() {
authentication = TestSecurityContextHolder.getContext().getAuthentication();
sid = new PrincipalSid(((JwtUser) authentication.getPrincipal()).getUsername());
oid = new ObjectIdentityImpl(TimeSheet.class, 1);
}
@Test
@WithUserDetails("admin")
public void testBeans() {
assertNotNull(aclService);
assertNotNull(mutableAclService);
assertNotNull(permissionEvaluator);
}
@Test
@Transactional
@WithUserDetails("admin")
@DirtiesContext(methodMode = AFTER_METHOD)
public void addPermissionIntegrationTest() {
assertFalse(permissionEvaluator.hasPermission(authentication, oid.getIdentifier(), oid.getType(), administration));
MutableAcl acl = aclService.addPermission(oi, sid, administration);
assertTrue(permissionEvaluator.hasPermission(authentication, oid.getIdentifier(), oid.getType(), administration));
assertEquals(TimeSheet.class.toString().split(" ")[1], acl.getObjectIdentity().getType());
assertTrue(acl.getEntries().stream().anyMatch(e -> e.getSid().equals(sid) && e.getPermission().equals(administration)));
assertTrue(acl.isGranted(List.of(administration), List.of(sid), true));
}
}