简体   繁体   English

Spring安全性:如何在异步Jobs中访问经过身份验证的用户

[英]Spring security: How to access authenticated user in asynchronous Jobs

As the jobs operate asynchronously they can't access to the user's session. 由于作业异步运行,因此它们无法访问用户的会话。 A solution needs to be found so that that the jobs can access the user's session (if the user is still logged in at that moment). 需要找到一个解决方案,以便作业可以访问用户的会话(如果此时用户仍处于登录状态)。

User session 用户会话

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

public class UserPrincipal implements UserDetails {

private User user;

private Collection<SimpleGrantedAuthority> grantedAuthorities;

public UserPrincipal(User user) {
    Assert.notNull(user);
    this.user = user;

    Set<SimpleGrantedAuthority> authorities = new LinkedHashSet<>();
    for (Role role : user.getRoles()) {
        authorities.add(new SimpleGrantedAuthority(role.getName().toUpperCase(Locale.ENGLISH)));
    }
    grantedAuthorities = Collections.unmodifiableCollection(authorities);
}

} }

Abstract Job class 抽象工作类别

public abstract class Job implements Runnable {

protected Logger logger = LoggerFactory.getLogger(getClass());

protected Job() {
}

@Override
public final void run() {
    logger.debug("starting work");
    /* code goes here */
    logger.debug("work is done");
}
}

Job class 工作类别

@Component
@Scope(value = "prototype")
public class ProcessLoggingJob extends Job {

@Override
protected void work(Map<String, Object> context) throws Exception {
    // need to access user session here
}

Async jobs are executed by another thread (as expected). 异步作业由另一个线程执行(如预期的那样)。 The session is managed by the application server and offered by the request. 会话由应用服务器管理,并由请求提供。 Spring security additional manages a context in a thread local to access it without the request (as @Michael shows in his Answer). Spring Security额外管理本地线程中的上下文,以在没有请求的情况下访问它(如@Michael在他的答案中所示)。

As the security context (got from the session) is held in one thread local (typical the HTTP-thread of the application server), the async job runs in another thread with no chance to access the thread local of the request thread. 由于安全上下文(从会话中获取)保存在本地一个线程(通常是应用程序服务器的HTTP线程)中,因此异步作业在另一个线程中运行,而没有机会访问请求线程的本地线程。

The only chance I see is to use a queue mechanism , creating new job data from the request thread including the user data, passing them to a queue, processing data from the queue in the job. 我看到的唯一机会是使用队列机制 ,从请求线程创建包括用户数据的新作业数据,将其传递到队列,处理作业中队列中的数据。

In the request handling thread this could look like (missing null handling): 在请求处理线程中,它可能看起来像(缺少空处理):

private BlockingQueue<UserDetails> usersToProceedAsync;

public void doSomethingInRequestThread() throws InterruptedException {
  UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()
  ...
  usersToProceedAsync.put(principal);
}

The job implementation may be: 作业实现可以是:

private BlockingQueue<UserDetails> usersToProceedAsync;

protected void work(Map<String, Object> context) throws Exception {
  UserDetails principal = usersToProceedAsync.poll();
  if (principal != null) {
    ...
  }
}

You just have to connect the two classes by injecting the same queue to both (a LinkedBlockingQueue eg). 您只需要通过将相同的队列注入两者来连接这两个类(例如, LinkedBlockingQueue )。 If the job instance is created per job run, you will need a factory to inject the queue. 如果按作业运行创建作业实例,则需要工厂来注入队列。

Be careful when creating one job data per request! 每个请求创建一个作业数据时要小心! How to ensure the async job is fast enough to process all the work? 如何确保异步作业足够快地处理所有工作? You have many options to tune the behavior using other methods to add to or remove data from the BlockingQueue and/or by using another implementation like the bounded ArrayBlockingQueue . 您可以使用许多方法来调整行为,这些方法可以使用其他方法添加到BlockingQueue或从BlockingQueue删除数据,和/或通过使用其他实现(例如bounded ArrayBlockingQueue

An other idea may be to use the executor framework instead of scheduled job execution. 另一种想法可能是使用执行程序框架而不是计划的作业执行。 Just create a executor in your request handler and let it run the jobs for you: 只需在您的请求处理程序中创建一个执行程序,然后让它为您运行作业即可:

private final Executor asyncJobs = Executors.newCachedThreadPool()

public void doSomethingInRequestThread() throws InterruptedException {
  UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()
  ...
  asyncJobs.execute(new AsyncJob(principal));
}

private class AsyncJob implements Runnable {
  private final UserDetails principal;

  public AsyncJob(UserDetails principal) {
    this.principal = principal;
  }

  public void run() {
    ...
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 Spring Security 中禁用经过身份验证的访问? - How to disable authenticated access in Spring Security? Spring Security 3检查用户是否通过身份验证 - Spring Security 3 check if user is authenticated 如何在 Spring Security / SpringMVC 中手动设置经过身份验证的用户 - How to manually set an authenticated user in Spring Security / SpringMVC 如何使用Spring安全性从经过身份验证的用户将数据库地址保存到数据库? - How to save ip address to a DB from authenticated user with Spring security? Spring Security:如何向经过身份验证的用户添加额外的角色 - Spring Security: How To add extra role to authenticated user Spring Security预认证用户登录 - spring security pre authenticated user login Spring Security - 用户在会话销毁时保持身份验证 - Spring Security - User keeps authenticated on session destroy Spring 安全性:在方法中获取经过身份验证的用户 - Spring security: get Authenticated user in method 对于经过身份验证且未经过身份验证的用户,Spring Security会在休息服务中获取用户信息 - Spring Security get user info in rest service, for authenticated and not authenticated users Spring安全匿名用户和具有弹簧安全性的经过身份验证的用户 - spring secuirty anonymous user and authenticated user with spring security
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM