我目前正在我的应用程序中实现Spring Security。我确实设法将@Secured注释放在我的服务上,来自数据库的getAllUsers(),并且只要用户被识别就可以正常工作(根据他的权利,他可以获得或不获得用户列表)。
但我有一个@Scheduled方法负责索引所有用户,当它启动时,它调用相同的受保护的getAllUsers()方法,并且显然崩溃,因为它没有登录:我得到以下异常:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
我目前正在考虑一种可能的解决方案,即使用自定义注释标记内部方法,该注释将由自定义AccessDecisionVoter
检索,允许调用者调用受保护的方法。 / p>
我正在为这种用例寻找最佳实践
答案 0 :(得分:2)
因为方法是@Secured并且spring期望安全认证对象在上下文中。以下是AccessDecisionVoter Spring-security - AccessDecisionVoter-impl wont be invoked
的工作示例或者如果你有过滤器或smth将取决于用户上下文值这个应该没问题
@Scheduled
public void method() {
try {
ScheduledAuthenticationUtil.configureAuthentication();
// do work
}
catch(Exception e) {
e.printStackTrace();
}
finally {
ScheduledAuthenticationUtil.cleanAuthentication();
}
}
private static class ScheduledAuthenticationUtil {
public static void configureAuthentication() {
// inject auth obj into SecurityContextHolder
}
public static void cleanAuthentication() {
// SecurityContextHolder clean authentication
}
}
答案 1 :(得分:2)
我假设您的服务类看起来像:
public class MyServiceImpl implements MyService {
...
@Secured
public Xxx getAllUsers() {
...
// call DAO
...
return xxx;
}
...
}
您从myService.getAllUsers()
班级致电@Scheduled
。
最简单的方法是拆分getAllUsers
并使服务类继承自2个接口,一个包含安全方法,另一个包含可公开访问的版本:
public class MyServiceImpl implements MyService, MyScheduledService {
...
@Secured
public Xxx getAllUsers() {
return restrictedGetAllUsers;
}
public Xxx restrictedGetAllUsers() {
...
// call DAO
...
return xxx;
}
...
}
public interface MyService {
Xxx getAllUsers();
}
public interface MyScheduledService extends MyService {
Xxx restrictedGetAllUsers();
}
然后在你的控制器类中:
@Autowired MyService myService => will call only getAllUsers()
并在您的@Scheduled
班级中:
@Autowired MyScheduledService myService => will call restrictedGetAllUsers()
所有这些看起来都过于复杂,但由于您的预定类和您的控制器没有理由以相同的方式调用服务方法,因此为它们提供两个具有不同安全要求的不同接口是有意义的。
答案 2 :(得分:1)
我使用kxyz回答,通过在运行代码之前设置所需的权限来运行一段代码的服务进行了改进,并在代码完成时放回了以前的权限:
public void runAs(Runnable runnable, GrantedAuthority... authorities) {
Authentication previousAuthentication = SecurityContextHolder.getContext().getAuthentication();
configureAuthentication(authorities);
try {
runnable.run();
} finally {
configureAuthentication(previousAuthentication);
}
}
protected void configureAuthentication(GrantedAuthority... authorities) {
Authentication authentication = new UsernamePasswordAuthenticationToken("system", null, Arrays.asList(authorities));
configureAuthentication(authentication);
}
protected void configureAuthentication(Authentication authentication) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}
答案 3 :(得分:0)
参考PhilippeAuriach的回答 - 有一个更好的方法来运行新的线程与authorites - 使用spring security extended runnable方法,主线程的上下文被复制到委托的runnable
public void authorizedExecute(Runnable runnable) {
new Thread(new DelegatingSecurityContextRunnable(runnable)).start();
}