Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
セキュリティ(ログイン認証)編
アジェンダ
 はじめに
 準備
 認証方式
 ユーザー認証処理
 ユーザー認証処理のカスタマイズ
 パスワードエンコード
 BCryptを使ったパスワードのハッシュ化
 ロール
 ユニットテスト
 静的コンテンツの配置
 ...
はじめに
 Spring Securityを用いたログイン認証について説明する
はじめに
 使用ライブラリ&バージョン
■Spring Security 4.0
準備
 Maven依存関係
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-sec...
準備
 設定用クラスの作成
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
準備
 設定用クラスの作成
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
認証方式・ユーザー認証処理
 認証方式とユーザー認証処理を組み合わせる
■認証方式
BASIC認証、Form認証
■ユーザー認証処理(何を基にユーザー認証をするか)
インメモリ(プログラム内固定)、
データベース、LDAP など
認証方式
 設定メソッドの上書き
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter...
認証方式
 アクセス出来るURLの範囲を決める
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(...
認証方式
 BASIC認証を使用する
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.an...
認証方式
 ここまでの状態を試す
http://localhost:8080/test
http://localhost:8080/sample
その他のURLでは
全て認証を求められる
BASIC認証をするよう実装したので
BASIC認証の...
認証方式
 以下の画面遷移でForm認証を使用する
ホーム画面
挨拶画面
【Click here】クリック
参考:Securing a Web Application
http://spring.io/guides/gs/securing-w...
認証方式
 以下の画面遷移でForm認証を使用する
ホーム画面
挨拶画面
【Click here】クリック
参考:Securing a Web Application
http://spring.io/guides/gs/securing-w...
認証方式
 以下の画面遷移でForm認証を使用する
ホーム画面
挨拶画面
【Click here】クリック
参考:Securing a Web Application
http://spring.io/guides/gs/securing-w...
認証方式
 ホーム画面を作成する
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec...
認証方式
 ホーム画面を作成する
home.html
認証方式
 挨拶画面を作成する
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec=...
認証方式
 挨拶画面を作成する
hello.html
ログインしたユーザー名が
埋め込まれる
認証方式
 URLとビューを関連付ける
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addView...
認証方式
 設定メソッドを変更する
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.ant...
認証方式
 URLとビューを関連付ける
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addView...
認証方式
 ログイン画面を作成する
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:se...
認証方式
 ログイン画面を作成する
login_page.html
認証方式
 ユーザー認証処理に、インメモリ認証を使用する
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfi...
認証方式
 ユーザー認証処理に、インメモリ認証を使用する
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
aut...
認証方式
 動作確認
http://localhost:8080/
user / password
を入力
userが表示される
admin / password
を入力 admin が表示される
ユーザー認証処理
 データベース登録したにログイン情報で認証処理を試す
ユーザー認証処理
 接続先をapplication.ymlに記述する
spring:
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://xxx.xxx.x...
ユーザー認証処理
 ユーザー認証処理をJDBC方式にする
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationM...
ユーザー認証処理
 デフォルトのユーザー情報テーブル
create table users(
username varchar_ignorecase(50) not null primary key,
password varchar_ign...
ユーザー認証処理
 テスト用のユーザーを登録
insert into users values('test_user', 'test', true);
insert into authorities values('test_user', '...
ユーザー認証処理
 動作確認
http://localhost:8080/
test_user / test
を入力
test_userが
表示される
ユーザー認証処理のカスタマイズ
 自前の認証処理を実装する
create table user_info (
username varchar(50) not null primary key,
email varchar(50) not n...
ユーザー認証処理のカスタマイズ
 ユーザー情報用のエンティティを作成する
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="user_info")
public...
ユーザー認証処理のカスタマイズ
 ユーザー情報用のエンティティを作成する
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="user_info")
public...
ユーザー認証処理のカスタマイズ
 ユーザー情報用のリポジトリを作成する
public interface UserInfoRepository
extends JpaRepository<UserInfo, String> {
public ...
ユーザー認証処理のカスタマイズ
 自前の認証処理を実装したServiceクラスを作成する
@Service
public class UserInfoService
implements UserDetailsService {
@Overr...
ユーザー認証処理のカスタマイズ
 自前の認証処理を実装したServiceクラスを作成する
@Override
public UserDetails loadUserByUsername(String s)
throws UsernameNot...
ユーザー認証処理のカスタマイズ
 Serviceクラスを使用して認証するよう設定する
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends We...
ユーザー認証処理のカスタマイズ
 動作確認
http://localhost:8080/
test@user / test
を入力
test_userが
表示される
認証はメールアドレス
で行う
ログイン後は
ユーザー名を表示
パスワードエンコード
 パスワードを平文のまま保存するのは危険なのでエンコードして保存し、
認証時に入力されたパスワードをエンコードしてチェックする
ログイン画面
平文パスワード
エンコード
処理
エンコード済み
パスワード
データベース
読...
パスワードエンコード
 ハッシュ値が入るようテーブルを変更
create table user_info (
username varchar(50) not null primary key,
email varchar(50) not n...
パスワードエンコード
 認証プロバイダを変更
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerA...
パスワードエンコード
 認証プロバイダを変更
auth
.authenticationProvider(createAuthProvider());
// .userDetailsService(userInfoService);
// .j...
BCryptを使った
パスワードのハッシュ化
 BCryptのハッシュ値
$2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu
BCryptのハッシュ値
insert int...
BCryptを使った
パスワードのハッシュ化
 BCryptのヘッダ
$2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu
ヘッダ ソルト値 22文字 ハッシュ値 32文字
...
BCryptを使った
パスワードのハッシュ化
 BCryptのソルト値
$2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu
ヘッダ ソルト値 22文字 ハッシュ値 32文字...
BCryptを使った
パスワードのハッシュ化
 BCryptのハッシュ値
$2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu
ヘッダ ソルト値 22文字 ハッシュ値 32文...
BCryptを使った
パスワードのハッシュ化
 Spring SecurityでのBCryptのハッシュ値の生成方法
String hashedPassword
= BCrypt.hashpw( rawPassword, BCrypt.gen...
BCryptを使った
パスワードのハッシュ化
 Spring SecurityでのBCryptのハッシュ値の生成方法
String hashedPassword
= BCrypt.hashpw( “testpassword”, BCrypt....
ロール
 Spring Securityのユーザごとの権限に応じたアクセス制御
UserDetails
GrantedAuthority
1
0..*
ユーザー名等の情報を保持
そのユーザーの
権限情報を保持
権限情報は
複数持てる 権限情報...
ロール
 サンプルのテーブル構成と実装
create table user_info (
username varchar(50) not null primary key,
email varchar(50) not null,
passw...
ロール
 サンプルユーザー
insert into user_info values('test_user', 'test@user',
'xxx', TRUE, 'ROLE_USER');
insert into user_info val...
ロール
 アクセス出来るURLの範囲を変更する
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurer...
ロール
 動作確認
管理者ユーザーで
ログイン
管理者のみのURLへ
アクセス可能
一般ユーザーで
ログイン 管理者ではないので
「403 Forbidden」
(閲覧禁止)が返ってくる
ユニットテスト
 MockMVCを利用してテストを行う
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(...
ユニットテスト
 MockMVCにSpring Securityの利用を通知
import static org.springframework.security.test.web.servlet.setup.
SecurityMockMvc...
ユニットテスト
 ログインしないでアクセスするテスト
@Test
public void ログインせずにルートページへアクセス() throws Exception {
mockMvc.perform(get("/"))
.andExpect...
ユニットテスト
 ログインしてアクセスするテスト
@Test
@WithMockUser(username="test@user")
public void ログインしてハローページへアクセス() throws Exception {
moc...
ユニットテスト
 権限を指定してアクセスするテスト
@Test
@WithMockUser(username="user@user", roles="USER")
public void 一般ユーザーで管理者ページへアクセス() throws...
ユニットテスト
 Formログインにアクセスするテスト
@Test
public void ログインページのテスト() throws Exception {
mockMvc.perform(
formLogin()
.user("userna...
静的コンテンツの配置
 ログイン前でも画像・CSS・JavaScript等は参照可能とする
<div><input type="submit" value="Sign In"/></div>
</form>
<img src="/img/te...
静的コンテンツの配置
 ログイン前でも画像・CSS・JavaScript等は参照可能とする
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends...
まとめ
 参考
■Spring Security Reference
http://docs.spring.io/spring-security/site/docs/4.0.3.CI-
SNAPSHOT/reference/htmlsingl...
Upcoming SlideShare
Loading in …5
×

Spring bootでweb セキュリティ(ログイン認証)編

24,740 views

Published on

Spring Securityを用いたログイン認証。

Published in: Engineering
  • Be the first to comment

Spring bootでweb セキュリティ(ログイン認証)編

  1. 1. セキュリティ(ログイン認証)編
  2. 2. アジェンダ  はじめに  準備  認証方式  ユーザー認証処理  ユーザー認証処理のカスタマイズ  パスワードエンコード  BCryptを使ったパスワードのハッシュ化  ロール  ユニットテスト  静的コンテンツの配置  まとめ
  3. 3. はじめに  Spring Securityを用いたログイン認証について説明する
  4. 4. はじめに  使用ライブラリ&バージョン ■Spring Security 4.0
  5. 5. 準備  Maven依存関係 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies>
  6. 6. 準備  設定用クラスの作成 @Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { } 設定クラスであることを @Configurationアノテーションで明記 WebMvcSecurityの有効化 既存のAdapterを拡張
  7. 7. 準備  設定用クラスの作成 @Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { } このクラスに様々な設定を記述する
  8. 8. 認証方式・ユーザー認証処理  認証方式とユーザー認証処理を組み合わせる ■認証方式 BASIC認証、Form認証 ■ユーザー認証処理(何を基にユーザー認証をするか) インメモリ(プログラム内固定)、 データベース、LDAP など
  9. 9. 認証方式  設定メソッドの上書き @Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { } } configureメソッドをオーバーライドし、 引数のHttpSecurityに対して 様々な設定を行う
  10. 10. 認証方式  アクセス出来るURLの範囲を決める @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated(); } HttpSecurityから、 アクセス範囲を決めるクラスを取得 ルート「/」は全ユーザーが アクセス可能 他のURLは認証が必要
  11. 11. 認証方式  BASIC認証を使用する @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated() .and() .httpBasic(); } BASIC認証を使用する
  12. 12. 認証方式  ここまでの状態を試す http://localhost:8080/test http://localhost:8080/sample その他のURLでは 全て認証を求められる BASIC認証をするよう実装したので BASIC認証の画面が出る http://localhost:8080/ ルートURLは全ユーザーが アクセス可能 (ただし今は未実装なので Not Foundエラーになる)
  13. 13. 認証方式  以下の画面遷移でForm認証を使用する ホーム画面 挨拶画面 【Click here】クリック 参考:Securing a Web Application http://spring.io/guides/gs/securing-web/ URL: / URL: /hello
  14. 14. 認証方式  以下の画面遷移でForm認証を使用する ホーム画面 挨拶画面 【Click here】クリック 参考:Securing a Web Application http://spring.io/guides/gs/securing-web/ URL: / URL: /hellohttp .authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated(); 下のアクセス制御により、ルート「/」は 全ユーザーがアクセス出来るので 問題なく表示される ただしルート「/」以外は 認証が必要になる。
  15. 15. 認証方式  以下の画面遷移でForm認証を使用する ホーム画面 挨拶画面 【Click here】クリック 参考:Securing a Web Application http://spring.io/guides/gs/securing-web/ URL: / URL: /hello ログイン画面 そのため実際は 間にログイン画面が挟まる
  16. 16. 認証方式  ホーム画面を作成する <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Spring Security Example</title> </head> <body> <h1>Welcome!</h1> <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p> </body> </html> home.html
  17. 17. 認証方式  ホーム画面を作成する home.html
  18. 18. 認証方式  挨拶画面を作成する <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Hello World!</title> </head> <body> <h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1> <form th:action="@{/logout}" method="post"> <input type="submit" value="Sign Out"/> </form> </body> </html> hello.html ログインしたユーザー名が 埋め込まれる
  19. 19. 認証方式  挨拶画面を作成する hello.html ログインしたユーザー名が 埋め込まれる
  20. 20. 認証方式  URLとビューを関連付ける @Configuration public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/home").setViewName("home"); registry.addViewController("/").setViewName("home"); registry.addViewController("/hello").setViewName("hello"); } } 本来はコントローラを作成してビューを制御するが、 今回は簡易版としてWebMvcConfigurerAdapterを 拡張してURLとビューのテンプレートをお手軽に関連付ける /home もしくは / は home.html を /hello は hello.html を表示する
  21. 21. 認証方式  設定メソッドを変更する @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated() .and() // .httpBasic(); .formLogin() .loginPage("/login_page") .usernameParameter("username") .passwordParameter("password") .permitAll(); } BASIC認証をやめて Form認証を行う 認証のページは /login_page ユーザー名とパスワードの項目は username と password 全ユーザーのアクセスを許可する
  22. 22. 認証方式  URLとビューを関連付ける @Configuration public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/home").setViewName("home"); registry.addViewController("/").setViewName("home"); registry.addViewController("/hello").setViewName("hello"); registry.addViewController("/login_page").setViewName("login_page"); } } ログインページ用の関連付けを追加
  23. 23. 認証方式  ログイン画面を作成する <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> <head> <title>Spring Security Example </title> </head> <body> <form th:action="@{/login_page}" method="post"> <div><label> User Name : <input type="text" name="username"/> </label></div> <div><label> Password: <input type="password" name="password"/> </label></div> <div><input type="submit" value="Sign In"/></div> </form></body></html> login_page.html ユーザー名とパスワードの項目は username と password
  24. 24. 認証方式  ログイン画面を作成する login_page.html
  25. 25. 認証方式  ユーザー認証処理に、インメモリ認証を使用する @Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { ~~ 中略 ~~ } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { } } HttpSecurityを引数に受けるconfigureメソッドの他に AuthenticationManagerBuilderを受け取るメソッドがあり、 ユーザー認証処理はここに設定を記述する
  26. 26. 認証方式  ユーザー認証処理に、インメモリ認証を使用する @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user").password("password").roles("USER").and() .withUser("admin").password("password").roles("USER", "ADMIN"); } } インメモリ認証を行う指定 ユーザーは、user と admin の2つを用意する。 パスワードはいずれも password で、 ロールは user が USERで、adminは USER と ADMIN。 (ロールについては後ほど)
  27. 27. 認証方式  動作確認 http://localhost:8080/ user / password を入力 userが表示される admin / password を入力 admin が表示される
  28. 28. ユーザー認証処理  データベース登録したにログイン情報で認証処理を試す
  29. 29. ユーザー認証処理  接続先をapplication.ymlに記述する spring: datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://xxx.xxx.xxx.xxx/database username: databaseusername password: xxx 接続先に応じて変更を!
  30. 30. ユーザー認証処理  ユーザー認証処理をJDBC方式にする @Autowired private DataSource dataSource; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth // .inMemoryAuthentication() // .withUser("user").password("password").roles("USER").and() // .withUser("admin").password("password").roles("USER", "ADMIN"); .jdbcAuthentication() .dataSource(dataSource); } } コンテナが管理している 接続先情報を取得 JDBC方式を指定する 接続先を指定する
  31. 31. ユーザー認証処理  デフォルトのユーザー情報テーブル create table users( username varchar_ignorecase(50) not null primary key, password varchar_ignorecase(50) not null, enabled boolean not null ); create table authorities ( username varchar_ignorecase(50) not null, authority varchar_ignorecase(50) not null, constraint fk_authorities_users foreign key(username) references users(username) ); create unique index ix_auth_username on authorities (username,authority); ■Spring Security Reference - 37.1 User Schema より http://docs.spring.io/spring-security/site/docs/4.0.3.CI-SNAPSHOT/reference/htmlsingle/#user-schema
  32. 32. ユーザー認証処理  テスト用のユーザーを登録 insert into users values('test_user', 'test', true); insert into authorities values('test_user', 'ROLE_USER');
  33. 33. ユーザー認証処理  動作確認 http://localhost:8080/ test_user / test を入力 test_userが 表示される
  34. 34. ユーザー認証処理のカスタマイズ  自前の認証処理を実装する create table user_info ( username varchar(50) not null primary key, email varchar(50) not null, password varchar(50) not null, enabled boolean not null, authority varchar(50) not null ); create unique index ix_user_info_email on user_info (email); 以下のテーブルに登録したユーザー情報で認証する テーブルを新規作成 メールアドレスを登録し、 この値とパスワードで 認証する insert into user_info values('test_user', 'test@user', 'test', TRUE, 'ROLE_USER'); サンプルデータ
  35. 35. ユーザー認証処理のカスタマイズ  ユーザー情報用のエンティティを作成する @Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name="user_info") public class UserInfo { @Id @GeneratedValue private String username; @Column(nullable = false) private String email; @Column(nullable = false) private String password; @Column(nullable = false) private Boolean enabled; @Column(nullable = false) private String authority; Lombokを使用 user_infoテーブルの エンティティとして宣言 カラムに応じた フィールドを宣言
  36. 36. ユーザー認証処理のカスタマイズ  ユーザー情報用のエンティティを作成する @Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name="user_info") public class UserInfo implements UserDetails { ~~ 中略 ~~ public Collection<GrantedAuthority> getAuthorities() { List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority(authority)); return authorities; } UserDetailsに定義された メソッドを実装 (他も同様に) Spring Securityの決まりに従って UserDetailsを実装
  37. 37. ユーザー認証処理のカスタマイズ  ユーザー情報用のリポジトリを作成する public interface UserInfoRepository extends JpaRepository<UserInfo, String> { public UserInfo findByEmail(String email); } メールアドレスで検証する メソッドを宣言 (実装はJPAに任せる) 先ほどのエンティティを扱うリポジトリ
  38. 38. ユーザー認証処理のカスタマイズ  自前の認証処理を実装したServiceクラスを作成する @Service public class UserInfoService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { return null; } } UserDetailsServiceを 実装する UserDetailsServiceに 定義されているメソッドを実装する InMemoryUserDetailsManagerなど 既存の認証処理はこのインターフェースを 実装している
  39. 39. ユーザー認証処理のカスタマイズ  自前の認証処理を実装したServiceクラスを作成する @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { if (s==null || "".equals(s)) { throw new UsernameNotFoundException("Username is empty"); } UserInfo userInfo = userInfoRepository.findByEmail(s); if (userInfo == null) { throw new UsernameNotFoundException( "User not found for name: " + s); } return userInfo; } 未入力はエラー 入力されたメールアドレスで検索 検索できなかった場合はエラー 検索できたらそれを返す
  40. 40. ユーザー認証処理のカスタマイズ  Serviceクラスを使用して認証するよう設定する @Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserInfoService userInfoService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userInfoService); // .jdbcAuthentication() // .dataSource(dataSource); } 作成したServiceクラスを宣言 JDBC認証から、 作成したServiceクラスでの 認証に切り替える
  41. 41. ユーザー認証処理のカスタマイズ  動作確認 http://localhost:8080/ test@user / test を入力 test_userが 表示される 認証はメールアドレス で行う ログイン後は ユーザー名を表示
  42. 42. パスワードエンコード  パスワードを平文のまま保存するのは危険なのでエンコードして保存し、 認証時に入力されたパスワードをエンコードしてチェックする ログイン画面 平文パスワード エンコード 処理 エンコード済み パスワード データベース 読み込み 処理 エンコード済み パスワード 認証 エンコード済みの パスワードを格納
  43. 43. パスワードエンコード  ハッシュ値が入るようテーブルを変更 create table user_info ( username varchar(50) not null primary key, email varchar(50) not null, password varchar(60) not null, enabled boolean not null, authority varchar(50) not null ); create unique index ix_user_info_email on user_info (email); 以下のテーブルに登録したユーザー情報で認証する insert into user_info values('test_user', 'test@user', '$2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu', TRUE, 'ROLE_USER'); サンプルデータ パスワードのカラムを拡張 「test」のハッシュ値
  44. 44. パスワードエンコード  認証プロバイダを変更 @Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserInfoService userInfoService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .authenticationProvider(createAuthProvider()); // .userDetailsService(userInfoService); // .jdbcAuthentication() // .dataSource(dataSource); } 作成したServiceクラスでの認証から、 後述する認証プロバイダに変更する
  45. 45. パスワードエンコード  認証プロバイダを変更 auth .authenticationProvider(createAuthProvider()); // .userDetailsService(userInfoService); // .jdbcAuthentication() // .dataSource(dataSource); } private AuthenticationProvider createAuthProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userInfoService); authProvider.setPasswordEncoder(new BCryptPasswordEncoder()); return authProvider; } 認証プロバイダの作成 認証処理を実装した Serviceクラスを指定する パスワードのエンコードに BCryptエンコーダを使用する
  46. 46. BCryptを使った パスワードのハッシュ化  BCryptのハッシュ値 $2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu BCryptのハッシュ値 insert into user_info values('test_user', 'test@user', '$2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu', TRUE, 'ROLE_USER'); サンプルデータ BCryptでハッシュ化したパスワード ヘッダ ソルト値 22文字 ハッシュ値 32文字
  47. 47. BCryptを使った パスワードのハッシュ化  BCryptのヘッダ $2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu ヘッダ ソルト値 22文字 ハッシュ値 32文字 2a → 10 → バージョン番号。 主流は「2a」。 ストレッチング(ハッシュ値の計算を繰り返す)回数の指定。 累乗数を指定する。 10の場合、210なので1,024回繰り返す。
  48. 48. BCryptを使った パスワードのハッシュ化  BCryptのソルト値 $2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu ヘッダ ソルト値 22文字 ハッシュ値 32文字 指定のソルト値とパスワードを基に 指定のストレッチ回数で求めたハッシュ値。
  49. 49. BCryptを使った パスワードのハッシュ化  BCryptのハッシュ値 $2a$10$fms1eItee/kPxD8FSdnk/uO9bC.CBweUrQE.CLRgZgzIGcFJVcLRu ヘッダ ソルト値 22文字 ハッシュ値 32文字 ハッシュ値を求める際に自動生成されるソルト値。
  50. 50. BCryptを使った パスワードのハッシュ化  Spring SecurityでのBCryptのハッシュ値の生成方法 String hashedPassword = BCrypt.hashpw( rawPassword, BCrypt.gensalt() ); ハッシュ化前のパスワード ソルト値の生成 ハッシュ化したパスワード BCryptクラスを使用
  51. 51. BCryptを使った パスワードのハッシュ化  Spring SecurityでのBCryptのハッシュ値の生成方法 String hashedPassword = BCrypt.hashpw( “testpassword”, BCrypt.gensalt() ); 上記コードを3回実行した結果 $2a$10$UmuUZJ4D6IaJMm5.UQdsAef2fz6QQuRyRDYrSFab4uNv2SzNEHFW2 $2a$10$.XMgSGCnfecWXh25jKDoZOojlyaMWODJxpoCcOh0YorGM53Vcxq22 $2a$10$1Cc9rpBWu5NenpYameu2f.nHohPeo0HfRLn7dAwKnhQMLkNdibOd6 「testpassword」のハッシュ値を取得するコード ソルト値もハッシュ値も違う 3つの値が生成された
  52. 52. ロール  Spring Securityのユーザごとの権限に応じたアクセス制御 UserDetails GrantedAuthority 1 0..* ユーザー名等の情報を保持 そのユーザーの 権限情報を保持 権限情報は 複数持てる 権限情報に応じた アクセス制御を行う
  53. 53. ロール  サンプルのテーブル構成と実装 create table user_info ( username varchar(50) not null primary key, email varchar(50) not null, password varchar(50) not null, enabled boolean not null, authority varchar(50) not null ); 以下のテーブルに登録したユーザー情報で認証する サンプルのテーブルは 1ユーザー1権限 権限を保持するカラム public class UserInfo implements UserDetails { ~~ 中略 ~~ public Collection<GrantedAuthority> getAuthorities() { List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority(authority)); return authorities; } 標準モデルに合わせて リストで返却
  54. 54. ロール  サンプルユーザー insert into user_info values('test_user', 'test@user', 'xxx', TRUE, 'ROLE_USER'); insert into user_info values('admin_user', 'admin@user', 'xxx', TRUE, 'ROLE_ADMIN'); サンプルのユーザー 一般権限ユーザー 管理者権限ユーザー
  55. 55. ロール  アクセス出来るURLの範囲を変更する @Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated(); ~~中略~~ 「/admin/」内のURLは 管理者権限ユーザーのみが アセス出来る
  56. 56. ロール  動作確認 管理者ユーザーで ログイン 管理者のみのURLへ アクセス可能 一般ユーザーで ログイン 管理者ではないので 「403 Forbidden」 (閲覧禁止)が返ってくる
  57. 57. ユニットテスト  MockMVCを利用してテストを行う @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @SpringApplicationConfiguration(classes = App.class) public class AppTest { @Autowired private WebApplicationContext context; private MockMvc mockMvc; @Before public void setup() { mockMvc = MockMvcBuilders .webAppContextSetup(context) .build(); }
  58. 58. ユニットテスト  MockMVCにSpring Securityの利用を通知 import static org.springframework.security.test.web.servlet.setup. SecurityMockMvcConfigurers.*; ~~中略~~ public void setup() { mockMvc = MockMvcBuilders .webAppContextSetup(context) .apply(springSecurity()) .build(); } MockMVCに通知
  59. 59. ユニットテスト  ログインしないでアクセスするテスト @Test public void ログインせずにルートページへアクセス() throws Exception { mockMvc.perform(get("/")) .andExpect(status().isOk()); } @Test public void ログインせずにハローページへアクセス() throws Exception { mockMvc.perform(get("/hello")) .andExpect(status().is3xxRedirection()); } ログインせずにルートへアクセスする場合は OKステータスが返ってくる ログインせずにハローページへアクセスする場合は 300番台(移動通知)のどれか (正確には302:Moved Temporarily)が 返ってくる
  60. 60. ユニットテスト  ログインしてアクセスするテスト @Test @WithMockUser(username="test@user") public void ログインしてハローページへアクセス() throws Exception { mockMvc.perform(get("/hello")) .andExpect(status().isOk()); } ログインするユーザを 「WithMockUser」アノテーションで指定 ログインした後なら OKステータスが返ってくる
  61. 61. ユニットテスト  権限を指定してアクセスするテスト @Test @WithMockUser(username="user@user", roles="USER") public void 一般ユーザーで管理者ページへアクセス() throws Exception { mockMvc.perform(get("/admin/adminPage")) .andExpect(status().is4xxClientError()); } @Test @WithMockUser(username="admin@user", roles="ADMIN") public void 管理者ーザーで管理者ページへアクセス() throws Exception { mockMvc.perform(get("/admin/adminPage")) .andExpect(status().isOk()); } 「WithMockUser」アノテーションに 権限を記述 一般ユーザーには権限がないので 400番台(処理失敗)のどれか (正確には403:Forbidden)が返ってくる 管理者ユーザーならアクセス可能
  62. 62. ユニットテスト  Formログインにアクセスするテスト @Test public void ログインページのテスト() throws Exception { mockMvc.perform( formLogin() .user("username", "test@user") .password("password", "test") .loginProcessingUrl("/login_page") ).andExpect(status().isFound()) .andExpect( authenticated() .withUsername("test_user") .withRoles("USER") ); } Formログインをする 認証に成功し、 ユーザー名とロールが想定通りか チェックする 認証情報(今回はメアド)、 パスワード、ログインURLを指定
  63. 63. 静的コンテンツの配置  ログイン前でも画像・CSS・JavaScript等は参照可能とする <div><input type="submit" value="Sign In"/></div> </form> <img src="/img/test.png"/> ログイン前は何も設定しないと、 静的コンテンツにアクセス出来ない 静的コンテンツは resources/static に配置
  64. 64. 静的コンテンツの配置  ログイン前でも画像・CSS・JavaScript等は参照可能とする @Configuration @EnableWebMvcSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/css/**", "/js/**", "/img/**"); } 除外リストに静的コンテンツを記述 ログイン前でも静的コンテンツへの アクセスが行えるようになった
  65. 65. まとめ  参考 ■Spring Security Reference http://docs.spring.io/spring-security/site/docs/4.0.3.CI- SNAPSHOT/reference/htmlsingle/ ■Spring Bootでユーザ認証 | memorandum http://ksoichiro.blogspot.jp/2015/03/spring-boot.html ■Spring Boot でログイン画面 + 一覧画面 + 登録画面の Webアプリケーションを作る ( その10 )( ロ グイン画面作成3 ) - かんがるーさんの日記 http://ksby.hatenablog.com/entry/2015/02/08/030648#LoginControllerTest.java ■BCrypt(Blowfish暗号)について調べたので文書化してみました http://www.kamiya54.info/post/100503173956/bcryptblowfish%E6%9A%97%E5%8F %B7%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E8%AA%BF%E3%81%B 9%E3%81%9F%E3%81%AE%E3%81%A7%E6%96%87%E6%9B%B8%E5%8C%96% E3%81%97%E3%81%A6%E3%81%BF%E3%81%BE%E3%81%97%E3%81%9F

×