Skip to content

Commit a7b463a

Browse files
authored
Merge pull request #11 from kento-kotlin-sandbox/future/add_spring_security
Future/add spring security
2 parents 06ae855 + e495dda commit a7b463a

File tree

9 files changed

+219
-43
lines changed

9 files changed

+219
-43
lines changed

pom.xml

+33-18
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<groupId>org.springframework.boot</groupId>
1616
<artifactId>spring-boot-starter-parent</artifactId>
17-
<version>2.1.1.RELEASE</version>
17+
<version>2.0.0.RELEASE</version>
1818
<relativePath/> <!-- lookup parent from repository -->
1919
</parent>
2020

@@ -25,61 +25,76 @@
2525
</properties>
2626

2727
<dependencies>
28+
<!-- SpringJDBC -->
2829
<dependency>
2930
<groupId>org.springframework.boot</groupId>
3031
<artifactId>spring-boot-starter-jdbc</artifactId>
3132
</dependency>
33+
<!-- Thymeleaf -->
3234
<dependency>
3335
<groupId>org.springframework.boot</groupId>
3436
<artifactId>spring-boot-starter-thymeleaf</artifactId>
3537
</dependency>
38+
<!-- SpringBoot -->
3639
<dependency>
3740
<groupId>org.springframework.boot</groupId>
3841
<artifactId>spring-boot-starter-web</artifactId>
3942
</dependency>
40-
43+
<!-- DevTools -->
4144
<dependency>
4245
<groupId>org.springframework.boot</groupId>
4346
<artifactId>spring-boot-devtools</artifactId>
4447
<scope>runtime</scope>
4548
</dependency>
49+
<!-- SpringSecurity -->
50+
<dependency>
51+
<groupId>org.springframework.boot</groupId>
52+
<artifactId>spring-boot-starter-security</artifactId>
53+
</dependency>
54+
<!-- Thyemeleaf拡張(セキュリティ) -->
55+
<dependency>
56+
<groupId>org.thymeleaf.extras</groupId>
57+
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
58+
</dependency>
59+
<!-- H2データベース -->
4660
<dependency>
4761
<groupId>com.h2database</groupId>
4862
<artifactId>h2</artifactId>
4963
<scope>runtime</scope>
5064
</dependency>
65+
<!-- Lombok -->
5166
<dependency>
5267
<groupId>org.projectlombok</groupId>
5368
<artifactId>lombok</artifactId>
5469
<optional>true</optional>
5570
</dependency>
56-
<dependency>
57-
<groupId>org.springframework.boot</groupId>
58-
<artifactId>spring-boot-starter-test</artifactId>
59-
<scope>test</scope>
60-
</dependency>
61-
6271
<!-- webjars:JQuery -->
6372
<dependency>
64-
<groupId>org.webjars</groupId>
65-
<artifactId>jquery</artifactId>
66-
<version>1.11.1</version>
73+
<groupId>org.webjars</groupId>
74+
<artifactId>jquery</artifactId>
75+
<version>1.11.1</version>
6776
</dependency>
6877
<!-- webjars:Bootstrap -->
6978
<dependency>
70-
<groupId>org.webjars</groupId>
71-
<artifactId>bootstrap</artifactId>
72-
<version>3.3.7-1</version>
79+
<groupId>org.webjars</groupId>
80+
<artifactId>bootstrap</artifactId>
81+
<version>3.3.7-1</version>
7382
</dependency>
7483
<!-- Spring AOP -->
7584
<dependency>
76-
<groupId>org.springframework</groupId>
77-
<artifactId>spring-aop</artifactId>
85+
<groupId>org.springframework</groupId>
86+
<artifactId>spring-aop</artifactId>
7887
</dependency>
7988
<!-- AspectJ -->
8089
<dependency>
81-
<groupId>org.aspectj</groupId>
82-
<artifactId>aspectjweaver</artifactId>
90+
<groupId>org.aspectj</groupId>
91+
<artifactId>aspectjweaver</artifactId>
92+
</dependency>
93+
<!-- Spring Test -->
94+
<dependency>
95+
<groupId>org.springframework.boot</groupId>
96+
<artifactId>spring-boot-starter-test</artifactId>
97+
<scope>test</scope>
8398
</dependency>
8499
</dependencies>
85100

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.example.demo;
2+
3+
import javax.sql.DataSource;
4+
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
9+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10+
import org.springframework.security.config.annotation.web.builders.WebSecurity;
11+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
12+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
13+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
14+
import org.springframework.security.crypto.password.PasswordEncoder;
15+
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
16+
17+
18+
@EnableWebSecurity
19+
@Configuration
20+
public class SecurityConfig extends WebSecurityConfigurerAdapter {
21+
22+
// パスワードエンコーダーBeanの定義
23+
@Bean
24+
public PasswordEncoder passwordEncoder() {
25+
return new BCryptPasswordEncoder();
26+
}
27+
28+
// データソース
29+
@Autowired
30+
private DataSource dataSource;
31+
32+
// ユーザーIDとパスワードを取得するSQL
33+
private static final String USER_SQL = "SELECT"
34+
+ " user_id,"
35+
+ " password,"
36+
+ " true"
37+
+ " FROM"
38+
+ " m_user"
39+
+ " WHERE"
40+
+ " user_id = ?";
41+
42+
// ユーザーのロールを取得するSQL
43+
private static final String ROLE_SQL = "SELECT"
44+
+ " user_id,"
45+
+ " role"
46+
+ " FROM"
47+
+ " m_user"
48+
+ " WHERE"
49+
+ " user_id = ?";
50+
51+
@Override
52+
public void configure(WebSecurity web) throws Exception {
53+
// 静的リソースへのアクセスには、セキュリティを適用しない
54+
web.ignoring().antMatchers("/webjars/**", "/css/**");
55+
}
56+
57+
@Override
58+
protected void configure(HttpSecurity http) throws Exception {
59+
// ログイン不要ページの設定
60+
http.authorizeRequests()
61+
.antMatchers("/webjars/**").permitAll() // webjarsへのアクセス許可
62+
.antMatchers("/css/**").permitAll() // cssへのアクセス許可
63+
.antMatchers("/login").permitAll() // ログインページは直リンク許可
64+
.antMatchers("/signup").permitAll() // ユーザー登録画面は直リンク許可
65+
.antMatchers("/admin").hasAuthority("ROLE_ADMIN") // 権限の設定
66+
.anyRequest().authenticated(); // それ以外は直リンク禁止
67+
68+
// ログイン処理
69+
http.formLogin()
70+
.loginProcessingUrl("/login") // ログイン処理のパス
71+
.loginPage("/login") // ログインページの指定
72+
.failureUrl("/login") // ログイン失敗時の遷移先
73+
.usernameParameter("userId") // ログインページのユーザーID
74+
.passwordParameter("password") // ログインページのパスワード
75+
.defaultSuccessUrl("/home", true); // ログイン成功後の遷移先
76+
77+
// ログアウト処理
78+
http.logout()
79+
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
80+
.logoutUrl("/logout")
81+
.logoutSuccessUrl("/login");
82+
83+
// CSRF対策を無効に設定(一時的)
84+
// http.csrf().disable();
85+
}
86+
87+
@Override
88+
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
89+
// ログイン処理時のユーザー情報を、DBから取得
90+
auth.jdbcAuthentication()
91+
.dataSource(dataSource)
92+
.usersByUsernameQuery(USER_SQL)
93+
.authoritiesByUsernameQuery(ROLE_SQL)
94+
.passwordEncoder(passwordEncoder());
95+
}
96+
}

src/main/java/com/example/demo/domain/model/repository/jdbc/UserDaoJdbcImpl.java

+37-20
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,22 @@
88
import org.springframework.beans.factory.annotation.Autowired;
99
import org.springframework.dao.DataAccessException;
1010
import org.springframework.jdbc.core.JdbcTemplate;
11+
import org.springframework.security.crypto.password.PasswordEncoder;
1112
import org.springframework.stereotype.Repository;
1213

1314
import com.example.demo.domain.model.User;
1415
import com.example.demo.domain.model.repository.UserDao;
1516

16-
// TODO: 各ロジックを埋める
17+
1718
@Repository("UserDaoJdbcImpl")
1819
public class UserDaoJdbcImpl implements UserDao {
1920

2021
@Autowired
2122
JdbcTemplate jdbc;
2223

24+
@Autowired
25+
PasswordEncoder passwordEncoder;
26+
2327
// Userテーブルの件数を取得
2428
@Override
2529
public int count() throws DataAccessException {
@@ -32,17 +36,24 @@ public int count() throws DataAccessException {
3236
// Userテーブルにデータを1件insert
3337
@Override
3438
public int insertOne(User user) throws DataAccessException {
39+
40+
// パスワード暗号化
41+
String password = passwordEncoder.encode(user.getPassword());
42+
43+
// ユーザーテーブルに1件登録するSQL
44+
String sql = "INSERT INTO m_user(user_id,"
45+
+ "password,"
46+
+ "user_name,"
47+
+ "birthday,"
48+
+ "age,"
49+
+ "marriage,"
50+
+ "role) "
51+
+ "VALUES(?, ?, ?, ?, ?, ?, ?)";
52+
3553
// 1件登録
36-
int rowNumber =jdbc.update("INSERT INTO m_user(user_id,"
37-
+ "password,"
38-
+ "user_name,"
39-
+ "birthday,"
40-
+ "age,"
41-
+ "marriage,"
42-
+ "role) "
43-
+ "VALUES(?, ?, ?, ?, ?, ?, ?)"
44-
,user.getUserId()
45-
,user.getPassword()
54+
int rowNumber =jdbc.update(sql
55+
,user.getUserId()
56+
,password
4657
,user.getUserName()
4758
,user.getBirthday()
4859
,user.getAge()
@@ -119,16 +130,22 @@ public List<User> selectMany() throws DataAccessException {
119130
@Override
120131
public int updateOne(User user) throws DataAccessException {
121132

133+
// パスワード暗号化
134+
String password = passwordEncoder.encode(user.getPassword());
135+
136+
// 1件更新するSQL
137+
String sql = "UPDATE M_USER"
138+
+ " SET"
139+
+ " password=?,"
140+
+ " user_name=?,"
141+
+ " birthday=?,"
142+
+ " age=?,"
143+
+ " marriage=?"
144+
+ " WHERE user_id=?";
145+
122146
// 1件更新
123-
int rowNumber = jdbc.update("UPDATE M_USER"
124-
+ " SET"
125-
+ " password=?,"
126-
+ " user_name=?,"
127-
+ " birthday=?,"
128-
+ " age=?,"
129-
+ " marriage=?"
130-
+ " WHERE user_id=?"
131-
, user.getPassword()
147+
int rowNumber = jdbc.update(sql
148+
, password
132149
, user.getUserName()
133150
, user.getBirthday()
134151
, user.getAge()

src/main/java/com/example/demo/login/controller/HomeController.java

+10
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ public String getUserList(Model model) {
6969
return "login/homeLayout";
7070
}
7171

72+
// 管理者用画面
73+
@GetMapping("/admin")
74+
public String getAdmin(Model model) {
75+
// コンテンツ部分にユーザー詳細を表示するための文字列を登録
76+
model.addAttribute("contents", "login/admin :: admin_contents");
77+
78+
// レイアウト用テンプレート
79+
return "login/homeLayout";
80+
}
81+
7282
// ユーザー詳細画面
7383
@GetMapping("/userDetail/{id:.+}")
7484
public String getDetail(@ModelAttribute SignupForm form, Model model, @PathVariable("id") String userId) {

src/main/resources/data.sql

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@ VALUES(1, '山田太郎', 30);
44

55
/* ユーザーマスタテーブルのデータ(管理者権限あり) */
66
INSERT INTO m_user(user_id, password, user_name, birthday, age, marriage, role)
7-
VALUES('[email protected]', 'password', 'Kento75', '1993-07-05', 25, false, 'ROLE_ADMIN');
7+
VALUES('[email protected]', '$2a$10$xRTXvpMWly0oGiu65WZlm.3YL95LGVV2ASFjDhe6WF4.Qji1huIPa', 'Kento75', '1993-07-05', 25, false, 'ROLE_ADMIN');
8+
9+
/* ユーザーマスタのデータ(一般権限) */
10+
INSERT INTO m_user(user_id, password, user_name, birthday, age, marriage, role)
11+
VALUES('[email protected]', '$2a$10$xRTXvpMWly0oGiu65WZlm.3YL95LGVV2ASFjDhe6WF4.Qji1huIPa', 'Kento755', '1993-07-05', 25, false, 'ROLE_GENERAL');

src/main/resources/messages.properties

+9
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,12 @@ typeMismatch.signupForm.age=数値を入力してください
2626

2727
# 結婚ステータス
2828
AssertFalse.signupForm.marriage=未婚の方のみ登録できます
29+
30+
# **********************
31+
# ログイン失敗時のメッセージ
32+
# **********************
33+
AbstractUserDetailsAuthenticationProvider.locked=アカウントはロックされています。
34+
AbstractUserDetailsAuthenticationProvider.disabled=アカウントは使用できません。
35+
AbstractUserDetailsAuthenticationProvider.expired=アカウントの有効期限が切れています。
36+
AbstractUserDetailsAuthenticationProvider.credentialsExpired=パスワードの有効期限が切れています。
37+
AbstractUserDetailsAuthenticationProvider.badCredentials=ログインIDまたはパスワードが間違っています。
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
3+
<head>
4+
<meta charset="UTF-8"></meta>
5+
<title>管理者画面</title>
6+
</head>
7+
<body>
8+
<div th:fragment="admin_contents">
9+
<div class="page-header">
10+
<h1>管理者専用画面</h1>
11+
</div>
12+
</div>
13+
</body>
14+
</html>

src/main/resources/templates/login/homeLayout.html

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<!DOCTYPE html>
2-
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
2+
<html xmlns:th="http://www.thymeleaf.org"
3+
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
4+
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
35
<head>
46
<meta charset="UTF-8"></meta>
57
<!-- BootStrap -->
@@ -28,7 +30,10 @@
2830
<div class="col-sm-2 sidebar">
2931
<ul class="nav nav-pills nav-stacked">
3032
<li role="presentation">
31-
<a th:href="@{/userList}">ユーザー管理</a>
33+
<a th:href="@{'/userList'}">ユーザー管理</a>
34+
</li>
35+
<li role="presentation" sec:authorize="hasRole('ADMIN')">
36+
<a th:href="@{'/admin'}">管理者用画面</a>
3237
</li>
3338
</ul>
3439
</div>

src/main/resources/templates/login/login.html

+8-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@
1010
<body class="text-center">
1111
<h1>Login</h1>
1212
<form method="post" action="/login">
13+
<p th:if="${session['SPRING_SECURITY_LAST_EXCEPTION']} != null"
14+
th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}" class="text-danger">
15+
ログインエラーメッセージ
16+
</p>
1317
<label>ユーザーID</label>
14-
<input type="text"/><br/>
18+
<input type="text" name="userId" /><br/>
1519
<br />
1620
<label>パスワード</label>
17-
<input type="password"/><br/>
21+
<input type="password" name="password" /><br/>
1822
<br/>
23+
<!-- CSRF -->
24+
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
1925
<button class="btn btn-primary" type="submit">ログイン</button>
2026
</form>
2127
<br/>

0 commit comments

Comments
 (0)