SpringブートThymeleafの例では、Spring Securityを使用してパス
/admin`と
/user`を保護します
使用される技術:
-
春のブート1.5.3.RELEASE
-
Spring 4.3.8.RELEASE
-
春のセキュリティ4.2.2
-
タイメレフ2.1.5.リリース
-
Thymeleaf extras Spring Security4 2.1.3
-
Tomcat埋め込み8.5.14
-
Maven 3
-
Java 8
1.プロジェクトディレクトリ
プロジェクトの依存関係
spring-boot-starter-security`を宣言すると、
SpringBoot + Spring Security` Webアプリケーションを開発するために必要なものがすべて得られます。
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>
<artifactId>spring-boot-web-spring-security</artifactId>
<packaging>jar</packaging>
<name>Spring Boot Web Spring Security</name>
<description>Spring Boot Web Spring Security Example</description>
<url>/</url>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- do you like thymeleaf? -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- optional, it brings userful tags to display spring security stuff -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<!-- hot swapping, disable cache for template, enable live reload -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- Optional, for bootstrap -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Package as an executable jar/war -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
プロジェクトの依存関係を表示する:
$ mvn dependency:tree [INFO]Scanning for projects...[INFO][INFO]------------------------------------------------------------------------[INFO]Building Spring Boot Web Spring Security 1.0[INFO]------------------------------------------------------------------------[INFO][INFO]--- maven-dependency-plugin:2.10:tree (default-cli) @ spring-boot-web-spring-security ---[INFO]org.springframework.boot:spring-boot-web-spring-security:jar:1.0[INFO]+- org.springframework.boot:spring-boot-starter-thymeleaf:jar:1.5.3.RELEASE:compile[INFO]| +- org.springframework.boot:spring-boot-starter:jar:1.5.3.RELEASE:compile[INFO]| | +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.3.RELEASE:compile[INFO]| | | +- ch.qos.logback:logback-classic:jar:1.1.11:compile[INFO]| | | | \- ch.qos.logback:logback-core:jar:1.1.11:compile[INFO]| | | +- org.slf4j:jcl-over-slf4j:jar:1.7.25:compile[INFO]| | | +- org.slf4j:jul-to-slf4j:jar:1.7.25:compile[INFO]| | | \- org.slf4j:log4j-over-slf4j:jar:1.7.25:compile[INFO]| | +- org.springframework:spring-core:jar:4.3.8.RELEASE:compile[INFO]| | \- org.yaml:snakeyaml:jar:1.17:runtime[INFO]| +- org.springframework.boot:spring-boot-starter-web:jar:1.5.3.RELEASE:compile[INFO]| | +- org.springframework.boot:spring-boot-starter-tomcat:jar:1.5.3.RELEASE:compile[INFO]| | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.14:compile[INFO]| | | +- org.apache.tomcat.embed:tomcat-embed-el:jar:8.5.14:compile[INFO]| | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.5.14:compile[INFO]| | +- org.hibernate:hibernate-validator:jar:5.3.5.Final:compile[INFO]| | | +- javax.validation:validation-api:jar:1.1.0.Final:compile[INFO]| | | +- org.jboss.logging:jboss-logging:jar:3.3.1.Final:compile[INFO]| | | \- com.fasterxml:classmate:jar:1.3.3:compile[INFO]| | +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.8:compile[INFO]| | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.0:compile[INFO]| | | \- com.fasterxml.jackson.core:jackson-core:jar:2.8.8:compile[INFO]| | +- org.springframework:spring-web:jar:4.3.8.RELEASE:compile[INFO]| | \- org.springframework:spring-webmvc:jar:4.3.8.RELEASE:compile[INFO]| +- org.thymeleaf:thymeleaf-spring4:jar:2.1.5.RELEASE:compile[INFO]| | \- org.thymeleaf:thymeleaf:jar:2.1.5.RELEASE:compile[INFO]| | +- ognl:ognl:jar:3.0.8:compile[INFO]| | +- org.javassist:javassist:jar:3.21.0-GA:compile[INFO]| | \- org.unbescape:unbescape:jar:1.1.0.RELEASE:compile[INFO]| \- nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:jar:1.4.0:compile[INFO]| \- org.codehaus.groovy:groovy:jar:2.4.10:compile[INFO]+- org.springframework.boot:spring-boot-starter-security:jar:1.5.3.RELEASE:compile[INFO]| +- org.springframework:spring-aop:jar:4.3.8.RELEASE:compile[INFO]| | \- org.springframework:spring-beans:jar:4.3.8.RELEASE:compile[INFO]| +- org.springframework.security:spring-security-config:jar:4.2.2.RELEASE:compile[INFO]| | +- org.springframework.security:spring-security-core:jar:4.2.2.RELEASE:compile[INFO]| | \- org.springframework:spring-context:jar:4.3.8.RELEASE:compile[INFO]| \- org.springframework.security:spring-security-web:jar:4.2.2.RELEASE:compile[INFO]| \- org.springframework:spring-expression:jar:4.3.8.RELEASE:compile[INFO]+- org.thymeleaf.extras:thymeleaf-extras-springsecurity4:jar:2.1.3.RELEASE:compile[INFO]| \- org.slf4j:slf4j-api:jar:1.7.25:compile[INFO]+- org.springframework.boot:spring-boot-devtools:jar:1.5.3.RELEASE:compile[INFO]| +- org.springframework.boot:spring-boot:jar:1.5.3.RELEASE:compile[INFO]| \- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.3.RELEASE:compile[INFO]\- org.webjars:bootstrap:jar:3.3.7:compile[INFO] \- org.webjars:jquery:jar:1.11.1:compile[INFO]------------------------------------------------------------------------[INFO]BUILD SUCCESS[INFO]------------------------------------------------------------------------[INFO]Total time: 2.072 s[INFO]Finished at: 2017-05-04T10:13:05+08:00[INFO]Final Memory: 19M/309M[INFO]------------------------------------------------------------------------
3.春のセキュリティ
3.1
WebSecurityConfigurerAdapter`を拡張し、
configure`メソッドでセキュリティルールを定義しました。
SpringSecurityConfig.java
package com.mkyong.config;
import org.springframework.beans.factory.annotation.Autowired;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.AccessDeniedHandler;
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AccessDeniedHandler accessDeniedHandler;
//roles admin allow to access/admin/** **
//roles user allow to access/user/** **
//custom 403 access denied handler
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/about").permitAll()
.antMatchers("/admin/** ** ").hasAnyRole("ADMIN")
.antMatchers("/user/** ** ").hasAnyRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
//create two users, admin and user
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("password").roles("ADMIN");
}
}
3.2カスタム403アクセス拒否ハンドラは、要求を記録し、 `/403`にリダイレクトします。
WelcomeController.java
package com.mkyong.error;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//handle 403 page
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
private static Logger logger = LoggerFactory.getLogger(MyAccessDeniedHandler.class);
@Override
public void handle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AccessDeniedException e) throws IOException, ServletException {
Authentication auth
= SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
logger.info("User '" + auth.getName()
+ "' attempted to access the protected URL: "
+ httpServletRequest.getRequestURI());
}
httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/403");
}
}
4.春のブート
4.1コントローラクラス。httpリクエストとビュー名を定義する。
DefaultController.java
package com.mkyong.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class DefaultController {
@GetMapping("/")
public String home1() {
return "/home";
}
@GetMapping("/home")
public String home() {
return "/home";
}
@GetMapping("/admin")
public String admin() {
return "/admin";
}
@GetMapping("/user")
public String user() {
return "/user";
}
@GetMapping("/about")
public String about() {
return "/about";
}
@GetMapping("/login")
public String login() {
return "/login";
}
@GetMapping("/403")
public String error403() {
return "/error/403";
}
}
4.2 Spring起動アプリケーションを起動します。
DefaultController.java
package com.mkyong;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootWebApplication {
public static void main(String[]args) throws Exception {
SpringApplication.run(SpringBootWebApplication.class, args);
}
}
5. Thymeleafリソース静的ファイル
5.1 Thymeleafファイルの場合、 `src/main/resources/templates/`フォルダに入れます。
5.2テンプレートレイアウトのためのThymeleafフラグメント – ヘッダ。
src/main/resources/templates/fragments/header.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:fragment="header-css">
<!-- this is header-css -->
<link rel="stylesheet" type="text/css"
href="webjars/bootstrap/3.3.7/css/bootstrap.min.css"/>
<link rel="stylesheet" th:href="@{/css/main.css}"
href="../../css/main.css"/>
</div>
</head>
<body>
<div th:fragment="header">
<!-- this is header -->
<nav class="navbar navbar-inverse">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" th:href="@{/}">Spring Boot</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a th:href="@{/}">Home</a></li>
</ul>
</div>
</div>
</nav>
</div>
</body>
</html>
5.3テンプレートレイアウトのためのThymeleafフラグメント – フッター。 `sec`タグを確認してください。これはSpring Securityの情報を表示するのに便利なタグです。詳しくは、https://github.com/thymeleaf/thymeleaf-extras-springsecurity[Thymeleaf extra Spring Security]を参照してください。
src/main/resources/templates/fragments/footer.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
</head>
<body>
<div th:fragment="footer">
<div class="container">
<footer>
<!-- this is footer -->
© 2017 mkyong.com
<span sec:authorize="isAuthenticated()">
| Logged user: <span sec:authentication="name"></span> |
Roles: <span sec:authentication="principal.authorities"></span> |
<a th:href="@{/logout}">Sign Out</a>
</span>
<script type="text/javascript"
src="webjars/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</footer>
</div>
</div>
</body>
</html>
5.4 Thymeleafファイルのリスト。何も特別なものではなく、自明です。
ホーム〜
src/main/resources/templates/home.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Thymeleaf + Spring Security</title>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>
<div th:replace="fragments/header :: header"/>
<div class="container">
<div class="starter-template">
<h1>Spring Boot Web Thymeleaf + Spring Security</h1>
<h2>1. Visit <a th:href="@{/admin}">Admin page (Spring Security protected, Need Admin Role)</a></h2>
<h2>2. Visit <a th:href="@{/user}">User page (Spring Security protected, Need User Role)</a></h2>
<h2>3. Visit <a th:href="@{/about}">Normal page</a></h2>
</div>
</div>
<!--/.container -->
<div th:replace="fragments/footer :: footer"/>
</body>
</html>
admin〜
src/main/resources/templates/admin.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>
<div th:replace="fragments/header :: header"/>
<div class="container">
<div class="starter-template">
<h1>Admin page (Spring Security protected)</h1>
<h1 th:inline="text">Hello[[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</div>
</div>
<!--/.container -->
<div th:replace="fragments/footer :: footer"/>
</body>
</html>
ユーザー〜
src/main/resources/templates/user.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>
<div th:replace="fragments/header :: header"/>
<div class="container">
<div class="starter-template">
<h1>User page (Spring Security protected)</h1>
<h1 th:inline="text">Hello[[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</div>
</div>
<!--/.container -->
<div th:replace="fragments/footer :: footer"/>
</body>
</html>
約〜
src/main/resources/templates/about.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>
<div th:replace="fragments/header :: header"/>
<div class="container">
<div class="starter-template">
<h1>Normal page (No need login)</h1>
</div>
</div>
<!--/.container -->
<div th:replace="fragments/footer :: footer"/>
</body>
</html>
ログイン〜
src/main/resources/templates/login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
>
<head>
<title>Spring Security Example </title>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>
<div th:replace="fragments/header :: header"/>
<div class="container">
<div class="row" style="margin-top:20px">
<div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
<form th:action="@{/login}" method="post">
<fieldset>
<h1>Please Sign In</h1>
<div th:if="${param.error}">
<div class="alert alert-danger">
Invalid username and password.
</div>
</div>
<div th:if="${param.logout}">
<div class="alert alert-info">
You have been logged out.
</div>
</div>
<div class="form-group">
<input type="text" name="username" id="username" class="form-control input-lg"
placeholder="UserName" required="true" autofocus="true"/>
</div>
<div class="form-group">
<input type="password" name="password" id="password" class="form-control input-lg"
placeholder="Password" required="true"/>
</div>
<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<input type="submit" class="btn btn-lg btn-primary btn-block" value="Sign In"/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
<div th:replace="fragments/footer :: footer"/>
</body>
</html>
403〜
src/main/resources/templates/error/403.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>
<div th:replace="fragments/header :: header"/>
<div class="container">
<div class="starter-template">
<h1>403 - Access is denied</h1>
<div th:inline="text">Hello '[[${#httpServletRequest.remoteUser}]]',
you do not have permission to access this page.</div>
</div>
</div>
<!--/.container -->
<div th:replace="fragments/footer :: footer"/>
</body>
</html>
5.5 CSSやJavascriptのような静的ファイルの場合、 `/src/main/resources/static/`に入れます。
/src/main/resources/static/css/main.css
h1{
color:#0000FF;
}
h2{
color:#FF0000;
}
footer{
margin-top:60px;
}
-
注** +これを理解するには、http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-static-content[Spring Boot static static content]をお読みください。リソースマッピング。
6.デモ
6.1 Spring起動Webアプリケーションを起動します。この `/admin/
`は保護されており、アクセスするにはadminとしてログインする必要があります。
ターミナル
$ mvn spring-boot:run //...
6.2 `http://localhost:8080`にアクセスします。
リンク://wp-content/uploads/2017/05/spring-boot-spring-security-1.png[
]
6.4不正なユーザ名またはパスワード
http://localhost:8080/login
リンク://wp-content/uploads/2017/05/spring-boot-spring-security-3.png[
]
6.5ログインに成功し、管理ページ `http://localhost:8080/admin`にリダイレクトし、フッターセクションを見直すと、ユーザー情報が表示されます。
リンク://wp-content/uploads/2017/05/spring-boot-spring-security-4.png[
]
リンク://wp-content/uploads/2017/05/spring-boot-spring-security-5.png[
]
6.7フッター内のサインアウトリンクをクリックし、 `http://localhost:8080/login?logout`にリダイレクトします。
完了しました。別のユーザ名 “user”でログインし、管理ページにアクセスしてください。
ソースコードをダウンロードする
それをダウンロードする –
spring-boot-spring-security-thymeleaf.zip
(15 KB)
参考文献
セキュリティリファレンス]。
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-security
[Spring
ブートセキュリティ機能]。リンク://spring-boot/spring-boot-hello-world-example-thymeleaf/[Spring
ブートHello Worldの例 – Thymeleaf]。リンク://spring-security/spring-security-hello-world-annotation-example/[Spring
セキュリティHello Worldの注釈の例]。
http://www.thymeleaf.org/doc/articles/springsecurity.html
[Thymeleaf –
Spring Securityの統合の基礎]。
https://github.com/thymeleaf/thymeleaf-extras-springsecurity
[Thymeleaf
特別なSpring Securityの統合の基礎]。
http://www.thymeleaf.org/doc/articles/standardurlsyntax.html
[Thymeleaf
Boot + Spring MVC + Springセキュリティ+ MySQL]。
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-static-content
[Spring
ブート – 静的コンテンツ]。リンク://spring-mvc/spring-mvc-how-to-include-js-or-css-files-in-a-jsp-page/[Spring
MVC – CSSファイルを含める]
リンク://タグ/カスタムログイン/[カスタムログイン]リンク://タグ/セキュリティ/[セキュリティ]
spring boot
spring
セキュリティ
リンク://タグ/テンプレート/[テンプレート]リンク://tag/thymeleaf/[thymeleaf]