Исправлена ​​ошибка выхода из системы "База данных не выбрана" при попытке реализовать мультиарендные службы в загрузке Hibernate Spring с использованием аутентификации на основе сеанса

Я интегрирую многопользовательскую логику в свой проект, используя загрузку Hibernate Spring, я могу переключаться между БД, используя метод DNS, например: db_name.example.com -> Имя базы данных = db_name. Я использую сеансовую аутентификацию в проект.

Проблема в том, что когда я хочу выйти из сеанса, проект возвращает следующую ошибку:

[удалить из persistent_logins, где username =?]; Состояние SQL [3D000]; код ошибки [1046]; База данных не выбрана; Вложенное исключение - java.sql.SQLException: база данных не выбрана

Я обнаружил, что этот пост похож на мой случай: как я могу заставить Global Logout работать в мультитенантном SAML-приложении с использованием spring-security-saml? Я попробовал это, но безрезультатно.

Я использую следующий мультитенантный пример кода, взятый со следующего веб-сайта https://javadeveloperzone.com/hibernate/spring-hibernate-xml-multi-tenancy-example/ (комментатор - это я)

Мой SecurityConfigWeb:

public class SecurityConfigWeb extends WebSecurityConfigurerAdapter implements Serializable {

@Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .antMatchers(Constants.URL_PATH2, Constants.URL_PATH3, Constants.URL_PATH4, Constants.URL_PATH5, Constants.URL_PATH6, Constants.URL_PATH7, Constants.CONFIRMATION_EMAIL,
                        Constants.PATTERN1, Constants.PATHPATTERN2, Constants.PATHPATTERN3, Constants.GET_SYSTEM_URL)
                .permitAll().anyRequest().authenticated().and().formLogin().loginPage(Constants.URL_PATH)
                .successHandler(this.authSuccess).permitAll().failureHandler(this.authFailure).permitAll().and()
                .rememberMe().rememberMeServices(rememberMeServices()).key(Constants.REMEMBER_ME_SECRET_KEY)
                .tokenValiditySeconds(86400).and().logout().logoutSuccessUrl(Constants.URL_PATH).and().csrf().disable()
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())

....
}

Ошибка:

SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/neorchaWEBPlatform] threw exception
org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [delete from persistent_logins where username = ?]; SQL state [3D000]; error code [1046]; No database selected; nested exception is java.sql.SQLException: No database selected
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:645)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:866)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:927)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:937)
    at org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl.removeUserTokens(JdbcTokenRepositoryImpl.java:114)
    at org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices.logout(PersistentTokenBasedRememberMeServices.java:166)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:112)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.sql.SQLException: No database selected
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:965)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3976)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3912)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2530)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2683)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2486)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1858)
    at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2079)
    at com.mysql.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2013)
    at com.mysql.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:5104)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1998)
    at org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:87)
    at org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:87)
    at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:873)
    at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:866)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:629)

Может кто-нибудь помочь нам, пожалуйста, чтобы исправить это или предложить решение?.. Заранее спасибо!

1 ответ

Решение

Реализовано вот решение:

Создайте копию класса JdbcTokenExtend в JdbcTokenRepositoryImpl:

package com.lbc.takeoff.neorcha.web;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

public class JdbcTokenExtend extends JdbcDaoSupport implements PersistentTokenRepository{  

    private boolean createTableOnStartup;

    protected void initDao() {
        if (createTableOnStartup) {
            getJdbcTemplate().execute("create table " + getSchemaName() + ".persistent_logins (username varchar(64) not null, series varchar(64) primary key, "
                    + "token varchar(64) not null, last_used timestamp not null)");
        }
    }

    public void createNewToken(PersistentRememberMeToken token) {
        getJdbcTemplate().update("insert into " + getSchemaName() + ".persistent_logins (username, series, token, last_used) values(?,?,?,?)", token.getUsername(), token.getSeries(),
                token.getTokenValue(), token.getDate());
    }

    public void updateToken(String series, String tokenValue, Date lastUsed) {
        getJdbcTemplate().update("update " + getSchemaName() + ".persistent_logins set token = ?, last_used = ? where series = ?", tokenValue, lastUsed, series);
    }

    /**
     * Loads the token data for the supplied series identifier.
     *
     * If an error occurs, it will be reported and null will be returned (since the result
     * should just be a failed persistent login).
     *
     * @param seriesId
     * @return the token matching the series, or null if no match found or an exception
     * occurred.
     */
    public PersistentRememberMeToken getTokenForSeries(String seriesId) {
        try {
            return getJdbcTemplate().queryForObject("select username,series,token,last_used from " + getSchemaName() + ".persistent_logins where series = ?",
                    new RowMapper<PersistentRememberMeToken>() {
                        public PersistentRememberMeToken mapRow(ResultSet rs, int rowNum)
                                throws SQLException {
                            return new PersistentRememberMeToken(rs.getString(1), rs
                                    .getString(2), rs.getString(3), rs.getTimestamp(4));
                        }
                    }, seriesId);
        }
        catch (EmptyResultDataAccessException zeroResults) {
            if (logger.isDebugEnabled()) {
                logger.debug("Querying token for series '" + seriesId
                        + "' returned no results.", zeroResults);
            }
        }
        catch (IncorrectResultSizeDataAccessException moreThanOne) {
            logger.error("Querying token for series '" + seriesId
                    + "' returned more than one value. Series" + " should be unique");
        }
        catch (DataAccessException e) {
            logger.error("Failed to load token for series " + seriesId, e);
        }

        return null;
    }

    public void removeUserTokens(String username) {
        getJdbcTemplate().update("delete from " + getSchemaName() + ".persistent_logins where username = ?", username);
    }

    /**
     * Intended for convenience in debugging. Will create the persistent_tokens database
     * table when the class is initialized during the initDao method.
     *
     * @param createTableOnStartup set to true to execute the
     */
    public void setCreateTableOnStartup(boolean createTableOnStartup) {
        this.createTableOnStartup = createTableOnStartup;
    }

    public String getSchemaName() {
// taking the schema name from the schema_name.dns.com
            final ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
            final HttpServletRequest request = attr.getRequest();

        String requestUrl = request.getRequestURL().toString();
        String schemaName = requestUrl.substring(requestUrl.lastIndexOf("://") + 3, requestUrl.lastIndexOf(".dns.com"));
        return schemaName;
    }

}

В SecurityConfigWeb:

/**
     * This bean is the JDBC token repository for remember me services.
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
//      JdbcTokenRepositoryImpldb = new JdbcTokenRepositoryImpl();  // old code deleted
        JdbcTokenExtend db = new JdbcTokenExtend(); // created this java class (copy on JdbcTokenRepositoryImpl)
        db.setCreateTableOnStartup(false);
        db.setDataSource(dataSource);
        return db;
    }

Это изменение устранило проблему, потому что теперь "delete from persistent_logins, где username" назначено конкретному имени схемы

Другие вопросы по тегам