Visual Studio CodeによるSpring5 MVC Webアプリ開発　Spring Security使用編 2/3【ページ作成編 1/2】

#はじめに
[こちら](https://qiita.com/t_skri/items/10760c93b1788207215f)からの続きです。
今回はログインページ、管理ユーザー作成ページを作成していきます。

##form作成
ログインページ、ユーザー作成ページで使用するformを作成します。

```
D:\JAVA\Project\securitySample\src\main\java\com\example
└─form
  └─AuthForm.java
```

###AuthForm.java

```java:AuthForm.java
package com.example.web.form;

import javax.validation.constraints.Size;

import lombok.Data;

@Data
public class AuthForm {
    private String userId;

    @Size(min=3, max=255)
    private String password;

    private String userNameJP;
    private String sectionNameJP;
    private Boolean enabled;

    private String[] authority;
}
```

##service作成
ユーザー登録に関すビジネスロジックを作成します。

```
D:\JAVA\Project\securitySample\src\main\java\com\example
└─service
  ├─AuthService.java
  └─AuthServiceImpl.java
```

###AuthService.java

```java:AuthService.java
package com.example.service;

import java.util.List;

import com.example.persistence.entity.UserInfo;
import com.example.web.form.AuthForm;

public interface AuthService {
	void insertAdmin(AuthForm authForm);

	void insertUser(AuthForm authForm);

	void updateUser(AuthForm authForm);

	void deleteUser(String id);

	Integer adminCheck();

	List<UserInfo> findUserAll();

	AuthForm userRegById(String id);

}
```

###AuthServiceImpl.java

```java:AuthServiceImpl.java
package com.example.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.example.persistence.entity.UserInfo;
import com.example.persistence.entity.UserRoles;
import com.example.persistence.repository.UserInfoRepository;
import com.example.persistence.repository.UserRolesRepository;
import com.example.web.form.AuthForm;

@Service
public class AuthServiceImpl implements AuthService {
	@Autowired
	UserInfoRepository userInfoRepository;

	@Autowired
	UserRolesRepository userRolesRepository;

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void insertAdmin(AuthForm authForm) {

		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

		UserInfo userInfo = new UserInfo();
		UserRoles userRoles = new UserRoles();
		List<UserRoles> userRokesList  = new  ArrayList<UserRoles>();

		userInfo.setUserId(authForm.getUserId());
		userInfo.setPassword(encoder.encode( authForm.getPassword() ));

		userInfo.setUserNameJP("管理者");
		userInfo.setSectionNameJP("管理者");
		userInfo.setEnabled(true);

		userRoles.setUserId(authForm.getUserId());
		userRoles.setAuthority("ROLE_ADMIN");
		userRokesList.add(userRoles);

		userInfo.setUserRolesList(userRokesList);

		userInfoRepository.insert(userInfo);

		userInfo.getUserRolesList().forEach(s -> {
			userRolesRepository.insert(s);
		});
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void insertUser(AuthForm authForm) {

		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

		UserInfo userInfo = new UserInfo();

		userInfo.setUserId(authForm.getUserId());
		userInfo.setPassword(encoder.encode( authForm.getPassword() ));

		userInfo.setUserNameJP(authForm.getUserNameJP());
		userInfo.setSectionNameJP(authForm.getSectionNameJP());
		userInfo.setEnabled(true);

		userInfoRepository.insert(userInfo);

		List<String> authorityList = new ArrayList<String>(Arrays.asList(authForm.getAuthority()));
		authorityList.add("ROLE_USER");

		authorityList.forEach(s -> {
			UserRoles userRoles = new UserRoles();
			userRoles.setUserId(authForm.getUserId());
			userRoles.setAuthority(s);

			userRolesRepository.insert(userRoles);
		});
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public Integer adminCheck() {
		return userRolesRepository.adminCheck();
	}


	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void updateUser(AuthForm authForm){
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

		UserInfo userInfo = new UserInfo();

		userInfo.setUserId(authForm.getUserId());
		userInfo.setPassword(encoder.encode(authForm.getPassword()));
		userInfo.setUserNameJP(authForm.getUserNameJP());
		userInfo.setSectionNameJP(authForm.getSectionNameJP());
		userInfo.setEnabled(authForm.getEnabled());

		userInfoRepository.update(userInfo);

		userRolesRepository.delete(authForm.getUserId());

		List<String> authorityList = new ArrayList<String>(Arrays.asList(authForm.getAuthority()));
		authorityList.add("ROLE_USER");

		authorityList.forEach(s -> {
			UserRoles userRoles = new UserRoles();
			userRoles.setUserId(authForm.getUserId());
			userRoles.setAuthority(s);

			userRolesRepository.insert(userRoles);
		});
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void deleteUser(String id) {
		userInfoRepository.delete(id);
		userRolesRepository.delete(id);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public List<UserInfo> findUserAll() {
		return userInfoRepository.findUserAll();
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
	public AuthForm userRegById(String id){
		AuthForm authForm = new AuthForm();

		Optional<UserInfo> userInfoOpt = userInfoRepository.selectDetailByUserId(id);

		userInfoOpt.ifPresentOrElse(userInfo -> {
			authForm.setUserId(userInfo.getUserId());
			authForm.setPassword(userInfo.getPassword());
			authForm.setUserNameJP(userInfo.getUserNameJP());
			authForm.setSectionNameJP(userInfo.getSectionNameJP());
			authForm.setEnabled(userInfo.getEnabled());

			String[] arrayAuthority = new String[userInfo.getUserRolesList().size()];

			Integer i = 0;
			for (UserRoles ur : userInfo.getUserRolesList()) {
				arrayAuthority[i] = ur.getAuthority();
				i++;
			}

			authForm.setAuthority(arrayAuthority);
		},null);

		return authForm;
	}
}
```

##controller作成
ログインページ、管理ユーザー作成ページのcontrollerを作成します。

```
D:\JAVA\Project\securitySample\src\main\java\com\example
└─controller
  ├─AuthController.java
  └─AdminRegController.java
```

###AuthController.java

```java:AuthController.java
package com.example.web.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.service.AuthService;

@Controller
@RequestMapping("/auth")
public class AuthController {
	@Autowired
	AuthService authService;

	@GetMapping("/login")
    public String loginGet(Model model) {

		if(authService.adminCheck() <= 0) {
        	return "redirect:/adminReg/index";
        }
		return "auth/login";
    }

	@GetMapping("/403")
    public String index403(Model model) {
		return "auth/403";
    }

	@GetMapping("/login-error")
    public String loginErrorGet(Model model) {
        model.addAttribute("loginError", true);
        return "auth/login";
    }
}
```

###AdminRegController.java

```java:AdminRegController.java
package com.example.web.controller;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.service.AuthService;
import com.example.web.form.AuthForm;

@Controller
@RequestMapping("/adminReg")
public class AdminRegController {
	@Autowired
	AuthService authService;

	@GetMapping("/index")
    public String indexGet(Model model) {
		//ADMINロールユーザーは1つのみ
        if(authService.adminCheck() > 0) {
        	return "redirect:registered";
        }

		model.addAttribute("authForm", new AuthForm());
        return "adminReg/index";
    }

	@PostMapping("/index")
    public String indexPost(Model model,
    		@Valid AuthForm authForm, BindingResult bindingResult, HttpServletRequest request) {
        if (bindingResult.hasErrors()) {
        	model.addAttribute("signupError", true);
            return "adminReg/index";
        }

        try {
        	authService.insertAdmin(authForm);
        } catch (DataIntegrityViolationException e) {
            model.addAttribute("signupError", true);
            return "adminReg/index";
        }
        return "adminReg/done";
    }

	@GetMapping("/registered")
    public String registered(Model model) {
        return "adminReg/registered";
    }
}
```

##RootController.javaの変更
ルートにアクセスがあった際のリダイレクト先を認証画面に設定します。

```java:RootController.java
package com.example.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class RootController {
    @GetMapping("/")
    public String root() {
        return "redirect:auth/login";
    }
}
```

##view作成
ログインページ、管理ユーザー作成ページのviewを作成します。

```
D:\JAVA\Project\securitySample\src\main\webapp\WEB-INF\templates
├─adminReg
| ├─done.html
| ├─index.html
| └─registered.html
├─auth
| ├─403.html
| └─login.html
└─fragment
  └─frag01.html
```
テンプレートフラグメントを使って共通部分を部品化します。

###fragment/frag01.html
```html:frag01.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<!-- HTMLヘッダ -->
<head th:fragment="htmlhead" th:remove="tag">
	<meta charset="UTF-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
</body>
</html>
```

###adminReg/done.html

```html:done.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
	<head th:include="fragment/frag01 :: htmlhead"></head>
	<title>管理者ID登録ページ完了</title>
</head>
<body>
	<h1>管理者ID登録しました。</h1>
	<a th:href="@{/auth/login}">トップ</a>
</body>
</html>
```

###adminReg/index.html

```html:index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
	<head th:insert="fragment/frag01 :: htmlhead"></head>
	<title>管理者ID登録ページ</title>
</head>
<body>
	<h1>管理者ID登録ページ</h1>

	<form method="post" action="#" th:action="@{index}" th:object="${authForm}">
		<div th:if="${signupDone}">
			<em>登録しました。</em>
		</div>

		<div th:if="${signupError}">
			<em>登録出来ませんでした。</em>
		</div>

		<div>
			<label for="userId">ログインID</label>:
			<input type="text" autofocus="autofocus" th:field="*{userId}">
		</div>

		<div>
			<label for="password">パスワード</label>:
			<input type="password" th:field="*{password}">
			<em th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Password Error</em>
		</div>

		<div>
			<button type="submit">登録</button>
		</div>
	</form>
</body>
</html>
```

###adminReg/registered.html

```html:registered.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
	<head th:insert="fragment/frag01 :: htmlhead"></head>
	<title>管理者ID登録ページ</title>
</head>
<body>
	<h1>管理者ID登録済みです。</h1>
	<a th:href="@{/auth/login}">トップ</a>
</body>
</html>
```

###auth/403.html

```html:403.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org" th:with="lang=${#locale.language}" th:lang="${lang}"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
	<head th:insert="fragment/frag01 :: htmlhead"></head>
	<title>Error page</title>
</head>
<body>
	<div>
		<h1>申し訳ございません。エラーが発生しました。</h1>
	</div>
</body>
</html>
```

###auth/login.html

```html:login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
	<head th:insert="fragment/frag01 :: htmlhead"></head>
	<title>ログインページ</title>
</head>
<body>
	<h1>ログインページ</h1>

	<form method="post" action="#" th:action="@{/login}">
		<div th:if="${loginError}">
			<em>ログイン出来ませんでした。ログインID、パスワードを確認して下さい。</em>
		</div>

		<div>
	        <label for="uername">ログインID</label>:
	   		<input type="text" id="username" name="username" autofocus="autofocus">
		</div>

		<div>
	    	<label for="password">パスワード</label>:
			<input type="password" id="password" name="password">
		</div>

		<div>
			<button type="submit">ログイン</button>
		</div>
	</form>
</body>
</html>
```

#まとめ
ここまで作りますと、動作確認が出来るようになります。
コンパイルして動かしてみて下さい。
管理ユーザーの登録が出来る様になります。

次はユーザー作成ページを作成します。
それで完成です。