Spring 安全性:针对特定服务绕过 @Secured
Spring Security : bypass @Secured for specific service
我目前正在我的应用程序中实现 Spring 安全性。我确实设法在我的服务上添加了 @Secured 注释,即从数据库中获取 getAllUsers() ,只要识别了用户,它就可以正常工作(取决于他的权限,他可以获取或不获取用户列表)。
但我有一个 @Scheduled 方法负责为所有用户编制索引,当它启动时它调用相同的受保护的 getAllUsers() 方法,并且由于未登录而明显崩溃:我得到以下异常:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
我目前正在考虑一种可能的解决方案,即使用自定义注释标记内部方法,该注释将由自定义 AccessDecisionVoter
检索,允许调用者调用受保护的方法。
我正在寻找此类用例的最佳实践
因为方法是@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
}
}
我假设您的服务 class 看起来像:
public class MyServiceImpl implements MyService {
...
@Secured
public Xxx getAllUsers() {
...
// call DAO
...
return xxx;
}
...
}
然后您从 @Scheduled
class 调用 myService.getAllUsers()
。
最简单的方法是拆分 getAllUsers
并使服务 class 继承自 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();
}
然后在你的控制器中 class :
@Autowired MyService myService => will call only getAllUsers()
在你的 @Scheduled
class 中:
@Autowired MyScheduledService myService => will call restrictedGetAllUsers()
所有这一切似乎过于复杂,但由于您的计划 class 和您的控制器没有理由以相同的方式调用服务方法,因此向它们提供具有不同安全要求的两个不同接口是有意义的。
我选择了 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);
}
参考 PhilippeAuriach 的回答 - 有一个更好的方法 运行 具有授权的新线程 - 使用 spring 安全扩展 运行nable 方法,主线程的上下文被复制到委托中运行启用
public void authorizedExecute(Runnable runnable) {
new Thread(new DelegatingSecurityContextRunnable(runnable)).start();
}
我目前正在我的应用程序中实现 Spring 安全性。我确实设法在我的服务上添加了 @Secured 注释,即从数据库中获取 getAllUsers() ,只要识别了用户,它就可以正常工作(取决于他的权限,他可以获取或不获取用户列表)。
但我有一个 @Scheduled 方法负责为所有用户编制索引,当它启动时它调用相同的受保护的 getAllUsers() 方法,并且由于未登录而明显崩溃:我得到以下异常:
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
我目前正在考虑一种可能的解决方案,即使用自定义注释标记内部方法,该注释将由自定义 AccessDecisionVoter
检索,允许调用者调用受保护的方法。
我正在寻找此类用例的最佳实践
因为方法是@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
}
}
我假设您的服务 class 看起来像:
public class MyServiceImpl implements MyService {
...
@Secured
public Xxx getAllUsers() {
...
// call DAO
...
return xxx;
}
...
}
然后您从 @Scheduled
class 调用 myService.getAllUsers()
。
最简单的方法是拆分 getAllUsers
并使服务 class 继承自 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();
}
然后在你的控制器中 class :
@Autowired MyService myService => will call only getAllUsers()
在你的 @Scheduled
class 中:
@Autowired MyScheduledService myService => will call restrictedGetAllUsers()
所有这一切似乎过于复杂,但由于您的计划 class 和您的控制器没有理由以相同的方式调用服务方法,因此向它们提供具有不同安全要求的两个不同接口是有意义的。
我选择了 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);
}
参考 PhilippeAuriach 的回答 - 有一个更好的方法 运行 具有授权的新线程 - 使用 spring 安全扩展 运行nable 方法,主线程的上下文被复制到委托中运行启用
public void authorizedExecute(Runnable runnable) {
new Thread(new DelegatingSecurityContextRunnable(runnable)).start();
}