Reputation: 31
I'm working with a spring app, that has around 150 services, so I need to audit each http event request (post, put, and delete) of each service, the audit data among others should be the user that made the http request and It has to be stored in the database.
To achieve that, on spring side I create an http interceptor to get the user and store it in a Postgres Temp table created with java.
// The Interceptor
@Component
public class AuditHttpRequestHandlerInterceptor implements HandlerInterceptor {
@Inject private AuditBasicAuthExtractor auditBasicAuthExtractor;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try {
String user = auditBasicAuthExtractor.getUser();
auditBasicAuthExtractor.addTempTable(user);
} catch (Exception ex) {
LOGGER.error("", ex);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
// Temp Table Creation
private void createNewTempTable(DataSource dataSource, String tempTableName, List<String> columns) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = dataSource.getConnection();
String sqlStatement = "CREATE TEMP TABLE IF NOT EXISTS ".concat(tempTableName).concat(" ( ");//here I create the temp table
for (String column:
columns) {
sqlStatement = sqlStatement.concat(column);// one of the columns is the user
}
sqlStatement = sqlStatement.concat(")");
preparedStatement = connection.prepareStatement(sqlStatement);
preparedStatement.executeUpdate();
} catch (SQLException throwables) {
LOGGER.error("SQLException ", throwables);
} finally {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException throwables) {
LOGGER.error("SQLException", throwables);
}
}
}
On Postgres, I created an after insert, update, and delete trigger that consults the temp table to fetch the corresponding user, after that the trigger stores this user in audit tables (In a parent audit table I store the user and in a child audit table I store the modified column name and the old and new values).
// Piece of code of the trigger
CREATE OR REPLACE FUNCTION audit_changes() RETURNS TRIGGER AS $$
DECLARE
BEGIN
IF EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'temp_user_table') THEN
SELECT user_id INTO user_name FROM cloud_context; --here fetches the user
END IF;
END;
$$ LANGUAGE plpgsql;
That works properly with 2 or 3 concurrent different users (the trigger fetches each user properly).
But suppose we have 50 different concurrent users making requests at the same time. In that case, the temp table stores 50 different rows (one for each user) so in that case, the trigger can't identify which user makes its corresponding request.
I thought of including a unique identifier in the interceptor, with the purpose the trigger can select the user by that identifier, but I don't know how to share that Id between the Interceptor and the trigger.
I'll appreciate any help or guide to solve that, any alternative solution would be good too.
Upvotes: 1
Views: 231