Google Code Prettify

2018年7月18日 星期三

Spring Security: 資料庫認證、授權

繼續前一篇 (Spring Security: getting started),這裡要將原本帳密寫在記憶體,改成帳密記錄在資料庫,也就是說,使用者登入後,系統應該到資料庫中取出帳密來比對認證。
  • Database
create table users(
  username varchar(50) not null primary key,
  password varchar(100) not null,
  enabled boolean not null
);
create table authorities (
  username varchar(50) not null,
  authority varchar(50) not null,
  constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);


insert into users(username,password,enabled)
 values('admin','$2a$10$hbxecwitQQ.dDT4JOFzQAulNySFwEpaFLw38jda6Td.Y/cOiRzDFu',true);
insert into authorities(username,authority) 
 values('admin','ROLE_ADMIN');
先在資料厙中建立上述兩個 table,並 insert 進資料,使用者只有一個 admin,密碼是加密過的,未加密前的密碼為 admin@123,加密的方式如下:
String encoded=new BCryptPasswordEncoder().encode("admin@123");
System.out.println(encoded);
  • build.gradle
buildscript {
   ext {
     springBootVersion = '2.0.3.RELEASE'
   }
   repositories {
       mavenCentral()
       jcenter()
       maven { url "https://repo.spring.io/libs-release" }
       maven { url "http://maven.springframework.org/milestone" }
       maven { url "http://repo.maven.apache.org/maven2" }
       maven { url "http://repo1.maven.org/maven2/" }
       maven { url "http://amateras.sourceforge.jp/mvn/" }
   }
   dependencies {
       classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
   }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'idv.steven.mysecurity'
version = '0.0.1'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
    jcenter()
    maven { url "https://repo.spring.io/libs-release" }
    maven { url "http://maven.springframework.org/milestone" }
    maven { url "http://repo.maven.apache.org/maven2" }
    maven { url "http://repo1.maven.org/maven2/" }
    maven { url "http://amateras.sourceforge.jp/mvn/" }
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    compile('org.springframework.boot:spring-boot-starter-security')
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compileOnly('org.projectlombok:lombok')
 
    testCompile("junit:junit")
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile('org.springframework.security:spring-security-test')
 
    compile group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.2.5'
}
我使用的是 MariaDB,所以要載入 MariaDB 的 JDBC driver,同時也要載入 spring jdbc 相關的 jar 檔,所以加入上面紅色的兩行。
  • application.properties
spring.datasource.url=jdbc:mariadb://localhost:3306/demo
spring.datasource.username=steven
spring.datasource.password=p@ssw0rd
在 application.properties 設定好 url、username、password,spring boot 就會幫我們建立好資料庫相關的連線。
  • WebSecurityConfig.java
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 private DataSource dataSource;
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
            .usersByUsernameQuery("select username, password, enabled"
                + " from users where username=?")
            .authoritiesByUsernameQuery("select username, authority "
                + "from authorities where username=?")
            .passwordEncoder(new BCryptPasswordEncoder());
    }
}
前一篇為了方便將帳密放在記憶體,這裡改成紅色的方式,到資料庫中查詢,在繼承 WebSecurityConfigurerAdapter 的類別中,覆寫 configure(AuthenticationManagerBuilder auth) method,AuthenticationManagerBuilder 是個 builder pattern 的類別,設定好驗證帳密的規則後,當 spring security 由 spring container 取得 UserDetailsService object 時,會由 builder 產生一個新的 object。

注意一下上面的兩個 sql,第一個 select username, password, enabled from users where username = ? 用來認證,第二個 select username, authority from authorities where username = ? 則是查出使用者被授予那些角色? 以這個例子來說,查出來會是 ROLE_ADMIN,spring security 會忽略 ROLE_ 只認定使用者擁有 ADMIN 這個角色。

沒有留言:

張貼留言