項目 | 日期 | |
---|---|---|
* |
Spring Security
| |
1 | Spring Security: getting started |
2018/07/18
|
2 | Spring Security: 資料庫認證、授權 |
2018/07/18
|
3 | Spring Security: AuthenticationManager |
2019/01/24
|
4 | Spring Security: 密碼加密 |
2018/08/14
|
5 | Spring Security: AES 加解密 |
2022/04/19
|
* |
Spring Batch
| |
1 | Spring Batch: getting started |
2018/05/23
|
2 | Spring Batch: FixedLengthTokenizer |
2018/12/07
|
3 | Spring Batch: multiple datasource |
2019/01/10
|
Google Code Prettify
2019年1月25日 星期五
Spring Security / Spring Batch
2019年1月24日 星期四
Spring Security: AuthenticationManager
前一篇 (Spring Security: 資料庫認證、授權) 說明了怎麼使用資料庫認證,但是,看完可能會有點混淆,似乎 Spring Security 的認證、授權,就一定要使用到 WebSecurityConfigurerAdapter,Spring Security 一定要在 Web 中使用? 當然不是! 這裡以官方文件 Spring Security Reference 中的範例來說明。
程式簡單的要使用者從命令列輸入帳號、密碼,然後檢查帳密是否合法,這裡把帳密當成同一字串,所以第二次輸入 bob / bob 時,認證才成功,現在來說明一下程式。 SampleAuthenticationManager 這個類別實作 AuthenticationManager 介面,這個介面很簡單,只有一個 method - authenticate(Authentication authentication),只需要傳入一個實作 Authentication 介面的類別物件就行,範例傳入的是 UsernamePasswordAuthenticationToken 類別的物件。 UsernamePasswordAuthenticationToken 這個類別非常簡單的,它實作 Authentication,用來放使用者的帳號、密碼 (如果必要還可以放角色,範例放入了預設的角色 USER。),然後在 authenticate method 裡,就可以看到,我們取出帳號、密碼來比對,相同就回傳 token,失敗拋出 exception。 回到主程式,可以看到取得 authenticate 的回傳值後,範例程式將它放入 SecurityContextHolder 的 context 的 authentication 裡,為什麼?
這是 spring security 的要求,在認證成功後,要將使用者的 token 放入 SecurityContext 裡,未來 spring security 需要使用者的權限時,會自行取用。
- AuthenticationExample
public class AuthenticationExample {
private static AuthenticationManager am = new SampleAuthenticationManager();
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.out.println("Please enter your username:");
String name = in.readLine();
System.out.println("Please enter your password:");
String password = in.readLine();
try {
Authentication request = new UsernamePasswordAuthenticationToken(name, password);
Authentication result = am.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
break;
} catch(AuthenticationException e) {
System.out.println("Authentication failed: " + e.getMessage());
}
}
System.out.println("Successfully authenticated. Security context contains: " +
SecurityContextHolder.getContext().getAuthentication());
}
}
- SampleAuthenticationManager
public class SampleAuthenticationManager implements AuthenticationManager {
static final List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
static {
AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
}
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
if (auth.getName().equals(auth.getCredentials())) {
return new UsernamePasswordAuthenticationToken(
auth.getName(), auth.getCredentials(), AUTHORITIES
);
}
throw new BadCredentialsException("Bad Credentials");
}
}
- 執行結果
Please enter your username:
bob
Please enter your password:
password
Authentication failed: Bad Credentials
Please enter your username:
bob
Please enter your password:
bob
Successfully authenticated. Security context contains: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d0230: Principal: bob; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER
Authentication result = am.authenticate(request); SecurityContextHolder.getContext().setAuthentication(result);
2019年1月10日 星期四
Spring Batch: multiple datasource
在 spring boot 環境中,使用 spring batch,如果需要連線兩個不同資料庫,要怎麼做呢?
如上,db1、db2 分別連到不同的兩個資料庫,但是,除此之外,還要設定一個給 spring batch 使用,所以有 batch (第一個) 的 dataSource 設定,雖然 spring batch 與 db1 使用同一個資料庫,還是要另外設定 dataSource,否則在執行到 spring batch 的 tasklet 時,如果裡面有存取到 db1、db2 的資料庫時,會抓不到 transaction。
三個 transaction manager 需有一個設定為 primary,萬一有任何的 dao 沒有指明用那一個,系統才知道預設是那個。
在有 @EnableBatchProcessing 的類別裡,建立一個 BatchConfigurer 的 bean,指定 spring batch 要使用的 dataSource。
- 在 application.properties 定義多個 dataSource
batch.datasource.driver-class-name=oracle.jdbc.OracleDriver batch.datasource.url=jdbc:oracle:thin:@192.168.50.12:1521:testDB1 batch.datasource.username=dbuser1 batch.datasource.password=p@ssword db1.datasource.driver-class-name=oracle.jdbc.OracleDriver db1.datasource.url=jdbc:oracle:thin:@192.168.50.12:1521:testDB1 db1.datasource.username=dbuser1 db1.datasource.password=p@ssword db2.datasource.driver-class-name=oracle.jdbc.OracleDriver db2.datasource.url=jdbc:oracle:thin:@192.168.51.168:1521:testDB2 db2.datasource.username=dbuser2 db2.datasource.password=p@ssword
- 設定多個 dataSource
@Configuration
public class MultipleDataSourceConfig {
@Autowired
private Environment env;
@Bean(name = "batchDatasource")
@ConfigurationProperties(prefix = "batch.datasource")
public DataSource batchDataSource() {
return DataSourceBuilder.create()
.driverClassName(env.getProperty("batch.datasource.driver-class-name"))
.url(env.getProperty("batch.datasource.url"))
.username(env.getProperty("batch.datasource.username"))
.password(env.getProperty("batch.datasource.password"))
.build();
}
@Bean(name = "db1Datasource")
@Primary
@ConfigurationProperties(prefix = "db1.datasource")
public DataSource db1DataSource() {
return DataSourceBuilder.create()
.driverClassName(env.getProperty("db1.datasource.driver-class-name"))
.url(env.getProperty("db1.datasource.url"))
.username(env.getProperty("db1.datasource.username"))
.password(env.getProperty("db1.datasource.password"))
.build();
}
@Bean(name = "db2Datasource")
@ConfigurationProperties(prefix = "db2.datasource")
public DataSource db2DataSource() {
return DataSourceBuilder.create()
.driverClassName(env.getProperty("db2.datasource.driver-class-name"))
.url(env.getProperty("db2.datasource.url"))
.username(env.getProperty("db2.datasource.username"))
.password(env.getProperty("db2.datasource.password"))
.build();
}
}
- 為每個 dataSource 設定相關的 transactionManager
- BatchDbConfig.java
@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "batchEntityManagerFactory", basePackages = { "idv.steven.batch.dao" }, transactionManagerRef = "batchTransactionManager" ) public class BatchDbConfig { @Autowired @Qualifier("batchDatasource") private DataSource datasource; private JpaVendorAdapter jpaVendorAdapter() { HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); adapter.setDatabase(Database.ORACLE); adapter.setShowSql(true); adapter.setGenerateDdl(false); adapter.setDatabasePlatform("org.hibernate.dialect.Oracle12cDialect"); return adapter; } /** * 載入 Entity * @return */ @Bean public LocalContainerEntityManagerFactoryBean batchEntityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(datasource); emf.setPackagesToScan(new String[] { "idv.steven.batch.entity" }); emf.setJpaVendorAdapter(this.jpaVendorAdapter()); emf.setSharedCacheMode(SharedCacheMode.NONE); return emf; } @Bean(name = "batchTransactionManager") public PlatformTransactionManager batchTransactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(this.batchEntityManagerFactory().getObject()); return tm; } }
- DB1DbConfig.java
@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "db1EntityManagerFactory", basePackages = { "idv.steven.database.db1.dao" }, transactionManagerRef = "db1TransactionManager" ) public class DB1DbConfig { @Autowired @Qualifier("db1Datasource") private DataSource datasource; private JpaVendorAdapter jpaVendorAdapter() { HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); adapter.setDatabase(Database.ORACLE); adapter.setShowSql(true); adapter.setGenerateDdl(false); adapter.setDatabasePlatform("org.hibernate.dialect.Oracle12cDialect"); return adapter; } /** * 載入 Entity * @return */ @Primary @Bean public LocalContainerEntityManagerFactoryBean db1EntityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(datasource); emf.setPackagesToScan(new String[] { "idv.steven.database.db1.entity" }); emf.setJpaVendorAdapter(this.jpaVendorAdapter()); emf.setSharedCacheMode(SharedCacheMode.NONE); return emf; } @Primary @Bean(name = "db1TransactionManager") public PlatformTransactionManager db1TransactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(this.db1EntityManagerFactory().getObject()); return tm; } }
- DB2DbConfig.java
@Configuration @EnableTransactionManagement @EnableJpaRepositories( entityManagerFactoryRef = "db2EntityManagerFactory", basePackages = { "idv.steven.database.db2.dao" }, transactionManagerRef = "db2TransactionManager" ) public class DB2DbConfig { @Autowired @Qualifier("db2Datasource") private DataSource datasource; private JpaVendorAdapter jpaVendorAdapter() { HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); adapter.setDatabase(Database.ORACLE); adapter.setShowSql(true); adapter.setGenerateDdl(false); adapter.setDatabasePlatform("org.hibernate.dialect.Oracle12cDialect"); return adapter; } /** * 載入 Entity * @return */ @Bean public LocalContainerEntityManagerFactoryBean db2EntityManagerFactory() { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); emf.setDataSource(datasource); emf.setPackagesToScan(new String[] { "idv.steven.database.db2.entity" }); emf.setJpaVendorAdapter(this.jpaVendorAdapter()); emf.setSharedCacheMode(SharedCacheMode.NONE); return emf; } @Bean(name = "db2TransactionManager") public PlatformTransactionManager db2TransactionManager() { JpaTransactionManager tm = new JpaTransactionManager(); tm.setEntityManagerFactory(this.db2EntityManagerFactory().getObject()); return tm; } }
- 指定 spring batch 使用那一個 dataSource
@Configuration @EnableBatchProcessing public class BatchConfig { @Bean BatchConfigurer configurer(@Qualifier("batchDatasource") DataSource dataSource){ return new DefaultBatchConfigurer(dataSource); } }
在有 @EnableBatchProcessing 的類別裡,建立一個 BatchConfigurer 的 bean,指定 spring batch 要使用的 dataSource。
訂閱:
文章 (Atom)