0 事前に修正
0.1 ユーザテーブルの修正
0.2 その他の修正
※PASSWORD項目により、下記の対象一覧の修正が必要になる
主にPASSWORD項目に対して修正なので、略します
対象一覧 |
---|
UserModel.java |
UserForm.java |
UserMapper.java |
UserService.java |
NewUser.html |
UserList.html |
1 Spring Securityの導入
セッション管理やURL管理の為に導入した
1.1 pom.xmlにSpring Security用のdependencyの追加
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1.2 管理者用ログイン機能追加
1.2.1 管理者用ログイン画面作成
LoginAdmin.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>管理者ログイン画面</title>
</head>
<body>
<h1>管理者ログイン</h1>
<form action="#" th:action="@{/admin/login}" th:object="${loginForm}" method="post">
<table>
<tr>
<th class="cell_title">ID</th>
<td><input type="text" th:field="*{Id}"></td>
</tr>
<tr>
<th class="cell_title">パスワード</th>
<td><input type="password" th:field="*{Password}"></td>
</tr>
<tr><td><input type="hidden" name="Admin" value="Admin"></td></tr>
</table>
<button type="submit">ログイン</button>
<input type="button" value="終了" onclick="window.open('about:blank', '_self').close()">
</form>
</body>
</html>
1.2.2 共通用ログインフォームの作成
LoginForm.java
package com.example.demo.dto;
import javax.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class LoginForm {
@NotBlank
private String Id;
@NotBlank
private String Password;
@NotBlank
private String Admin;
}
1.2.3 管理者用ログイン用のWebSecurityConfigurerAdapter仕組の導入
アクセス制限、ログイン、ログアウト等が設定可能の場所
AdminLoginSecurityConfig.java
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.example.demo.config.loginSecurity.AdminAuthenticationProvider;
import com.example.demo.config.loginSecurity.LoginAuthenticationFilter;
@Configuration
@EnableWebSecurity
@Order(1)
public class AdminLoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
AdminAuthenticationProvider authenticationProvider;
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/images/**", "/css/**", "/javascript/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/admin/**")
.authorizeRequests()
.anyRequest()
.hasRole("ADMIN")
.and()
.logout()
.logoutUrl("/admin/logout").permitAll()
.logoutSuccessUrl("/admin/login");
http.addFilterBefore(createAdminFilter(), LoginAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
private LoginAuthenticationFilter createAdminFilter() throws Exception {
LoginAuthenticationFilter filter = new LoginAuthenticationFilter();
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/login", "POST"));
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/admin/loginFail"));
filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/admin/loginSuccess"));
return filter;
}
}
1.2.4 共通用のUsernamePasswordAuthenticationFilter仕組の導入
ログイン情報を保存する場所
LoginAuthenticationFilter.java
package com.example.demo.config.loginSecurity;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.example.demo.dto.LoginForm;
public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
LoginForm loginInfo = new LoginForm();
loginInfo.setId(request.getParameter("Id"));
loginInfo.setPassword(request.getParameter("Password"));
loginInfo.setAdmin(request.getParameter("Admin"));
String password = obtainPassword(request);
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(loginInfo, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
1.2.5 共通用のAuthenticationProvider仕組の導入
ログイン成功や権限設定の場所
LoginAuthenticationProvider.java
package com.example.demo.config.loginSecurity;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import com.example.demo.dto.LoginForm;
import com.example.demo.service.UserService;
@Configuration
public class LoginAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
LoginForm loginInfo = (LoginForm)auth.getPrincipal();
String password = (String)auth.getCredentials();
Collection<GrantedAuthority> authorityList = getAuthority(loginInfo);
if(authorityList.size() == 0) {
throw new BadCredentialsException("Authentication Error");
}
return new UsernamePasswordAuthenticationToken(loginInfo, password, authorityList);
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
private Collection<GrantedAuthority> getAuthority(LoginForm loginInfo) {
Collection<GrantedAuthority> authorityList = new ArrayList<>();
if(userService.login(loginInfo.getId(), loginInfo.getPassword())) {
if (loginInfo.getAdmin().equals("Admin")) {
authorityList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
} else if (loginInfo.getAdmin().equals("User")) {
authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
}
}
return authorityList;
}
}
1.2.6 ログイン機能のテーブル接続の作成
ユーザサービスクラスに追加
UserService.java
public boolean login(String Id, String Password) {
return dao.login(Id, Password).size() == 1;
}
ユーザマッパークラスに追加
UserMapper.java
@Select("SELECT * FROM USER WHERE ID=#{Id} AND PASSWORD=#{Password}")
List<UserModel> login(String Id, String Password);
1.2.7 ログイン用のコントローラーを作成
LoginController.java
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demo.dto.LoginForm;
@Controller
public class LoginController {
@RequestMapping("/admin/login")
public String loginAdmin(LoginForm loginForm) {
return "LoginAdmin.html";
}
@RequestMapping("/admin/loginSuccess")
public String loginSuccessAdmin(LoginForm loginForm) {
return "redirect:/admin/userlist";
}
}
1.2.8 ユーザコントローラーのURLを修正(アクセス制限により)
UserController.java
package com.example.demo.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demo.dto.LoginForm;
import com.example.demo.dto.UserForm;
import com.example.demo.model.UserModel;
import com.example.demo.service.UserService;
@Controller
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping("/admin/new")
public String addUser(UserForm userForm) {
return "admin/NewUser.html";
}
@PostMapping("/admin/new")
public String create(@Validated @ModelAttribute UserForm userForm, BindingResult errorResult, Model model) {
if (errorResult.hasErrors()) {
return "admin/NewUser.html";
}
try {
userService.insert(userForm);
return "redirect:userlist";
} catch(UncategorizedSQLException e) {
FieldError fieldError = new FieldError(errorResult.getObjectName(), "Id", "ユーザ新規作成に失敗");
errorResult.addError(fieldError);
return "admin/NewUser.html";
}
}
@GetMapping("/admin/userlist")
public String displayUsers(Model model) {
List<UserModel> users = userService.selectAll();
model.addAttribute("users", users);
return "admin/UserList.html";
}
}
1.2.9 ユーザ一覧画面トユーザ新規画面を管理者専用にすることと伴い、ファイルパスの修正
1.3 ユーザ用ログイン機能追加
1.2.1 ユーザ用ログイン画面作成
LoginUser.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>ユーザログイン画面</title>
</head>
<body>
<h1>ユーザログイン</h1>
<form action="#" th:action="@{/user/login}" th:object="${loginForm}" method="post">
<table>
<tr>
<th class="cell_title">ID</th>
<td><input type="text" th:field="*{Id}"></td>
</tr>
<tr>
<th class="cell_title">パスワード</th>
<td><input type="password" th:field="*{Password}"></td>
</tr>
<tr><td><input type="hidden" name="Admin" value="User"></td></tr>
</table>
<button type="submit">ログイン</button>
<input type="button" value="終了" onclick="window.open('about:blank', '_self').close()">
</form>
</body>
</html>
1.3.2 共通用ログインフォームの作成(1.2.2と同様になるので、割合)
1.3.3 ユーザ用ログイン用のWebSecurityConfigurerAdapter仕組の導入
アクセス制限、ログイン、ログアウト等が設定可能の場所
UserLoginSecurityConfig.java
package com.example.demo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.example.demo.config.loginSecurity.LoginAuthenticationFilter;
import com.example.demo.config.loginSecurity.LoginAuthenticationProvider;
@Configuration
@EnableWebSecurity
@Order(2)
public class UserLoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
LoginAuthenticationProvider authenticationProvider;
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/images/**", "/css/**", "/javascript/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/user/**")
.authorizeRequests()
.anyRequest()
.hasRole("USER")
.and()
.logout()
.logoutUrl("/user/logout").permitAll()
.logoutSuccessUrl("/user/login");
http.addFilterBefore(createUserFilter(), LoginAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
private LoginAuthenticationFilter createUserFilter() throws Exception {
LoginAuthenticationFilter filter = new LoginAuthenticationFilter();
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/user/login", "POST"));
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/user/loginFail"));
filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/user/loginSuccess"));
return filter;
}
}
1.3.4 共通用のUsernamePasswordAuthenticationFilter仕組の導入(1.2.4と同様になるので、割合)
1.3.5 共通用のAuthenticationProvider仕組の導入(1.2.5と同様になるので、割合)
1.3.6 ログイン機能のテーブル接続の作成(1.2.6と同様になるので、割合)
1.3.7 ユーザログイン用のコントローラーをログインコントローラーに追加
LoginController.java
@RequestMapping("/user/login")
public String loginUser(LoginForm loginForm) {
return "LoginUser.html";
}
@RequestMapping("/user/loginSuccess")
public String loginSuccessUser(LoginForm loginForm) {
return "redirect:/user/userinfo";
}
1.3.8 ユーザログイン成功後のコントローラーをユーザコントローラーに追加
UserController.java
@GetMapping("/user/userinfo")
public String displayUserInfo(Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
LoginForm loginInfo = (LoginForm)auth.getPrincipal();
UserModel user = userService.selectUser(loginInfo.getId());
model.addAttribute("user", user);
return "user/UserInfo.html";
}
1.3.9 ログインユーザの情報を表示する機能の作成
ユーザ情報画面作成
パス: template/user/UserInfo.html
UserInfo.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>ユーザ情報</title>
</head>
<body>
<h1>ユーザ情報</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>名前</th>
<th>メール</th>
</tr>
</thead>
<tbody>
<tr th:object="${user}">
<td th:text="*{Id}"></td>
<td th:text="*{Name}"></td>
<td th:text="*{Email}"></td>
</tr>
</tbody>
</table>
<form action="#" th:action="@{/user/logout}" method="post">
<button type="submit">ログアウト</button>
</form>
</body>
</html>
ユーザサービスクラスにユーザ情報取得機能の追加
UserService.java
public UserModel selectUser(String Id) {
return dao.selectUser(Id);
}
ユーザマッパークラスにユーザ情報取得機能の追加
UserMapper.java
@Select("SELECT * FROM USER WHERE ID=#{Id}")
UserModel selectUser(String Id);
2 デモ
2.1 管理者
2.1.1 ログイン
2.1.2 ログイン成功(ユーザ一覧表示)
2.1.3 ログアウト
2.2 ユーザ
2.2.1 ログイン
2.2.2 ログイン成功(ユーザ情報表示)
2.2.3 ログアウト
3 終わりに
ここまで、SpringSecurityについて、書きました。
前章と比較しながら参照すればよいと思います。