Spring Security

Answers are from chat.openai.com

Q: Does Spring Security WebSecurityConfigurerAdapter must use Form-Submittion?

A: No, WebSecurityConfigurerAdapter does not require the use of form submission. WebSecurityConfigurerAdapter is a class provided by Spring Security to help configure security settings for your web application. While it includes convenient methods for configuring form-based authentication, it’s not limited to that mechanism.

You have the flexibility to configure different authentication mechanisms based on your application’s requirements. Spring Security supports various authentication methods, including:

  1. Form-Based Authentication: This is the traditional login form where users provide their credentials (username and password) via an HTML form.
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
           .formLogin()
               .loginPage("/login")
               .permitAll()
               .and()
           .authorizeRequests()
               .antMatchers("/public/**").permitAll()
               .anyRequest().authenticated();
   }
  1. HTTP Basic Authentication: This is a simple authentication mechanism where the browser prompts the user for a username and password.
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
           .httpBasic()
               .and()
           .authorizeRequests()
               .antMatchers("/public/**").permitAll()
               .anyRequest().authenticated();
   }
  1. Token-Based Authentication (JWT): This is commonly used in modern web applications where a token is issued upon successful authentication and sent with each request for authentication.
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http
           .csrf().disable()
           .authorizeRequests()
               .antMatchers("/authenticate").permitAll()
               .anyRequest().authenticated()
               .and().sessionManagement()
               .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
   }

In the third example, it’s configuring Spring Security to be stateless and use token-based authentication. This doesn’t rely on traditional form submissions but rather expects authentication information (e.g., username and password) to be sent in a different way, such as in the request headers.

In summary, WebSecurityConfigurerAdapter allows you to configure a variety of authentication mechanisms, and you can choose the one that best fits your application’s needs. It’s not restricted to form-based authentication.

Reference

http://pro.ctlok.com/2010/02/spring-security-3.html

Spring Tool Suite + Git Jot Down

Checkout conflict with files…

Situation

Pull files from remote repository, “Checkout conflict with files…” occurs.

Solution

  1. Stage and commit the files
  2. Pull again
  3. If automerge is possible, everything is ok
  4. If not, the pull merges the files and inserts the merge-conflict markers→Manually edit the conflicting files.
  5. Commit and push

https://stackoverflow.com/questions/18050220/eclipse-egit-checkout-conflict-with-files-egit-doesnt-want-to-continue

Springboot String Converter Jot Down

Convert Empty String to Null in Controller Methods

import org.springframework.core.convert.converter.Converter;

public class StringTrimToNullConverter implements Converter<String, String> {

  @Override
  public String convert(String source) {
    return StringUtils.trimToNull(source);
  }
}
@Configuration
public class MyWebAppConfigurer implements WebMvcConfigurer {
  @Override
  public void addFormatters(FormatterRegistry registry) {
      registry.addConverter(new StringTrimToNullConverter());
  }

https://www.baeldung.com/spring-type-conversions

Convert Empty String to Null Using Hibernate Interceptor

Implement Interceptor

public class StringEmptyToNullInterceptor extends EmptyInterceptor {
  private static final long serialVersionUID = 1L;

  @Override
  public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
    for (int i = 0; i < state.length; i++) {
      if (types[i] instanceof StringType) {
        state[i] = StringUtils.trimToNull((String) state[i]);
      }
    }
    return true;
  }
}

Register Interceptor

@Configuration
public class DBConfig {

  @Autowired
  private DataSourceService dss;

  @Bean
  public DataSource myDataSource() {
    return dss.getDataSource();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean myEntityManagerFactory() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setShowSql(true);

    Properties jpaProperties = new Properties();
    jpaProperties.setProperty("hibernate.dialect", SQLServer2012Dialect.class.getName());
    jpaProperties.setProperty("hibernate.session_factory.interceptor", StringEmptyToNullInterceptor.class.getName());

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setJpaProperties(jpaProperties);

    factory.setPackagesToScan(new String[] { "com.my.dao.entity" });
    factory.setDataSource(myDataSource());
    return factory;
  }

  @Bean
  public PlatformTransactionManager transactionManager(@Qualifier("myEntityManagerFactory") EntityManagerFactory emf) {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(emf);
    return txManager;
  }
}

https://docs.jboss.org/hibernate/core/3.3/reference/en/html/events.html

Convert Empty String to Null Using JPA AttributeConverter

Make sure this AttributeConverter is part of the JPA @EntityScan packages

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.apache.commons.lang3.StringUtils;

@Converter(autoApply = true)
public class EmptyStringToNullConverter implements AttributeConverter<String, String> {
  @Override
  public String convertToDatabaseColumn(String attribute) {
    return StringUtils.trimToNull(attribute);
  }

  @Override
  public String convertToEntityAttribute(String dbData) {
    return dbData;
  }
}

https://stackoverflow.com/questions/66966435/converterautoapply-true-not-working-in-different-package

Springboot JPA Jot Down

Environment

spring-boot-starter-parent 2.x

mssql-jdbc

Multiple DataSource with @Primary

@Configuration
public class MyPrimaryDBConfig {

  @Autowired
  private MyDataSourceComp comp;

  @Bean
  @Primary
  public DataSource primaryDataSource() {
    return comp.getPrimaryDataSource();
  }

  @Bean
  @Primary
  public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setShowSql(true);

    Properties jpaProperties = new Properties();
    jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.SQLServer2012Dialect");

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setJpaProperties(jpaProperties);
    // Entity Scan
    factory.setPackagesToScan(new String[] { "com.my.dao.entity" });
    factory.setDataSource(primaryDataSource());
    return factory;
  }

  @Bean
  @Primary
  public PlatformTransactionManager primaryTransactionManager(EntityManagerFactory emf) {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(emf);
    return txManager;
  }
}

@Configuration
public class MySecondaryDBConfig {

  @Autowired
  private MyDataSourceComp comp;

  @Bean
  public DataSource secondaryDataSource() {
    return comp.getSecondaryDataSource();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setShowSql(true);

    Properties jpaProperties = new Properties();
    jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.SQLServer2012Dialect");

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setJpaProperties(jpaProperties);
    // Entity Scan
    factory.setPackagesToScan(new String[] { "com.my.dao.secondary.entity" });
    factory.setDataSource(secondaryDataSource());
    return factory;
  }

  @Bean
  public PlatformTransactionManager myTransactionManager(EntityManagerFactory emf) {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(emf);
    return txManager;
  }
}

Multiple DataSource without @Primary (Using @Qualifier)

@Configuration
public class MyPrimaryDBConfig {

  @Autowired
  private MyDataSourceComp comp;

  @Bean
  public DataSource primaryDataSource() {
    return comp.getPrimaryDataSource();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setShowSql(true);

    Properties jpaProperties = new Properties();
    jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.SQLServer2012Dialect");

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setJpaProperties(jpaProperties);
    // Entity Scan
    factory.setPackagesToScan(new String[] { "com.my.dao.entity" });
    factory.setDataSource(primaryDataSource());
    return factory;
  }

  @Bean
  public PlatformTransactionManager myTransactionManager(@Qualifier("primaryEntityManagerFactory") EntityManagerFactory emf) {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(emf);
    return txManager;
  }
}

@Configuration
public class MySecondaryDBConfig {

  @Autowired
  private MyDataSourceComp comp;

  @Bean
  public DataSource secondaryDataSource() {
    return comp.getSecondaryDataSource();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory() {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setShowSql(true);

    Properties jpaProperties = new Properties();
    jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.SQLServer2012Dialect");

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setJpaProperties(jpaProperties);
    // Entity Scan
    factory.setPackagesToScan(new String[] { "com.my.dao.secondary.entity" });
    factory.setDataSource(secondaryDataSource());
    return factory;
  }

  @Bean
  public PlatformTransactionManager myTransactionManager(@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory emf) {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(emf);
    return txManager;
  }
}

JPA Component Scan Exclude Package

Situation: primary/secondary jpa repository in the same package

com.my.dao → primary repository package

com.my.dao.secondary → secondary repository package

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  basePackages = {"com.my.dao"},
  excludeFilters = @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "com.my.dao.secondary.*"),
  entityManagerFactoryRef = "primaryEntityManagerFactory",
  transactionManagerRef = "primaryTransactionManager")
public class MyPrimaryRepoConfig {
}

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  basePackages = {"com.my.dao.secondary"},
  entityManagerFactoryRef = "secondaryEntityManagerFactory",
  transactionManagerRef = "secondaryTransactionManager")
public class MySecondaryRepoConfig {
}

Bootstrap-Select Jot Down

Environment

Bootstrap v3.X

(jquery.js/bootstrap.js/bootstrap.css)

Bootstrap-Select v1.11.X

(bootstrap-select.js/bootstrap-select.css)

jQuery Plugin 基本上版本要互相配合(JS、CSS、Version),不然有時執行結果不如預期,也可能出Exception

載入的順序也滿重要的:jquery > bootstrap > bootstrap-select

Dynamic Create Options

<select class="js-selectpicker selectpicker">
  <option value="" selected disabled>Please Select One</option>
</select>

var elm = $('.js-selectpicker');

elm.find('option').not(':first').remove(); → remove all options except the first one

var dataList = [{'value':'1', 'desc':'Option 1'},...] ;
$.each(dataList, function(index, data) {
  elm.append($('<option>').prop("value",data.value).text(data.desc));
}

elm.selectpicker('refresh');  → render new select

Remove All Options Except the First One

var elm = $('.js-selectpicker');
elm.find('option').not(':first').remove();
⇢ $('.js-selectpicker option:not(:first)').remove();
elm.selectpicker('refresh');

Reset

$('.js-selectpicker').val('');
elm.selectpicker('refresh');

Reference

https://developer.snapappointments.com/bootstrap-select/