Ich fühle mich wie spät, aber ich habe angefangen, Spring Boot zu studieren. Die meisten Unternehmen verwendeten ihre eigenen Frameworks und Struts2-Systeme, aber die Sicherheitslücken, die heutzutage Lärm gemacht haben, und der Trend zu Microservices, und als ich im Frühjahr an JJUG CCC 2017 Spring teilnahm, überall Spring Boot Ich habe mich für Spring Boot entschieden, weil es viele Geschichten darüber gibt und es viele Informationen und Bücher im Internet gibt.
Als Gefühl, es zu benutzen, erkannte ich, dass es praktisch war, weil ich sofort anfangen und einfach Funktionen hinzufügen konnte. Wenn Sie das Buch lesen, das offizielle Dokument lesen und die Informationen im Internet nachschlagen, wird ein einfaches System in kürzester Zeit fertiggestellt. Es gab jedoch einige süchtig machende Punkte ... Es ist in Ordnung, die Funktionen entsprechend zu verschieben, aber ich habe beschlossen, ein einfaches Beispielsystem zu erstellen und es zu studieren.
Die erstellte Quelle wird auf github veröffentlicht, daher hoffe ich, dass sie für diejenigen hilfreich ist, die von nun an beginnen. https://github.com/ewai/spring-boot-mvc-template
Wir planen, es von Zeit zu Zeit zu erstellen und zu aktualisieren.
System zur Verwaltung von Buchinformationen (System, das eine einfache Masterverwaltung durchführt)
Sie müssen sich anmelden, um mit den Daten arbeiten zu können Nur der Administrator kann sich registrieren / aktualisieren. ← Die TODO-Berechtigung funktioniert nicht richtig
Außer dem oberen Bildschirm kann es nur angezeigt werden, wenn es authentifiziert wurde.
https://ewai.info/sbt/
Nutzer | Passwort | Behörde |
---|---|---|
sbt | sbt | Normale Benutzerberechtigung (nur Referenzsystem) |
admin | admin | Administratorrechte (Daten können aktualisiert werden) |
src
├─main
│ ├─java
│ │ └─info
│ │ └─ewai
│ │ └─sbmt
│ │ ├concon (Einstellungen rund um Sicherheit usw.)
│ │ ├─domain (entity,Repository usw.
│ │ ├─service (service
│ │ └─web (controller, validator
│ │ └─form (form
│ └─resources
│ ├─static
│ │ ├─css
│ │ └─img
│ └ Vorlagen (Thymeleaf-Vorlagen
└─test
└─java
└─info
└─ewai
└─sbt (TODO Junit
Es hat eine Standardpaketkonfiguration. Es scheint, dass Sie @ComponentScan ("xxx") angeben müssen, wenn es nicht dem Standard entspricht. Ich war süchtig danach, es frei zu machen, ohne es zuerst zu wissen.
Offizielle Dokumentation http://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/htmlsingle/#using-boot-using-the-default-package
build.gradle
~
springBootVersion = '1.5.6.RELEASE'
~
compile("org.webjars:jquery:3.2.1")
compile("org.webjars:bootstrap:3.3.7")
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-jetty')
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.2.RELEASE')
runtime('mysql:mysql-connector-java:5.1.43')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')
Github-Quelle build.gradle
Standardmäßig wird Tomcat verwendet, aber Jetty wird verwendet, da das Unternehmen Jetty verwendet.
Bibliothek
Anwendungsserver
DB
Basierend auf dem offiziellen MySQL von Docker Hub habe ich eine Docker-Datei erstellt, die DDL- und Testdaten automatisch registriert. Wenn Sie von dort aus Docker erstellen und ein Image erstellen, enthält es DDL- und Testdaten. Seit dem Start von MySQL können Sie das System sofort starten.
Das Folgende ist ein ziemlich grobes Konstruktionsverfahren.
Erstes Mal
#Klonen oder herunterladen
https://github.com/ewai/docker-spring-boot-template-mysql
Führen Sie unten den Befehl aus
#Bilderzeugung
docker build -t sbtdb .
#Containererstellung
docker run -d --name sbtdb -p 3306:3306 sbtdb
Es sollte jetzt laufen.
docker ps -a
OK, wenn der Status UP ist.
* DDL- und Testdaten wurden bereits eingegeben.
Nach dem zweiten Mal
Überprüfen Sie den Status
docker ps -a
Starten Sie den Container, wenn der Status Beendet lautet
docker start sbtdb
Wenn der Status UP lautet, können Sie eine Verbindung herstellen. Versuchen Sie daher, eine Verbindung mit MySQL Workbench herzustellen. sbtdb
Verbindungsinformationen
jdbc:mysql://localhost/sbtdb
Benutzer: sbt
Passwort: sbt
Beispiel) MySQL Workbench
build.gradle
+ compile('org.springframework.boot:spring-boot-starter-security')
Aktiviert die Federsicherheit.
Fügen Sie dies einfach hinzu und die Standardauthentifizierung wird automatisch angewendet. Ich habe diesen Teil gemacht, weil ich den Login authentifizieren möchte.
SecurityConfig.java
package info.ewai.sbmt.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.authentication.configurers.GlobalAuthenticationConfigurerAdapter;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import info.ewai.sbmt.service.UserService;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/img/**", "/css/**", "/js/**", "/webjars/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("username")
.passwordParameter("password").permitAll().and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true).permitAll();
}
@Configuration
protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Autowired
UserService userService;
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
}
}
SimpleController.java
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login() {
return "login";
}
Es ist ein Controller, der nur zur Anmeldeseite übergeht. Ich glaube, ich habe festgestellt, dass so etwas nur durch Einstellen möglich ist, aber ich habe es vergessen Ich habe sie einmal in SimpleController.java gesammelt.
UserService.java
@Component
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (StringUtils.isEmpty(username)) {
throw new UsernameNotFoundException("Username is empty");
}
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found for name: " + username);
}
return user;
}
}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
public User findByUsername(String username);
}
Andere info.ewai.sbmt.domain.User info.ewai.sbmt.domain.Authorities login.html
Ich habe UserDetails implementiert und es fast zum Standard gemacht. Möglicherweise kann es verwendet werden, indem die Benutzerinformationen des internen Systems konvertiert und synchronisiert werden.
Dateneingabe standardmäßig
Nutzer | Passwort | Behörde | Berechtigungsinhalt |
---|---|---|---|
sbt | sbt | ROLE_USER | Normale Benutzerberechtigung (nur Referenzsystem) |
admin | admin | ROLE_ADMIN | Administratorrechte (Daten können aktualisiert werden) |
admin | admin | ACTUATOR | Berechtigung zur Verwendung des Spring Boot Actuator |
BookController.java
@RequestMapping(value = "/book", method = RequestMethod.GET)
public String index(Model model) {
List<Book> list = this.bookservice.findAll();
model.addAttribute("booklist", list);
model.addAttribute("bookForm", new BookForm());
return "book";
}
BookService.java
public List<Book> findByBookNameLikeAndTagLike(String bookName, String tag) {
if (StringUtils.isEmpty(bookName) && (StringUtils.isEmpty(tag))) {
return this.findAll();
}
return this.bookRepository.findByBookNameLikeAndTagLike("%" + bookName + "%", "%" + tag + "%");
}
BookRepository.java
public interface BookRepository extends JpaRepository<Book, Long> {
public List<Book> findByBookNameLikeAndTagLike(String bookName, String tag);
}
Wenn Sie in der Methode Like angeben, können Sie eine mehrdeutige Suche durchführen, daher habe ich versucht, sie zu verwenden. Ich dachte, dass% automatisch zum Parameter hinzugefügt wird, aber ich konnte nicht, also fügte ich% hinzu. Es scheint, dass Sie leicht mit Standardfunktionen blättern können, aber ich habe dies vorerst nicht getan.
Eigentlich denke ich, dass es möglich ist, kompliziertes SQL zu erstellen, also denke ich, dass ich ein benutzerdefiniertes Repository erstellen und JPQL und SQL schreiben werde.
@PersistenceContext
EntityManager entityManager;
~
Query query = entityManager.createQuery("from Book where id = :id")
Ich mache einen benutzerdefinierten Varidator und überprüfe ihn.
BookValidator.java
@Component
public class BookValidator implements Validator {
@Autowired
BookService bookService;
@Override
public boolean supports(Class<?> clazz) {
return BookForm.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
// required check
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "bookName", "field.required");
// TODO form check
// BookForm form = BookForm.class.cast(target);
// errors.rejectValue("field", "errorCode");
// global message
if (errors.hasErrors()) {
errors.reject("input.error");
}
}
}
BookForm.java
Fast Buch.Gleich wie Java Entity
Einfache Überprüfungen wie obligatorische Überprüfungen und Größenprüfungen können durch Kommentieren des Formulars durchgeführt werden. Es ist möglich zu überprüfen, aber ich wollte das Formular wiederverwenden und wollte den Scheck nicht an Form und Validator verteilen Schecks werden in Validator zusammengefasst.
Fehlermeldung ist gesetzt.
Die Fehlermeldung befindet sich in messages_ja.properties.
BookController.java
@RequestMapping(value = "/book/save", method = RequestMethod.POST)
public String save(@Valid @ModelAttribute BookForm bookForm, BindingResult result, Model model) {
logger.info("save/" + bookForm.getBookId());
if (result.hasErrors()) {
return "book-edit";
}
try {
this.bookservice.save(new Book(bookForm));
} catch (Exception e) {
result.reject("exception.error");
result.reject("using defaultMessage", e.toString());
return "book-edit";
}
return "book-complete";
}
new Book(bookForm) Ich wechsle von Form zu Entität bei. Gibt es keinen guten Weg?
// Globale Nachricht anzeigen bindingResult.reject("errorCode")
// Fehlermeldung für jedes Feld anzeigen bindingResult.reject("field", "errorCode")
Setzen Sie in Controller in BindingResult und ablehnen Setzen Sie im Validator unter Fehler die Option Ablehnen.
@Valid @ModelAttribute BookForm bookForm, BindingResult result
Es scheint eine Regel zu sein, die Definition des Argumentteils in dieser Reihenfolge zu schreiben. Wenn Sie das BindingResult nach vorne bringen, tritt ein Fehler auf. Ich habe mich ein wenig darauf eingelassen.
Wenn @Valid hinzugefügt wird, wird dies in einem Zustand aufgerufen, der zuvor von Validator überprüft wurde. Daher überprüfe ich result.hasErrors () nur auf Fehler.
BookService.java
@Transactional
public Book save(Book book) {
return this.bookRepository.save(book);
}
Eigentlich denke ich, dass eine kompliziertere Verarbeitung kommen wird, aber es spart nur. Wenn @Transactional hinzugefügt wird, wird es zurückgesetzt, wenn eine Ausnahme auftritt. Es scheint, dass nicht aktivierte Ausnahmen (RuntimeException usw.) zurückgesetzt werden.
Ich durfte mich beziehen) http://qiita.com/NagaokaKenichi/items/a279857cc2d22a35d0dd
Ich dachte darüber nach, @Transactional zur Controller-Methode hinzuzufügen. Aufgrund der Bildschirmsteuerung wird die Ausnahme abgefangen und verarbeitet, aber dann wurde sie nicht zurückgesetzt Ich frage mich, ob die gesamte Geschäftslogik hier angehängt werden sollte, indem sie zum Service gebracht wird Ich dachte.
Wenn es kompliziert ist, scheinen Sie natürlich die Transaktion aus EntityManager herauszunehmen und zu steuern. Ich frage mich, ob es in Ordnung ist, Anmerkungen als Grundlage zu verwenden oder nicht.
Wenn sich die Quelle ändert, die üblicherweise auf jedem Bildschirm verwendet wird, müssen alle Seiten geändert werden ... Ich habe die Quelle gerne geteilt.
Das Folgende ist eine häufige Quelle.
common.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.springframework.org/schema/security">
<head>
<!-- common head -->
<th:block th:fragment="head"><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.min.css}" rel="stylesheet" />
<link href="/css/common.css"
th:href="@{/css/common.css}" rel="stylesheet" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"
th:src="@{/webjars/jquery/3.2.1/jquery.min.js}"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
th:src="@{/webjars/bootstrap/3.3.7/js/bootstrap.min.js}"></script></th:block>
</head>
<body>
<div th:fragment="header" class="container" id="header">
<h1><a href="/">Book</a></h1>
<p style="text-align:right;" th:if="${#httpServletRequest.remoteUser != null}">Hello
<span th:text="${#httpServletRequest.remoteUser}" /> |
<a href="/logout">Ausloggen</a> |
<a href="https://github.com/ewai/spring-boot-mvc-template" target="_blank" alt="spring-boot-mvc-template"><img src="img/mark-github.svg" /></a>
</p>
</div>
<div th:fragment="footer" class="container" id="footer">
<ul>
<li><a href="https://github.com/ewai/spring-boot-mvc-template" target="_blank"><img src="img/mark-github.svg" alt="spring-boot-mvc-template"/></a> <a href="https://github.com/ewai/spring-boot-mvc-template">spring-boot-mvc-template</a></li>
<li><a href="https://github.com/ewai/docker-spring-boot-template-mysql">docker-spring-boot-template-mysql</a></li>
</ul>
</div>
</body>
</html>
Die Vorlage befindet sich im Tag ** th: fragment **. Ich habe drei gemacht.
Da es sich um eine gemeinsame Datei handelt, dachte ich, ich sollte sie in ein anderes Verzeichnis stellen, aber sie wurde nicht gelesen, sodass sie direkt unter Vorlagen abgelegt wird.
book.html
<head>
<th:block th:include="common::head"></th:block>
<title>Book Search</title>
</head>
<body>
<th:block th:replace="common::header"></th:block>
~~~Inhalt~~~
<th:block th:replace="common::footer"></th:block>
</body>
</html>
Ich benutze jede Seite so.
Der Nachteil ist, dass Sie das Design nicht als HTML überprüfen können. Persönlich starte ich die Anwendung und überprüfe sie, während ich sie ausführe. Es ist möglicherweise besser, es nicht zu verwenden, wenn der Designer es mit reinem HTML erstellt und überprüft.
Bei der Anzeige nur für einen bestimmten Benutzer
build.gradle
+ compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4:2.1.3.RELEASE')
Zuerst habe ich Folgendes hinzugefügt, aber es hat nicht funktioniert (sec: authorize = "hasRole ('ROLE_ADMIN')" wurde so wie es ist in HTML angezeigt) und es hat funktioniert, als ich die Version gesenkt habe.
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.2.RELEASE')
Wenn nur autorisierte Benutzer angezeigt werden
index.html
<li class="list-group-item" sec:authorize="hasRole('ROLE_ADMIN')"><a href="/book/create" th:href="@{/book/create}" class="btn btn-link" id="link">Buchregistrierung</a></li>
Hinzugefügt, damit sec verwendet werden kann
index.html
xmlns:sec="http://www.springframework.org/schema/security">
https://github.com/thymeleaf/thymeleaf-extras-springsecurity
build.gradle
springBoot {
executable = true
}
Wenn Sie dies hinzufügen und erstellen, wird es zu einer ausführbaren JAR-Datei. Was bedeutet das?
./spring-boot-mvc-template-0.0.1-SNAPSHOT.jar
Es ist möglich, es so auszuführen.
Es kann verwendet werden, wenn Sie es beim Start des Betriebssystems automatisch starten möchten.
Für centos7
/etc/systemd/system/sbt.service
[Unit]
Description=sbt
After=syslog.target
[Service]
User=sbtuser
ExecStart=/xxx/xxx/spring-boot-mvc-template-0.0.1-SNAPSHOT.jar
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
cmd
systemctl enable sbt.service
Jetzt wird es automatisch gestartet, wenn das Betriebssystem gestartet wird.
Spring Boot Actuator
build.gradle
+ compile('org.springframework.boot:spring-boot-starter-actuator')
Ich habe es hinzugefügt, weil Sie den Serverstatus einfach überprüfen können, indem Sie dies hinzufügen. Ein Benutzer mit der Berechtigung "ACUTIATOR" in getAuthorities () des Benutzers (UserDetails) Es scheint, dass Sie es nicht sehen können, wenn Sie sich nicht anmelden. Diesmal verfügt der Administrator über diese Berechtigung, sodass Sie sie anzeigen können, indem Sie sich als Administrator / Administrator anmelden.
http://localhost:8080/health
/health
{"status":"UP","diskSpace":{"status":"UP","total":247762329600,"free":125178765312,"threshold":10485760},"db":{"status":"UP","database":"MySQL","hello":1}}
Sie können sehen, dass der Server aktiv ist, die Datenbank aktiv ist und der Speicherplatz noch frei ist.
Als ich versuchte, es als Benutzer ohne "ACUTIATOR" -Berechtigung zu bezeichnen, Da {"status": "UP"} zurückgegeben wird, können Sie sehen, ob der Server aktiv ist.
http://localhost:8080/env Wenn Sie nicht über die Berechtigung "ACUTIATOR" verfügen, können Sie erwartungsgemäß nicht auf das Umgebungsvariablensystem verweisen. Ich habe einen Fehler bekommen.
Access is denied. User must have one of the these roles: ACTUATOR
http://localhost:8080/mappings Es scheint, dass ein Designdokument erstellt werden kann.
/mappings
"{[/book],methods=[GET]}":{
"bean":"requestMappingHandlerMapping",
"method":"public java.lang.String info.ewai.sbmt.web.BookController.index(org.springframework.ui.Model)"
},
"{[/book/edit/{bookId}],methods=[GET]}":{
"bean":"requestMappingHandlerMapping",
"method":"public java.lang.String info.ewai.sbmt.web.BookController.edit(info.ewai.sbmt.web.form.BookForm,org.springframework.validation.BindingResult,java.lang.Long,org.springframework.ui.Model)"
},
"{[/book/save],methods=[POST]}":{
"bean":"requestMappingHandlerMapping",
"method":"public java.lang.String info.ewai.sbmt.web.BookController.save(info.ewai.sbmt.web.form.BookForm,org.springframework.validation.BindingResult,org.springframework.ui.Model)"
},
Es gibt wahrscheinlich auch andere Endpunkte. http://qiita.com/MariMurotani/items/01dafd2978076b5db2f3
Es scheint, dass Sie es anpassen und den Port der URL ändern können, aber lassen Sie es wie es ist.
Spring Framework Reference Documentation 4.3.0.RELEASE http://docs.spring.io/spring/docs/4.3.0.RELEASE/spring-framework-reference/htmlsingle/
Spring Boot Reference Guide 1.5.6.RELEASE http://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/htmlsingle/
Gründliche Einführung in die Entwicklung von Spring Java-Anwendungen mit Spring Framework Es war hilfreich, die Funktionen von Spring im Allgemeinen zu erklären und zu verstehen. Es gibt auch ein wenig über Spring Boot geschrieben.
Einführung in die Spring Boot-Programmierung
Es ist ein Frühlingsstiefel, der sehr einfach herzustellen ist, aber es gab Zeiten, in denen ich die Regeln nicht kannte. Ich hatte jedoch das Gefühl, dass es aufgrund der zahlreichen Informationen zu offiziellen Dokumenten, Büchern und dem Internet relativ einfach zu lösen war. Dieses Mal habe ich ein einfaches Beispielsystem erstellt, daher denke ich, dass es bei der Erstellung eines praktischen Systems verschiedene Versuche und Fehler und Anpassungen geben wird, aber ich hatte das Gefühl, dass ich es gerne verwenden würde. Vielen Dank an alle, die verschiedene Informationen in Büchern und online veröffentlicht haben.