#spring #spring-boot #jpa
#spring #весенняя загрузка #jpa
Вопрос:
У меня ситуация, когда мне нужно создать соединение с БД на основе пользовательского ввода, и это созданное соединение должно использоваться для всех транзакций, пока этот конкретный пользователь не выйдет из системы. Мне нужно сделать это в Spring boot JPA. По умолчанию я беру сведения о подключении из файла yaml при запуске сервера.
Я не понимаю, как создать новый источник данных и использовать его для всего сеанса.
Я новичок в spring boot и JPA. Может кто-нибудь, пожалуйста, помочь мне с примером.
Комментарии:
1. Вы пытаетесь подключиться к той же базе данных, когда пользователь входит в систему? Какую базу данных вы используете, Oracle?
2. Вы хотите создать подключение к БД при входе пользователя в систему и использовать то же соединение, пока он не выйдет из системы. Это ваше требование? Вы можете попробовать это
3. @rjdkolb — я пытаюсь подключиться к той же базе данных, и в данном случае это Oracle DB.
4. Таким образом, вы можете использовать прокси-пользователя Oracle. Это обеспечит вам производительность реального пула, но у каждого пользователя будут свои особые права на базу данных. В журналах аудита также будет указан пользователь, к которому подключен прокси. andrejkoelewijn.com/blog/2005/09/12 /…
Ответ №1:
Это не быстрое решение, и для правильной реализации может потребоваться день или около того, но использование прокси-соединения Oracle обеспечивает скорость обычного пула JDBC, но у каждого пользователя будут свои особые права в базе данных. В журналах аудита также будет указан пользователь, к которому подключен прокси.
Пользователь Spring, вошедший в систему, используется для определения, от имени какого пользователя использовать прокси.
Вам понадобятся две зависимости, загруженные со страницы Oracle JDBC. Убедитесь, что версии совпадают. Установите их в свой репозиторий Maven.
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ucp</artifactId>
<version>12.2.0.1.0</version>
</dependency>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1.0</version>
<scope>provided</scope>
</dependency>
Предоставьте источник данных для Spring.
import oracle.ucp.UniversalConnectionPool;
import oracle.ucp.UniversalConnectionPoolAdapter;
import oracle.ucp.admin.UniversalConnectionPoolManager;
import oracle.ucp.admin.UniversalConnectionPoolManagerImpl;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
@Bean
@SneakyThrows
public DataSource dataSource() throws SQLException {
final PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
UniversalConnectionPoolManager mgr = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager();
pds.registerConnectionLabelingCallback(new MyProxyConnectionLabelingCallback("MY_NOPRIV"));//you can make this any name you want
DataSource result = new ProxyDelegatingDataSource(pds, getNoPrivilegesUser());
mgr.createConnectionPool((UniversalConnectionPoolAdapter) pds);
mgr.startConnectionPool(PROXY_UCP_NAME);
return resu<
}
Внедрите ConnectionLabelingCallback и поймите, что означает стоимость.
import java.sql.SQLException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import oracle.jdbc.OracleConnection;
import oracle.ucp.ConnectionLabelingCallback;
import oracle.ucp.jdbc.LabelableConnection;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
public class MyProxyConnectionLabelingCallback implements ConnectionLabelingCallback {
public static final String USER_PROXY = "USER_PROXY";
private final String noPrivilegesUser;
public ProxyConnectionLabelingCallback(String noPrivilegesUser) {
this.noPrivilegesUser = noPrivilegesUser;
}
@Override
public int cost(Properties reqLabels, Properties currentLabels) {
final Object request = reqLabels.get(USER_PROXY);
final Object current = currentLabels.get(USER_PROXY);
if (request.equals(current)) {
//Same user
return 0;
}
if (noPrivilegesUser.equals(current)) {
//No Priv User. No Little Cost
return 1;
}
//Different user request
return 1000;
}
@Override
public boolean configure(Properties reqLabels, Object conn) {
try {
LabelableConnection lConn = (LabelableConnection) conn;
String userName = getUserName(noPrivilegesUser);
final Properties connectionLabels = lConn.getConnectionLabels();
if (connectionLabels == null) {
setProxy((OracleConnection) conn, noPrivilegesUser, userName, null);
lConn.applyConnectionLabel(USER_PROXY, userName);
return true;
}
Object currentLabel = connectionLabels.get(USER_PROXY);
if (!userName.equals(currentLabel)) {
setProxy((OracleConnection) conn, currentLabel, userName, null);
lConn.applyConnectionLabel(USER_PROXY, userName);
} else {
//Label match, good
}
return true;
} catch (SQLException| RuntimeException sExp) {
return false;
}
}
public static String getUserName(String noPrivilegesUser) {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();// you are logged with this user using Spring Security
if ((authentication == null)) {
//Not logged in returning non privileged user
return noPrivilegesUser;
}
Object principal = authentication.getPrincipal();
if (principal.equals("anonymousUser")) {
//Not logged in returning non privileged user
return noPrivilegesUser;
}
if (principal instanceof UserDetails) {
final String username = ((UserDetails) (principal)).getUsername();
//proxy connection to user username
return username;
} else {
return principal.toString();
}
}
public void setProxy(OracleConnection oraCon, Object currentLabel, String proxyUserName, String proxyPassword) throws SQLException {
if (oraCon.isProxySession()) {
oraCon.close(OracleConnection.PROXY_SESSION);
}
Properties proxyProperties = new Properties();
proxyProperties.setProperty(OracleConnection.PROXY_USER_NAME, proxyUserName);
if (proxyPassword != null) {
proxyProperties.setProperty(OracleConnection.PROXY_USER_PASSWORD, proxyPassword);
}
//below is what can take a few ms
oraCon.openProxySession(OracleConnection.PROXYTYPE_USER_NAME, proxyProperties);
}
}
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import oracle.ucp.jdbc.PoolDataSource;
import org.springframework.jdbc.datasource.DelegatingDataSource;
class ProxyDelegatingDataSource extends DelegatingDataSource {
private final PoolDataSource pds;
private final String noPrivilegesUser;
public ProxyDelegatingDataSource(PoolDataSource pds, String noPrivilegesUser) {
super(pds);
this.pds = pds;
this.noPrivilegesUser = noPrivilegesUser;
}
@Override
public Connection getConnection() throws SQLException {
Properties prprts = new Properties();
prprts.put(ProxyConnectionLabelingCallback.USER_PROXY, getUserName());
return getConnection(prprts);
}
public Connection getConnection(Properties prprts) throws SQLException {
return pds.getConnection(prprts);
}
public Connection getConnection(String username, String password, Properties prprts) throws SQLException {
return pds.getConnection(username, password, prprts);
}
private String getUserName() {
return ProxyConnectionLabelingCallback.getUserName(noPrivilegesUser);
}
}
Настройка привилегий Oracle:
create user JDBCPROXY identified by pass123;
create user MY_NOPRIV identified by pass123;
GRANT CONNECT TO JDBCPROXY;
GRANT CONNECT TO MY_NOPRIV;
alter user MY_NOPRIV grant connect through JDBCPROXY;
alter user USER1 grant connect through JDBCPROXY;
Когда пул будет создан, все пользователи будут подключаться как JDBCPROXY, проксируемый как MY_NOPRIV.
Когда «ПОЛЬЗОВАТЕЛЬ1» войдет в систему, он переключится на JDBCPROXY, прокси-сервер которого указан как ПОЛЬЗОВАТЕЛЬ1.
Установите Spring security и войдите в систему как «ПОЛЬЗОВАТЕЛЬ1». Я не буду вдаваться в подробности.