#sidebar.htmlが読み込まれていない原因
SecurityConfig.javaのセキュリティ設定を無視(ignoring)するパスの指定する際の構文が誤っていたためsidebar.htmlが読み込まれていなかったそのためログイン後の画面が表示されない
誤り
@Override
public void configure(WebSecurity web) throws Exception{
web.ignoring().antMatchers("css/***,*/webjars/***");
}
修正
@Override
public void configure(WebSecurity web) throws Exception{
web.ignoring().antMatchers("css/***","/webjars/***");
}
#結果
1.ログイン後の画面の確認
ブラウザでhttp/localhost:8080/にアクセス後表示された画面でユーザ名「user」
パスワード「password」を入力しログイン後の画面を確認
***2.Remember Meの機能が正しく機能しているか確認
2-1ログイン画面でユーザ名とパスワード入力後「ログインしたままにする」にチェックをつけてログインする。
2-2 ブラウザをいったん閉じ再度http:loclahost:8080/にアクセスしログイン後の画面が表示されていることを確認
#DBのユーザでログインする。
以下のことを実施します。
※カスタムしたログインページを表示のプログラムを使用します。
・DBにユーザを登録する
・DBのユーザでログイン
#ユーティリティの作成
・列挙型を作成
package com.example.demo.util;
public enum Role {
ADMIN,USER
}
・クラス型を作成
Collections.unmodifiableMap()はMAPを定数として利用が可能
package com.example.demo.util;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
public class Type {
public static final Map<Integer,String>GENDERS;
static {
Map<Integer,String>genders = new LinkedHashMap<>();
genders.put(0, "選択しない");
genders.put(1, "男性");
genders.put(2,"女性");
genders.put(3, "その他");
GENDERS = Collections.unmodifiableMap(genders);
}
}
#モデルの作成
package com.example.demo.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class SiteUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Size(min = 2, max = 20)
private String username;
@Size(min =4,max = 255)
private String password;
@NotBlank
@Email
private String email;
private int gender;
private boolean admin;
private String role;
private boolean active = true;
}
#H2 Databaseの設定
application.propertiesを編集
spring.datasource.url=jdbc:h2:./textdb
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:h2:保存先
DBをローカルに保存
Spring.jpa.hibernate.ddl-auto=update
@Entityのテーブルがない場合、作成する。
(Spring Bootアプリケーションを終了してもテーブルは削除されない)
#リポジトリ
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.model.SiteUser;
public interface SiteUserRepository extends JpaRepository<SiteUser,Long> {
SiteUser findByUsername(String username);
boolean existsByUsername(String username);
}
#サービスを作成
Spring Securityはユーザデータを扱うためにインターフェースを認証に利用する。
@Service
クラスがサービスそうであることを示す。
UserDetailsServiceインターフェースはユーザを特定するために使用される。
このインターフェースには実装が必要なloadUserByUsername()というメソッドがある。
package com.example.demo.service;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.demo.model.SiteUser;
import com.example.demo.repository.SiteUserRepository;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class UserDetailsServiceImpl implements UserDetailsService{
private final SiteUserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException{
SiteUser user = userRepository.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException(username + "not found");
}
return createUserDetails(user);
}
public User createUserDetails(SiteUser user) {
Set<GrantedAuthority> grantedAuthories = new HashSet<>();
grantedAuthories.add(
new SimpleGrantedAuthority("ROLE_" + user.getRole()));
return new User(user.getUsername(),user.getPassword(),grantedAuthories);
}
}
#SecurityConfigとSecurityControllerの編集
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import com.example.demo.util.Role;
import lombok.RequiredArgsConstructor;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception{
web.ignoring().antMatchers("/js/**","css/***","/webjars/***");
}
@Override
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/login","/register").permitAll()
.antMatchers("/admin/**").hasRole(Role.ADMIN.name())
.anyRequest().authenticated().and()
.formLogin().loginPage("/login").defaultSuccessUrl("/").and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).and()
.rememberMe();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
package com.example.demo.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
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 com.example.demo.model.SiteUser;
import com.example.demo.repository.SiteUserRepository;
import com.example.demo.util.Role;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Controller
public class SecurityController {
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/")
public String showList(Authentication loginUser,Model model) {
model.addAttribute("username",loginUser.getName());
model.addAttribute("role",loginUser.getAuthorities());
return"user";
}
@GetMapping("/admin/list")
public String showAdminList(Authentication loginUser,Model model) {
model.addAttribute("users",userRepository.findAll());
return "user";
}
@GetMapping("/register")
public String register(@ModelAttribute("user") SiteUser user) {
return "register";
}
@PostMapping("/register")
public String process(@Validated @ModelAttribute("user") SiteUser user,BindingResult reult) {
if(result.hasErrors()) {
return "register";
}else {
user.setRole(Role.USER.name());
}
userRepository.seve(user);
return"redirect:/login?register";
}
}
#pom.xml編集
thymeleaf-extras-springsecurity5
ThymeleafとSpring Security の結合モジュール
Thymeleafで、現在のユーザ権限などを取得できる
datatables-buttons、datatables-plugins、jszip
DataTablesのボタン出力、日本語化で必要なモジュール
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>datatables-buttons</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>datatables-plugins</artifactId>
<version>1.10.20</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jszip</artifactId>
<version>3.1.0</version>
</dependency>
#thymeleafテンプレートを作成
login.htmlを編集
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<th:block th:insert="fragments/base :: header"></th:block>
</head>
<body class="bg-gradient-primary">
<div class="container">
<div class="row justify-content-center">
<div class="col-xl-10 col-lg-12 col-md-9">
<div class="card o-hidden border-0 shadow-lg my-5">
<div class="card-body p-0">
<div class="row">
<div class="col-lg-6 d-none d-lg-block bg-login-image"></div>
<div class="col-lg-6">
<div class="p-5">
<div class="text-center">
<h1 class="h4 text-gray-900 mb-4">ユーザ管理システム</h1>
</div>
<div class="alert alert-danger" th:if="${param.error}">
<small>ユーザ名またはパスワードが正しくありません。</small>
</div>
<div class="alert alert-success" th:if="${param.register}">
<small>ユーザを新規登録しました</small>
</div>
<div class="alert alert-success" th:if="${param.logout}">
<small>ログアウトしました</small>
</div>
<form th:action="@{/login}" class="user" method="post">
<div class="form-group">
<input type="text"class="form-control form-control-user" name="username"placeholder="ユーザ名">
</div>
<div class="form-group">
<input type="password" class="form-control form-control-user" name="password" placeholder="パスワード">
</div>
<div class="form-group">
<div class="custom-control custom-checkbox small">
<input type="checkbox" class="custom-control-input"id="remember-me"name="remember-me">
<label class="custom-control-label"for="remember-me">ログインしたままにする</label>
</div>
</div>
<button class="btn btn-primary btn-user btn-block">ログイン</button>
<div class="text-center">
<a th:href="@{/register}">新規登録はこちら</a>
</div>
</form>
<hr>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<th:block th:insert="fragments/base :: scripts"></th:block>
</body>
</html>
#以上