LoginSignup
3
1

More than 3 years have passed since last update.

Visual Studio CodeによるSpring5 MVC Webアプリ開発 Spring Security使用編 1/3【準備編】

Posted at

はじめに

SQLServer接続編で作成したプロジェクトを拡張していきます。
順番にコピペしていけば、とりあえず動く物は出来上がります。
コピペ中にエラーが発生しない順番にこだわりました。
ですので、このタイミングでこのコードが存在しているの?必要か?と言う部分もあります。
エラーチェック処理は不完全です。あらかじめご了承下さい。

下記の仕様で作成しました。

  • SQLServer接続編で作成したプロジェクトを拡張する。
  • 独自のユーザーテーブルを作成して認証を行う。
  • ロールによる動作の違いを見られるように、管理ユーザーと一般ユーザー2つを作成する。
  • 一般ユーザーを登録出来るのは管理ユーザーのみとする。
  • 管理ユーザーは一つしか作成出来ない。
  • CSS、JavaScriptは使用しない。

環境

OS:Windows 10 Pro 64bit
DB:SQL Server 2019(Cent OS 8 on Hyper-V)
Editor:Visual Studio Code 1.44.2
JDK:AdoptOpenJDK 11.0.6+10 x64
Apache Maven:v3.6.3
Apache Tomcat:v9.0.31

作成手順

1.ユーザー情報を保存するテーブルの作成
2.DBアクセスに必要なentity、repositoryの作成
3.Spring Securityを使用する為の設定
4.管理ユーザー登録ページの作成
5.一般ユーザー登録ページの作成

ユーザー情報を保存するテーブルの作成

こちらで作成したDBにユーザー情報を保存するUserInfoテーブルと、ロールを保存するUserRolesテーブルを作成します。

UserInfo
CREATE TABLE [dbo].[UserInfo](
    [UserId] [varchar](255) NOT NULL,
    [Password] [varchar](255) NOT NULL,
    [UserNameJP] [nvarchar](255) NOT NULL,
    [SectionNameJP] [nvarchar](255) NOT NULL,
    [Enabled] [bit] NOT NULL,
 CONSTRAINT [PK_UserInfo] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY];
UserRoles
CREATE TABLE [dbo].[UserRoles](
    [UserId] [varchar](255) NOT NULL,
    [Authority] [varchar](255) NOT NULL,
 CONSTRAINT [PK_UserRoles] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC,
    [Authority] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY];

プロジェクト作成

SQLServer接続編で作成したプロジェクトを「D:\JAVA\Project\securitySample」にコピーして作成しました。

pom.xml

Spring Securityに必要なRepositoryと、パスワードの文字数チェックに使用するRepositoryを追加します。

<properties>
    <spring-security.version>5.3.0.RELEASE</spring-security.version>
</properties>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>${spring-security.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>${spring-security.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>${spring-security.version}</version>
</dependency>

<dependency>
    <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.1.2.Final</version>
        <scope>compile</scope>
</dependency>

pom.xml全体です。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>securitySample1</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>11</java.version>
        <spring.version>5.2.4.RELEASE</spring.version>
        <spring-security.version>5.3.0.RELEASE</spring-security.version>
        <!-- web.xmlが無い場合でもビルドを実行する -->
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring-security.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring-security.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${spring-security.version}</version>
        </dependency>

        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <version>8.2.1.jre11</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>3.4.2</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.11.RELEASE</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
            <version>3.0.4.RELEASE</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.1.2.Final</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

entity作成

ユーザー情報を格納するentityを作成します。

  • UserInfo.java
  • UserRoles.java
D:\JAVA\Project\securitySample\src\main\java\com\example\persistence
└─entity
  ├─UserInfo.java
  └─UserRoles.java

UserRoles.java

ロール(権限)を格納します。

UserRoles.java
package com.example.persistence.entity;

import lombok.Data;

@Data
public class UserRoles {
    private String userId;
    private String authority;

    public UserRoles() {}

    public UserRoles(String userId, String authority) {
        this.userId = userId;
        this.authority = authority;
    }

}

UserInfo.java

ユーザー情報を格納します。

UserInfo.java
package com.example.persistence.entity;

import java.util.List;

import lombok.Data;

@Data
public class UserInfo {
    private String userId;
    private String password;
    private String userNameJP;
    private String sectionNameJP;
    private Boolean enabled;
    private List<UserRoles> userRolesList;

    public UserInfo() {}

    public UserInfo(String userId, String userNameJP, String sectionNameJP, Boolean enabled) {
        this.userId = userId;
        this.userNameJP = userNameJP;
        this.sectionNameJP = sectionNameJP;
        this.enabled = enabled;
    }
}

repositoryの作成

DBへアクセスするrepositoryを作成します。

  • UserInfoRepository.java
  • UserInfoRepositoryImpl.java
  • UserRolesRepository.java
  • UserRolesRepositoryImpl.java
D:\JAVA\Project\securitySample\src\main\java\com\example\persistence
└─repository
  ├─UserInfoRepository.java
  ├─UserInfoRepositoryImpl.java
  ├─UserRolesRepository.java
  └─UserRolesRepositoryImpl.java

UserInfoRepository.java

UserInfoRepository.java
package com.example.persistence.repository;

import java.util.List;
import java.util.Optional;

import com.example.persistence.entity.UserInfo;

public interface UserInfoRepository {
    Optional<UserInfo> selectDetailByUserId(String id);

    Integer insert(UserInfo userInfo);

    Integer update(UserInfo userInfo);

    Integer delete(String userId);

    List<UserInfo> findUserAll();
}

UserInfoRepositoryImpl.java

UserInfoRepositoryImpl.java
package com.example.persistence.repository;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

import com.example.persistence.entity.UserInfo;
import com.example.persistence.entity.UserRoles;

@Repository
public class UserInfoRepositoryImpl implements UserInfoRepository{
    @Autowired
    NamedParameterJdbcTemplate jdbcTemplate;

    private static final RowMapper<UserInfo> INFO_MAPPER = (rs, rowNum) ->
    new UserInfo(rs.getString("UserId"),
            rs.getString("UserNameJP"),
            rs.getString("SectionNameJP"),
            rs.getBoolean("Enabled")
            );

    private static final RowMapper<UserRoles> ROLES_MAPPER = (rs, rowNum) ->
    new UserRoles(rs.getString("UserId"),
            rs.getString("Authority")
            );

    @Override
    public Optional<UserInfo> selectDetailByUserId(String id) {
        try {

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

            String sql = "SELECT UserInfo.UserId, UserInfo.Password, UserInfo.UserNameJP, UserInfo.SectionNameJP, UserInfo.Enabled"
                    + " , UserRoles.Authority"
                    + " FROM UserInfo INNER JOIN UserRoles ON UserInfo.UserId = UserRoles.UserId "
                    + " WHERE (UserInfo.UserId = :id)";

            MapSqlParameterSource parameterSource = new MapSqlParameterSource();
            parameterSource.addValue("id", id);
            List<Map<String, Object>> resultList = jdbcTemplate.queryForList(sql, parameterSource);

            for(Map<String, Object> result : resultList) {
                UserRoles userRoles = new UserRoles();
                userRoles.setUserId((String) result.get("UserId"));
                userRoles.setAuthority((String) result.get("Authority"));
                userRolesList.add(userRoles);

                userInfo.setUserId((String) result.get("UserId"));
                userInfo.setPassword((String) result.get("Password"));
                userInfo.setUserNameJP((String) result.get("UserNameJP"));
                userInfo.setSectionNameJP((String) result.get("SectionNameJP"));
                userInfo.setEnabled((Boolean) result.get("Enabled"));
            }

            userInfo.setUserRolesList(userRolesList);

            return Optional.of(userInfo);

        }catch (EmptyResultDataAccessException e) {

            return Optional.empty();
        }
    }

    @Override
    public Integer insert(UserInfo userInfo) {
        String sql = "INSERT INTO UserInfo"
                + " VALUES(:UserId, :Password, :UserNameJP, :SectionNameJP, :Enabled);";
        MapSqlParameterSource parameterSource = new MapSqlParameterSource();
        parameterSource.addValue("UserId",          userInfo.getUserId());
        parameterSource.addValue("Password",        userInfo.getPassword());
        parameterSource.addValue("UserNameJP",      userInfo.getUserNameJP());
        parameterSource.addValue("SectionNameJP",   userInfo.getSectionNameJP());
        parameterSource.addValue("Enabled",         userInfo.getEnabled());

        Integer rows = jdbcTemplate.update(sql, parameterSource);
        return rows;
    }

    @Override
    public Integer update(UserInfo userInfo) {
        String sql = "UPDATE UserInfo SET "
                + " Password = :Password "
                + ",UserNameJP = :UserNameJP "
                + ",SectionNameJP = :SectionNameJP "
                + ",Enabled = :Enabled "
                + " WHERE UserId = :UserId;";

        MapSqlParameterSource parameterSource = new MapSqlParameterSource();
        parameterSource.addValue("UserId",          userInfo.getUserId());
        parameterSource.addValue("Password",        userInfo.getPassword());
        parameterSource.addValue("UserNameJP",      userInfo.getUserNameJP());
        parameterSource.addValue("SectionNameJP",   userInfo.getSectionNameJP());
        parameterSource.addValue("Enabled",         userInfo.getEnabled());

        Integer rows = jdbcTemplate.update(sql, parameterSource);
        return rows;
    }

    @Override
    public Integer delete(String userId) {
        String sql = "DELETE FROM UserInfo WHERE UserId = :UserId;";
        MapSqlParameterSource parameterSource = new MapSqlParameterSource();
        parameterSource.addValue("UserId", userId);

        Integer rows = jdbcTemplate.update(sql, parameterSource);
        return rows;
    }

    @Override
    public List<UserInfo> findUserAll() {
        String sql1 = "SELECT DISTINCT UserInfo.UserId, UserInfo.UserNameJP, UserInfo.SectionNameJP, UserInfo.Enabled"
                + " FROM UserInfo INNER JOIN UserRoles ON UserInfo.UserId = UserRoles.UserId "
                + " WHERE UserRoles.Authority = 'ROLE_USER'"
                + " ORDER BY UserInfo.UserId;" ;

        List<UserInfo> userInfoList = jdbcTemplate.query(sql1, INFO_MAPPER);

        userInfoList.forEach(userInfo -> {
            String sql2 = "SELECT * FROM UserRoles WHERE UserId = :UserId;";
            MapSqlParameterSource parameterSource = new MapSqlParameterSource();
            parameterSource.addValue("UserId", userInfo.getUserId());
            List<UserRoles> userRolesList = jdbcTemplate.query(sql2, parameterSource, ROLES_MAPPER);

            userInfo.setUserRolesList(userRolesList);
        });

        return userInfoList;
    }
}

UserRolesRepository.java

UserRolesRepository.java
package com.example.persistence.repository;

import com.example.persistence.entity.UserRoles;

public interface UserRolesRepository {
    Integer insert(UserRoles userRoles);

    Integer update(UserRoles userRoles);

    Integer delete(String userId);

    Integer adminCheck();
}

UserRolesRepositoryImpl.java

UserRolesRepositoryImpl.java
package com.example.persistence.repository;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

import com.example.persistence.entity.UserRoles;

@Repository
public class UserRolesRepositoryImpl implements UserRolesRepository {
    @Autowired
    NamedParameterJdbcTemplate jdbcTemplate;

    @Override
    public Integer insert(UserRoles userRoles) {
        String sql = "INSERT INTO UserRoles"
                + " VALUES(:UserId, :Authority);";
        MapSqlParameterSource parameterSource = new MapSqlParameterSource();
        parameterSource.addValue("UserId", userRoles.getUserId());
        parameterSource.addValue("Authority", userRoles.getAuthority());

        Integer rows = jdbcTemplate.update(sql, parameterSource);
        return rows;
    }

    @Override
    public Integer update(UserRoles userRoles) {
        String sql = "UPDATE UserRoles SET Authority = :Authority"
                + " WHERE UserId = :UserId;";
        MapSqlParameterSource parameterSource = new MapSqlParameterSource();
        parameterSource.addValue("UserId", userRoles.getUserId());
        parameterSource.addValue("Authority", userRoles.getAuthority());

        Integer rows = jdbcTemplate.update(sql, parameterSource);
        return rows;
    }

    @Override
    public Integer delete(String userId) {
        String sql = "DELETE FROM UserRoles WHERE UserId = :UserId;";
        MapSqlParameterSource parameterSource = new MapSqlParameterSource();
        parameterSource.addValue("UserId", userId);

        Integer rows = jdbcTemplate.update(sql, parameterSource);
        return rows;
    }

    @Override
    public Integer adminCheck() {
        Integer rows = 0;

        String sql = "SELECT COUNT(*) AS CNT"
                + " FROM UserRoles"
                + " WHERE Authority = 'ROLE_ADMIN';" ;
        MapSqlParameterSource parameterSource = new MapSqlParameterSource();

        Map<String, Object> result = jdbcTemplate.queryForMap(sql,parameterSource);
        rows = Integer.parseInt(result.get("CNT").toString());

        return rows;
    }
}

security部作成

「D:\JAVA\Project\sqlSample\src\main\java\com\example」に「security」フォルダを作成します。
その直下にconfigフォルダを作成します。
configフォルダに以下のファイルを作成します。

  • SecurityConfig.java
  • SpringSecurityInitializer.java

securityフォルダ直下には以下のファイルを作成します。

  • MyAccessDeniedHandler.java
  • MyAuthenticationSuccessHandler.java
  • MyUserDetails.java
  • MyUserDetailsService.java
D:\JAVA\Project\securitySample\src\main\java\com\example\security
├─config
|  ├─SecurityConfig.java
|  └─SpringSecurityInitializer.java
├─MyAccessDeniedHandler.java
├─MyAuthenticationSuccessHandler.java
├─MyUserDetails.java
└─MyUserDetailsService.java

MyAuthenticationSuccessHandler.java

認証成功時に表示するページを指定します。
ロールごとに分ける事も可能です。

MyAuthenticationSuccessHandler.java
package com.example.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;


public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler  {
    @Override
    public void onAuthenticationSuccess(
        HttpServletRequest request, HttpServletResponse response, Authentication authentication)
                throws IOException, ServletException {

        RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

        //認証後のリダイレクト先
        if (authentication.getAuthorities().toString().contains("ADMIN")) {
            // ADMINロールはユーザー登録ページを表示する。
            redirectStrategy.sendRedirect(request, response, "/userReg/index");
        } else {
            // それ以外のロールの時に表示するページ。
            redirectStrategy.sendRedirect(request, response, "/hello/index");
        }
    }
}

MyAccessDeniedHandler.java

認証処理でエラーが発生した際、表示するページを指定します。

MyAccessDeniedHandler.java
package com.example.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.access.AccessDeniedHandler;

public class MyAccessDeniedHandler implements AccessDeniedHandler{
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);

        RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
        redirectStrategy.sendRedirect(request, response, "/auth/403");
    }
}

MyUserDetails.java

標準の認証情報に追加したい項目を追加します。
ユーザー名の日本語項目と部署名を追加しています。

MyUserDetails.java
package com.example.security;

import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import com.example.persistence.entity.UserInfo;

public class MyUserDetails extends User {
    private static final long serialVersionUID = 1L;
    private final String userNameJP;
    private final String sectionNameJP;

    public MyUserDetails(UserInfo userInfo, List<? extends GrantedAuthority> authorityList) {
        super(userInfo.getUserId(), userInfo.getPassword(), userInfo.getEnabled(), true, true, true, authorityList);
        this.userNameJP = userInfo.getUserNameJP();
        this.sectionNameJP = userInfo.getSectionNameJP();
    }

    public String getUserNameJP() {
        return userNameJP;
    }

    public String getSectionNameJP() {
        return sectionNameJP;
    }
}

MyUserDetailsService.java

MyUserDetailsService.java
package com.example.security;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.example.persistence.entity.UserInfo;
import com.example.persistence.entity.UserRoles;
import com.example.persistence.repository.UserInfoRepository;

public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    UserInfoRepository userInfoRepository;

    @Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        Optional<UserInfo> userInfoOpt = userInfoRepository.selectDetailByUserId(userId);
        if (userInfoOpt == null) {
            throw new UsernameNotFoundException("");
        }

        UserInfo userInfo = userInfoOpt.get();

        List<SimpleGrantedAuthority> authorityList = new ArrayList<SimpleGrantedAuthority>();

        List<UserRoles> roles = userInfo.getUserRolesList();
        for (UserRoles role: roles) {
            authorityList.add(new SimpleGrantedAuthority(role.getAuthority()));
        }

        return new MyUserDetails(userInfo, authorityList);
    }
}

SecurityConfig.java

今回のアプリではBasic認証は使っておりませんが、Form認証と併用する例として、記述してあります。
Basic認証を優先する記述になっています。

SecurityConfig.java
package com.example.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
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.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.crypto.password.PasswordEncoder;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.CharacterEncodingFilter;

import com.example.security.MyAccessDeniedHandler;
import com.example.security.MyAuthenticationSuccessHandler;
import com.example.security.MyUserDetailsService;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService());
    }

    @Bean
    public UserDetailsService myUserDetailsService() {
        return new MyUserDetailsService();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    /**
     * Basic認証
     */
    @Configuration
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class WebSecurityBasicConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable()
                .antMatcher("/api/**")
                .authorizeRequests()
                    .antMatchers("/api/admin/**").hasAnyRole("ADMIN")
                .anyRequest().authenticated()
                    .and()
                    .httpBasic()
            ;
        }
    }

    /**
     * Form認証
     */
    @Configuration
    public class WebSecurityFormConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            filter.setEncoding("UTF-8");
            filter.setForceEncoding(true);

              http
                .addFilterBefore(filter, CsrfFilter.class)
                .authorizeRequests()

                //どのロールでもアクセス可
                .antMatchers("/", "/resources/**", "/webjars/**", "/auth/**", "/adminReg/**").permitAll()

                // ロールにるアクセス設定
                // '/userReg/'で始まるURLには、'ADMIN'ロールのみアクセス可
                .antMatchers("/userReg/**").hasRole("ADMIN")

                .anyRequest().authenticated()
                .and()
                    .formLogin()
                    .loginProcessingUrl("/login")
                    .loginPage("/auth/login")
                    .failureUrl("/auth/login-error")
                    .successHandler(new MyAuthenticationSuccessHandler())
                    .permitAll()
                .and()
                // ログアウト処理の設定
                    .logout()
                    // ログアウト処理のURL
                    .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                    // ログアウト成功時の遷移先URL
                    .logoutSuccessUrl("/auth/login")
                    // ログアウト時に削除するクッキー名
                    .deleteCookies("JSESSIONID")
                    // ログアウト時のセッション破棄を有効化
                    .invalidateHttpSession(true)
                    .permitAll()
                .and()
                //403エラー時の遷移先設定
                    .exceptionHandling()
                    .accessDeniedHandler(new MyAccessDeniedHandler())

                ;
        }

    }
}

SpringSecurityInitializer.java

SpringSecurityInitializer.java
package com.example.security.config;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {

}

WebAppInitializer.java追記

WebAppInitializerのgetRootConfigClassesにSecurityConfig.classを追加します。

protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] {SecurityConfig.class, PersistenceConfig.class, ServiceConfig.class};
}

WebAppInitializer.java全体です。

WebAppInitializer.java
package com.example.web.config;

import java.nio.charset.StandardCharsets;

import javax.servlet.Filter;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import com.example.persistence.config.PersistenceConfig;
import com.example.security.config.SecurityConfig;
import com.example.service.config.ServiceConfig;

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     * ビジネスロジックなど、Spring MVC以外に関するJava Configクラスを指定します。
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {SecurityConfig.class, PersistenceConfig.class, ServiceConfig.class};
    }

    /**
     * Spring MVCに関するJava Configクラスを指定します。
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {MvcConfig.class};
    }

    /**
     * DispatcherServletに対するURLパターンを指定します。
     * "/"を指定することで、全リクエストをDispatcherServletが受け取ります。
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    /**
     * サーブレットフィルターを指定します。
     * 複数のフィルターがあった場合、配列に指定した順番に実行されます。
     */
    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] {
                new CharacterEncodingFilter(StandardCharsets.UTF_8.name(), true)
            };
    }
}

まとめ

以上でSpring Securityを使う上で必要な準備が終わりました。
次はログインページ、管理ユーザー作成ページを作っていきます。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1