在AuditorAware实现中覆盖由@CreatedBy注释注入的用户

时间:2017-09-11 07:32:56

标签: spring spring-mvc spring-data-jpa

以下是我对AuditAware的实施。

@Component
public class AuditorAwareImpl implements AuditorAware<String> {

    @Log
    private Logger logger;

    @Override
    public String getCurrentAuditor() {
        String user = "SYSTEM";
        try {
            final Subject subject = SecurityUtils.getSubject();
            if (subject != null) {
                final Session session = subject.getSession(false);
                if (session != null) {
                    final User userObj = (User) session.getAttribute("user");
                    if (userObj != null) {
                        user = userObj.getUsername();
                    }
                }
            }
        } catch (final Exception e) {
            this.logger.error("getCurrentAuditor", "No logged in user information found.", e);
        }

        return user;
    }
}

我们知道,由Spring注入的AuditorAwareImpl返回的用户值在@CreatedBy注释标记的属性中如下所示。

@CreatedBy
@Column(name = "CREATED_BY", length = 150, nullable = false, updatable = false)
private String createdBy;

我想解决两个问题。

  1. 我正在触发一个保存的单独线程 数据库中有数千个对象。如果会话超时,则会话对象变为空,AuditAwareImpl会抛出错误,从而导致后台进程错误。
  2. 我还想运行一些石英计划相关的工作,其中会话对象本身不可用。但我还是想在这种情况下注入一些硬编码用户。但是我该怎么做呢?
  3. 我在单个对象级别尝试了一件事,使用以下代码,

    @Transient
    private String overRiddenUser;
    
    @PrePersist
    public void updateCreatedBy(){
        if(overRiddenUser!=null){ 
            this.createdBy=overRiddenUser;
        }
    }
    

    每当我想在createBy中覆盖spring注入的用户并使用PrePersist方法覆盖它时,我就显式设置了overRiddenUser="Admin"。但是即使这样做也没有用,因为在我覆盖这个值之前,spring尝试使用AuditAware返回的值来填充。如果会话已经过期,则过程出错!我该如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

我找到了上述问题的解决方案如下。

第1步

我在下面定义了一个包含HashMap的bean。此映射将当前线程名称存储为键,并将映射的用户名存储为值。

public class OverRiddenUser {
Map<String,String> userThreadMap= new HashMap<>(); 
//add getters/setters
}

第2步

@Configuration注释的类中启动时初始化此bean。

@Bean("overRiddenUser")
public OverRiddenUser overRideUser(){
    OverRiddenUser obj = new OverRiddenUser();
    return obj;
    // Singleton implementation can be done her if required 
}

第3步

在我要覆盖会话用户的impl类中自动连接此bean。

@Autowired
OverRiddenUser overRiddenUser;

为当前正在运行的线程分配了一个随机名称。

RandomString randomThreadName = new RandomString(9);
Thread.currentThread().setName(randomThreadName.toString());

然后在hashmap中为当前线程添加所需的用户名,如下所示。

try {
    if(overRiddenUser!=null){
    overRiddenUser.getUserThreadMap().put(Thread.currentThread().getName(),"My Over ridden username");
    }

// Entire Database related code here

} catch (Exception e){

 // handle the exceptions thrown by code

} finally {
            //Perform clean up here
            if(overRiddenUser!=null){
                overRiddenUser.getUserThreadMap().remove(Thread.currentThread().getName());
        }
    }
}

第4步

然后我修改了AuditAwareImpl,如下所示。

@Component
public class AuditorAwareImpl implements AuditorAware<String> {

    @Log
    private Logger logger;

    @Autowired
    OverRiddenUser overRiddenUser;

    @Override
    public String getCurrentAuditor() {

        String user = "SYSTEM";
        try {

            if(overRiddenUser!=null && overRiddenUser.getUserThreadMap()!=null && overRiddenUser.getUserThreadMap().get(Thread.currentThread().getName())!=null){
                user=overRiddenUser.getUserThreadMap().get(Thread.currentThread().getName());
            }else{
                final Subject subject = SecurityUtils.getSubject();
                if (subject != null) {
                    final Session session = subject.getSession(false);
                    if (session != null) {
                        final User userObj = (User) session.getAttribute("user");
                        if (userObj != null) {
                            user = userObj.getUsername();
                        }
                    }
                }
            }
        } catch (final Exception e) {
            this.logger.error("getCurrentAuditor", "No logged in user information found.", e);
        }

        return user;
    }
}

因此,如果当前正在运行的线程访问AuditAwareImpl,那么它将获得为该线程设置的自定义用户名。如果这是null,则它会像之前那样从会话中提取用户名。希望有所帮助。