авторизация #4
@ -37,10 +37,10 @@ dependencies {
|
|||||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
|
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf'
|
||||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation'
|
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation'
|
||||||
implementation group: 'org.springframework.boot', name:'spring-boot-starter-data-jpa'
|
implementation group: 'org.springframework.boot', name:'spring-boot-starter-data-jpa'
|
||||||
//implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security'
|
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security'
|
||||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: versionSLF4J
|
implementation group: 'org.slf4j', name: 'slf4j-api', version: versionSLF4J
|
||||||
implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect'
|
implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect'
|
||||||
//implementation group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity5'
|
implementation group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity5'
|
||||||
implementation group: 'com.h2database', name:'h2'
|
implementation group: 'com.h2database', name:'h2'
|
||||||
implementation group: 'javax.xml.bind', name:'jaxb-api'
|
implementation group: 'javax.xml.bind', name:'jaxb-api'
|
||||||
implementation group: 'org.javassist', name:'javassist'
|
implementation group: 'org.javassist', name:'javassist'
|
||||||
|
BIN
data/db.mv.db
BIN
data/db.mv.db
Binary file not shown.
@ -2,10 +2,25 @@ package ru.ulstu;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import ru.ulstu.user.UserService;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class SeminarApplication {
|
public class SeminarApplication {
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
public SeminarApplication(UserService userService) {
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(SeminarApplication.class, args);
|
SpringApplication.run(SeminarApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
public void doSomethingAfterStartup() {
|
||||||
|
System.out.println("hello world, I have just started up");
|
||||||
|
userService.initDefaultAdmin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
12
src/main/java/ru/ulstu/configuration/Constants.java
Normal file
12
src/main/java/ru/ulstu/configuration/Constants.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
public static final int MIN_PASSWORD_LENGTH = 6;
|
||||||
|
|
||||||
|
public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$";
|
||||||
|
|
||||||
|
public static final String COOKIES_NAME = "JSESSIONID";
|
||||||
|
public static final String LOGOUT_URL = "/login?logout";
|
||||||
|
public static final String SESSION_ID_ATTR = "sessionId";
|
||||||
|
public static final int SESSION_TIMEOUT_SECONDS = 30 * 60;
|
||||||
|
}
|
@ -20,6 +20,8 @@ import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
|||||||
public class MvcConfiguration implements WebMvcConfigurer {
|
public class MvcConfiguration implements WebMvcConfigurer {
|
||||||
@Override
|
@Override
|
||||||
public void addViewControllers(ViewControllerRegistry registry) {
|
public void addViewControllers(ViewControllerRegistry registry) {
|
||||||
|
registry.addViewController("/login");
|
||||||
|
registry.addViewController("/loginError");
|
||||||
registry.addViewController("/index");
|
registry.addViewController("/index");
|
||||||
registry.addViewController("/admin");
|
registry.addViewController("/admin");
|
||||||
registry.addViewController("/editNews");
|
registry.addViewController("/editNews");
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class PasswordEncoderConfiguration {
|
||||||
|
@Bean
|
||||||
|
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||||
|
return new BCryptPasswordEncoder();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.BeanInitializationException;
|
||||||
|
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.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
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.authentication.AuthenticationSuccessHandler;
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||||
|
import ru.ulstu.model.UserRoleConstants;
|
||||||
|
import ru.ulstu.user.UserService;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||||
|
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
private final BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||||
|
private final AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||||
|
private final LogoutSuccessHandler logoutSuccessHandler;
|
||||||
|
|
||||||
|
public SecurityConfiguration(UserService userService,
|
||||||
|
BCryptPasswordEncoder bCryptPasswordEncoder,
|
||||||
|
AuthenticationSuccessHandler authenticationSuccessHandler,
|
||||||
|
LogoutSuccessHandler logoutSuccessHandler) {
|
||||||
|
this.userService = userService;
|
||||||
|
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
|
||||||
|
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
||||||
|
this.logoutSuccessHandler = logoutSuccessHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
http.csrf().disable();
|
||||||
|
http.headers().frameOptions().disable();
|
||||||
|
log.debug("Security enabled");
|
||||||
|
http.authorizeRequests()
|
||||||
|
.antMatchers("/").permitAll()
|
||||||
|
.antMatchers("/login", "/index", "/news/**", "/h2-console/*", "/h2-console").permitAll()
|
||||||
|
.antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
.formLogin()
|
||||||
|
.loginPage("/login")
|
||||||
|
.failureUrl("/loginError")
|
||||||
|
.successHandler(authenticationSuccessHandler)
|
||||||
|
.permitAll()
|
||||||
|
.and()
|
||||||
|
.logout()
|
||||||
|
.logoutSuccessHandler(logoutSuccessHandler)
|
||||||
|
.logoutSuccessUrl(Constants.LOGOUT_URL)
|
||||||
|
.invalidateHttpSession(false)
|
||||||
|
.clearAuthentication(true)
|
||||||
|
.deleteCookies(Constants.COOKIES_NAME)
|
||||||
|
.permitAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(WebSecurity web) {
|
||||||
|
web.ignoring()
|
||||||
|
.antMatchers("/css/**")
|
||||||
|
.antMatchers("/js/**")
|
||||||
|
.antMatchers("/img/**")
|
||||||
|
.antMatchers("/templates/**")
|
||||||
|
.antMatchers("/webjars/**");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||||
|
try {
|
||||||
|
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new BeanInitializationException("Security configuration failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,11 +7,9 @@
|
|||||||
package ru.ulstu.configuration;
|
package ru.ulstu.configuration;
|
||||||
|
|
||||||
import nz.net.ultraq.thymeleaf.LayoutDialect;
|
import nz.net.ultraq.thymeleaf.LayoutDialect;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.MessageSource;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
|
import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect;
|
||||||
import org.thymeleaf.spring5.SpringTemplateEngine;
|
import org.thymeleaf.spring5.SpringTemplateEngine;
|
||||||
import org.thymeleaf.templateresolver.ITemplateResolver;
|
import org.thymeleaf.templateresolver.ITemplateResolver;
|
||||||
|
|
||||||
@ -19,10 +17,11 @@ import org.thymeleaf.templateresolver.ITemplateResolver;
|
|||||||
public class TemplateConfiguration {
|
public class TemplateConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
|
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver, SpringSecurityDialect sec) {
|
||||||
final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
|
final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
|
||||||
templateEngine.addTemplateResolver(templateResolver);
|
templateEngine.addTemplateResolver(templateResolver);
|
||||||
templateEngine.addDialect(new LayoutDialect());
|
templateEngine.addDialect(new LayoutDialect());
|
||||||
|
templateEngine.addDialect(sec);
|
||||||
return templateEngine;
|
return templateEngine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public class IndexController {
|
|||||||
|
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String index(Model model) {
|
public String index(Model model) {
|
||||||
model.addAttribute("news", newsService.getAll());
|
model.addAttribute("news", newsService.getLast());
|
||||||
return "index";
|
return "index";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,14 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import ru.ulstu.model.News;
|
import ru.ulstu.model.News;
|
||||||
import ru.ulstu.service.NewsService;
|
import ru.ulstu.service.NewsService;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
|
@RequestMapping("news")
|
||||||
public class NewsController {
|
public class NewsController {
|
||||||
private final NewsService newsService;
|
private final NewsService newsService;
|
||||||
|
|
||||||
@ -26,6 +28,12 @@ public class NewsController {
|
|||||||
this.newsService = newsService;
|
this.newsService = newsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/news")
|
||||||
|
public String index(Model model) {
|
||||||
|
model.addAttribute("news", newsService.getAll());
|
||||||
|
return "news";
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/editNews/{newsId}")
|
@GetMapping("/editNews/{newsId}")
|
||||||
public String editNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
public String editNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
||||||
model.addAttribute("news", (id != null && id != 0) ? newsService.getById(id) : new News());
|
model.addAttribute("news", (id != null && id != 0) ? newsService.getById(id) : new News());
|
||||||
@ -35,7 +43,7 @@ public class NewsController {
|
|||||||
@GetMapping("/news/{newsId}")
|
@GetMapping("/news/{newsId}")
|
||||||
public String viewNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
public String viewNews(@PathVariable(value = "newsId") Integer id, Model model) {
|
||||||
model.addAttribute("news", id != null ? newsService.getById(id) : new News());
|
model.addAttribute("news", id != null ? newsService.getById(id) : new News());
|
||||||
return "news";
|
return "viewNews";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("saveNews")
|
@PostMapping("saveNews")
|
||||||
@ -45,12 +53,12 @@ public class NewsController {
|
|||||||
}
|
}
|
||||||
newsService.save(news);
|
newsService.save(news);
|
||||||
|
|
||||||
return "redirect:/";
|
return "redirect:/news/news";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("deleteNews/{newsId}")
|
@GetMapping("deleteNews/{newsId}")
|
||||||
public String delete(@PathVariable(value = "newsId") Integer id) {
|
public String delete(@PathVariable(value = "newsId") Integer id) {
|
||||||
newsService.delete(id);
|
newsService.delete(id);
|
||||||
return "redirect:/";
|
return "redirect:/news/news";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
71
src/main/java/ru/ulstu/model/User.java
Normal file
71
src/main/java/ru/ulstu/model/User.java
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package ru.ulstu.model;
|
||||||
|
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.JoinTable;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "is_users")
|
||||||
|
public class User extends BaseEntity {
|
||||||
|
@NotNull
|
||||||
|
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||||
|
@Size(min = 1, max = 50)
|
||||||
|
@Column(length = 50, unique = true, nullable = false)
|
||||||
|
private String login;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Size(min = 60, max = 60)
|
||||||
|
@Column(name = "password_hash", length = 60, nullable = false)
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
@JoinTable(
|
||||||
|
name = "is_user_role",
|
||||||
|
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
|
||||||
|
inverseJoinColumns = {@JoinColumn(name = "user_role_name", referencedColumnName = "name")})
|
||||||
|
private Set<UserRole> roles;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
roles = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(String login, String password, Set<UserRole> roles) {
|
||||||
|
this.login = login;
|
||||||
|
this.password = password;
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogin(String login) {
|
||||||
|
this.login = login.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UserRole> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(Set<UserRole> roles) {
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
}
|
50
src/main/java/ru/ulstu/model/UserRole.java
Normal file
50
src/main/java/ru/ulstu/model/UserRole.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package ru.ulstu.model;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "is_user_roles")
|
||||||
|
public class UserRole {
|
||||||
|
@Id
|
||||||
|
@NotNull
|
||||||
|
@Size(max = 50)
|
||||||
|
@Column(length = 50, nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public UserRole() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserRole(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UserRole role = (UserRole) o;
|
||||||
|
return !(name != null ? !name.equals(role.name) : role.name != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return name != null ? name.hashCode() : 0;
|
||||||
|
}
|
||||||
|
}
|
6
src/main/java/ru/ulstu/model/UserRoleConstants.java
Normal file
6
src/main/java/ru/ulstu/model/UserRoleConstants.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package ru.ulstu.model;
|
||||||
|
|
||||||
|
public class UserRoleConstants {
|
||||||
|
public static final String ADMIN = "ROLE_ADMIN";
|
||||||
|
public static final String USER = "ROLE_USER";
|
||||||
|
}
|
103
src/main/java/ru/ulstu/model/UserSession.java
Normal file
103
src/main/java/ru/ulstu/model/UserSession.java
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package ru.ulstu.model;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "is_user_sessions")
|
||||||
|
public class UserSession extends BaseEntity {
|
||||||
|
@NotNull
|
||||||
|
@Column(name = "session_id", nullable = false, unique = true)
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(name = "ip_address", nullable = false)
|
||||||
|
private String ipAddress;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(nullable = false)
|
||||||
|
private String host;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Column(name = "login_time", nullable = false)
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
private Date loginTime;
|
||||||
|
|
||||||
|
@Column(name = "logout_time")
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
private Date logoutTime;
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "user_id")
|
||||||
|
private User user;
|
||||||
|
|
||||||
|
public UserSession() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserSession(String sessionId, String ipAddress, String host, User user) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
this.ipAddress = ipAddress;
|
||||||
|
this.host = host;
|
||||||
|
this.loginTime = new Date();
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIpAddress() {
|
||||||
|
return ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLoginTime() {
|
||||||
|
return loginTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getLogoutTime() {
|
||||||
|
return logoutTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSessionId(String sessionId) {
|
||||||
|
this.sessionId = sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIpAddress(String ipAddress) {
|
||||||
|
this.ipAddress = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoginTime(Date loginTime) {
|
||||||
|
this.loginTime = loginTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogoutTime(Date logoutTime) {
|
||||||
|
this.logoutTime = logoutTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
this.logoutTime = new Date();
|
||||||
|
}
|
||||||
|
}
|
@ -3,5 +3,8 @@ package ru.ulstu.repository;
|
|||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import ru.ulstu.model.News;
|
import ru.ulstu.model.News;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface NewsRepository extends JpaRepository<News, Integer> {
|
public interface NewsRepository extends JpaRepository<News, Integer> {
|
||||||
|
List<News> findFirst3ByOrderByDateDesc();
|
||||||
}
|
}
|
||||||
|
@ -46,4 +46,8 @@ public class NewsService {
|
|||||||
public void delete(Integer id) {
|
public void delete(Integer id) {
|
||||||
newsRepository.deleteById(id);
|
newsRepository.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<News> getLast() {
|
||||||
|
return newsRepository.findFirst3ByOrderByDateDesc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
23
src/main/java/ru/ulstu/user/IpAddressResolver.java
Normal file
23
src/main/java/ru/ulstu/user/IpAddressResolver.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
public final class IpAddressResolver {
|
||||||
|
private static final String CLIENT_IP_HEADER = "Client-IP";
|
||||||
|
private static final String FORWARDED_FOR_HEADER = "X-Forwarded-For";
|
||||||
|
|
||||||
|
public static String getRemoteAddr(HttpServletRequest request) {
|
||||||
|
String headerClientIp = request.getHeader("");
|
||||||
|
String headerXForwardedFor = request.getHeader(HttpServletRequest.FORM_AUTH);
|
||||||
|
if (StringUtils.isEmpty(request.getRemoteAddr()) && !StringUtils.isEmpty(headerClientIp)) {
|
||||||
|
return headerClientIp;
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(headerXForwardedFor)) {
|
||||||
|
return headerXForwardedFor;
|
||||||
|
}
|
||||||
|
return request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
7
src/main/java/ru/ulstu/user/UserNotFoundException.java
Normal file
7
src/main/java/ru/ulstu/user/UserNotFoundException.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
public class UserNotFoundException extends RuntimeException {
|
||||||
|
public UserNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
15
src/main/java/ru/ulstu/user/UserRepository.java
Normal file
15
src/main/java/ru/ulstu/user/UserRepository.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.EntityGraph;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import ru.ulstu.model.User;
|
||||||
|
|
||||||
|
public interface UserRepository extends JpaRepository<User, Integer> {
|
||||||
|
User findOneByLoginIgnoreCase(String login);
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = "roles")
|
||||||
|
User findOneWithRolesById(int id);
|
||||||
|
|
||||||
|
@EntityGraph(attributePaths = "roles")
|
||||||
|
User findOneWithRolesByLogin(String login);
|
||||||
|
}
|
7
src/main/java/ru/ulstu/user/UserRoleRepository.java
Normal file
7
src/main/java/ru/ulstu/user/UserRoleRepository.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import ru.ulstu.model.UserRole;
|
||||||
|
|
||||||
|
public interface UserRoleRepository extends JpaRepository<UserRole, String> {
|
||||||
|
}
|
72
src/main/java/ru/ulstu/user/UserService.java
Normal file
72
src/main/java/ru/ulstu/user/UserService.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
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.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.ulstu.model.User;
|
||||||
|
import ru.ulstu.model.UserRole;
|
||||||
|
import ru.ulstu.model.UserRoleConstants;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class UserService implements UserDetailsService {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserService.class);
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
private final UserRoleRepository userRoleRepository;
|
||||||
|
|
||||||
|
public UserService(PasswordEncoder passwordEncoder,
|
||||||
|
UserRepository userRepository,
|
||||||
|
UserRoleRepository userRoleRepository) {
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
this.userRoleRepository = userRoleRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUserByLogin(String login) {
|
||||||
|
return userRepository.findOneByLoginIgnoreCase(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) {
|
||||||
|
final User user = userRepository.findOneByLoginIgnoreCase(username);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(username);
|
||||||
|
}
|
||||||
|
return new org.springframework.security.core.userdetails.User(user.getLogin(),
|
||||||
|
user.getPassword(),
|
||||||
|
Optional.ofNullable(user.getRoles()).orElse(Collections.emptySet()).stream()
|
||||||
|
.map(role -> new SimpleGrantedAuthority(role.getName()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public User createUser(User user) {
|
||||||
|
if (getUserByLogin(user.getLogin()) != null) {
|
||||||
|
throw new RuntimeException(user.getLogin());
|
||||||
|
}
|
||||||
|
//user.setRoles(Collections.singleton(new UserRole(UserRoleConstants.USER)));
|
||||||
|
user.setPassword(passwordEncoder.encode(user.getPassword()));
|
||||||
|
user = userRepository.save(user);
|
||||||
|
log.debug("Created Information for User: {}", user.getLogin());
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initDefaultAdmin() {
|
||||||
|
String adminLogin = "admin";
|
||||||
|
String adminPassword = "adminadmin";
|
||||||
|
if (getUserByLogin(adminLogin) == null) {
|
||||||
|
UserRole adminRole = userRoleRepository.save(new UserRole(UserRoleConstants.ADMIN));
|
||||||
|
createUser(new User(adminLogin, adminPassword, Set.of(adminRole)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/main/java/ru/ulstu/user/UserSessionLoginHandler.java
Normal file
44
src/main/java/ru/ulstu/user/UserSessionLoginHandler.java
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||||
|
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserSessionLoginHandler extends SavedRequestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserSessionLoginHandler.class);
|
||||||
|
private final UserSessionService userSessionService;
|
||||||
|
|
||||||
|
public UserSessionLoginHandler(UserSessionService userSessionService) {
|
||||||
|
super();
|
||||||
|
this.userSessionService = userSessionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSuccess(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
Authentication authentication) throws IOException, ServletException {
|
||||||
|
super.onAuthenticationSuccess(request, response, authentication);
|
||||||
|
final String login = authentication.getName();
|
||||||
|
final String ipAddress = IpAddressResolver.getRemoteAddr(request);
|
||||||
|
final String host = request.getRemoteHost();
|
||||||
|
log.debug("Authentication Success for {}@{} ({})", login, ipAddress, host);
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
final String sessionId = session.getId();
|
||||||
|
userSessionService.createUserSession(sessionId, login, ipAddress, host);
|
||||||
|
session.setAttribute(Constants.SESSION_ID_ATTR, sessionId);
|
||||||
|
session.setMaxInactiveInterval(Constants.SESSION_TIMEOUT_SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
src/main/java/ru/ulstu/user/UserSessionLogoutHandler.java
Normal file
48
src/main/java/ru/ulstu/user/UserSessionLogoutHandler.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||||
|
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import ru.ulstu.configuration.Constants;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserSessionLogoutHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserSessionLogoutHandler.class);
|
||||||
|
private final UserSessionService userSessionService;
|
||||||
|
|
||||||
|
public UserSessionLogoutHandler(UserSessionService userSessionService) {
|
||||||
|
this.userSessionService = userSessionService;
|
||||||
|
setDefaultTargetUrl(Constants.LOGOUT_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLogoutSuccess(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
Authentication authentication) throws IOException, ServletException {
|
||||||
|
if (authentication == null) {
|
||||||
|
super.onLogoutSuccess(request, response, authentication);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String login = authentication.getName();
|
||||||
|
final String ipAddress = IpAddressResolver.getRemoteAddr(request);
|
||||||
|
final String host = request.getRemoteHost();
|
||||||
|
log.debug("Logout Success for {}@{} ({})", login, ipAddress, host);
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
final String sessionId = session.getAttribute(Constants.SESSION_ID_ATTR).toString();
|
||||||
|
userSessionService.closeUserSession(sessionId);
|
||||||
|
session.removeAttribute(Constants.SESSION_ID_ATTR);
|
||||||
|
session.invalidate();
|
||||||
|
}
|
||||||
|
super.onLogoutSuccess(request, response, authentication);
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/ru/ulstu/user/UserSessionRepository.java
Normal file
13
src/main/java/ru/ulstu/user/UserSessionRepository.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import ru.ulstu.model.UserSession;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface UserSessionRepository extends JpaRepository<UserSession, Integer> {
|
||||||
|
UserSession findOneBySessionId(String sessionId);
|
||||||
|
|
||||||
|
List<UserSession> findAllByLogoutTimeIsNullAndLoginTimeBefore(Date date);
|
||||||
|
}
|
40
src/main/java/ru/ulstu/user/UserSessionService.java
Normal file
40
src/main/java/ru/ulstu/user/UserSessionService.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import ru.ulstu.model.User;
|
||||||
|
import ru.ulstu.model.UserSession;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class UserSessionService {
|
||||||
|
private final Logger log = LoggerFactory.getLogger(UserSessionService.class);
|
||||||
|
private final UserSessionRepository userSessionRepository;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
public UserSessionService(UserSessionRepository userSessionRepository, UserService userService) {
|
||||||
|
this.userSessionRepository = userSessionRepository;
|
||||||
|
this.userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createUserSession(String sessionId, String login, String ipAddress, String host) {
|
||||||
|
final User user = userService.getUserByLogin(login);
|
||||||
|
if (user == null) {
|
||||||
|
throw new UserNotFoundException(login);
|
||||||
|
}
|
||||||
|
userSessionRepository.save(new UserSession(sessionId, ipAddress, host, user));
|
||||||
|
log.debug("User session {} created for user {}@{} ({})", sessionId, login, ipAddress, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeUserSession(String sessionId) {
|
||||||
|
final UserSession userSession = userSessionRepository.findOneBySessionId(sessionId);
|
||||||
|
if (userSession == null) {
|
||||||
|
throw new IllegalArgumentException(String.format("User session %s not found", sessionId));
|
||||||
|
}
|
||||||
|
userSession.close();
|
||||||
|
userSessionRepository.save(userSession);
|
||||||
|
log.debug("User session {} closed", sessionId);
|
||||||
|
}
|
||||||
|
}
|
24
src/main/java/ru/ulstu/user/UserUtils.java
Normal file
24
src/main/java/ru/ulstu/user/UserUtils.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package ru.ulstu.user;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
public class UserUtils {
|
||||||
|
public static String getCurrentUserLogin() {
|
||||||
|
final SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||||
|
if (securityContext == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Authentication authentication = securityContext.getAuthentication();
|
||||||
|
if (authentication.getPrincipal() instanceof UserDetails) {
|
||||||
|
final UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
|
||||||
|
return springSecurityUser.getUsername();
|
||||||
|
}
|
||||||
|
if (authentication.getPrincipal() instanceof String) {
|
||||||
|
return (String) authentication.getPrincipal();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,6 @@
|
|||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-dark, .link-dark:visited, .link-dark:focus {
|
.link-dark, .link-dark:visited, .link-dark:focus, .link-dark:any-link {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
@ -5,12 +5,10 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
<html
|
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
|
||||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
|
||||||
layout:decorate="~{default}">
|
|
||||||
<div class="container" layout:fragment="content">
|
<div class="container" layout:fragment="content">
|
||||||
<a href="/editNews/0" class="btn btn-outline-dark">
|
<a href="/editNews/0" class="btn btn-outline-dark">
|
||||||
<i class="fa fa-plus-square" aria-hidden="true"> Добавить новость</i>
|
<i class="fa fa-plus-square" aria-hidden="true">Добавить новость</i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</html>
|
</html>
|
||||||
|
@ -5,8 +5,9 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru"
|
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml">
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
|
||||||
|
xmlns:th="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<title th:text="#{messages.app-name}">app-name</title>
|
<title th:text="#{messages.app-name}">app-name</title>
|
||||||
@ -32,7 +33,7 @@
|
|||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="navbar-nav mr-auto">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/news">Новости</a>
|
<a class="nav-link" href="/news/news">Новости</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/news">Заседания</a>
|
<a class="nav-link" href="/news">Заседания</a>
|
||||||
@ -43,6 +44,12 @@
|
|||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/admin">Администратору</a>
|
<a class="nav-link" href="/admin">Администратору</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<div sec:authorize="isAuthenticated()">
|
||||||
|
<a class="nav-link" href="/logout">Выход</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
layout:decorate="~{default}">
|
layout:decorate="~{default}">
|
||||||
<div class="container" layout:fragment="content">
|
<div class="container" layout:fragment="content">
|
||||||
<h3>Редактирование новости:</h3>
|
<h3>Редактирование новости:</h3>
|
||||||
<form action="#" th:action="@{/saveNews}" th:object="${news}" method="post">
|
<form action="#" th:action="@{/news/saveNews}" th:object="${news}" method="post">
|
||||||
<input type="hidden" th:field="*{id}">
|
<input type="hidden" th:field="*{id}">
|
||||||
<input type="hidden" th:field="*{date}">
|
<input type="hidden" th:field="*{date}">
|
||||||
<input type="hidden" th:field="*{version}">
|
<input type="hidden" th:field="*{version}">
|
||||||
|
13
src/main/resources/templates/error/403.html
Normal file
13
src/main/resources/templates/error/403.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<h5>Доступ запрещён</h5>
|
||||||
|
<a href="/"><h6>Вернуться на главную</h6></a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
src/main/resources/templates/error/404.html
Normal file
13
src/main/resources/templates/error/404.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<h5>Страница не найдена</h5>
|
||||||
|
<a href="/"><h6>Вернуться на главную</h6></a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
src/main/resources/templates/error/500.html
Normal file
13
src/main/resources/templates/error/500.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<h5>Ошибка сервера</h5>
|
||||||
|
<a href="/"><h6>Вернуться на главную</h6></a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -5,9 +5,8 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
<html
|
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
||||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
layout:decorate="~{default}">
|
||||||
layout:decorate="~{default}">
|
|
||||||
<div class="container" layout:fragment="content">
|
<div class="container" layout:fragment="content">
|
||||||
<div th:each="n : ${news}" class="news">
|
<div th:each="n : ${news}" class="news">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -17,16 +16,7 @@
|
|||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<a th:href="@{'/news/' + ${n.id}}" class="link-dark"><h5 th:text="${n.title}"/></a>
|
<a th:href="@{'/news/news/' + ${n.id}}" class="link-dark"><h5 th:text="${n.title}"/></a>
|
||||||
</div>
|
|
||||||
<div class="col-md-2" style="text-align: right">
|
|
||||||
<a th:href="@{'/editNews/' + ${n.id}}" class="link-dark">
|
|
||||||
<i class="fa fa-pencil" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
<a th:href="@{'/deleteNews/' + ${n.id}}" class="link-dark"
|
|
||||||
onclick="return confirm('Удалить новость?')">
|
|
||||||
<i class="fa fa-trash" aria-hidden="true"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:text="${n.preview}" class="news-item"></div>
|
<div th:text="${n.preview}" class="news-item"></div>
|
||||||
|
35
src/main/resources/templates/login.html
Normal file
35
src/main/resources/templates/login.html
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav layout:fragment="navbar">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<a class="navbar-brand" href="/"><span class="ui-menuitem-text"><i
|
||||||
|
class="fa fa-plane fa-4" aria-hidden="true"></i> Balance</span></a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div id="signin" class="tab-pane active">
|
||||||
|
<form th:action="@{/login}" method="post" class="margined-top-10">
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="username" id="username" class="form-control"
|
||||||
|
placeholder="Логин" required="true" autofocus="true"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" name="password" id="password" class="form-control"
|
||||||
|
placeholder="Пароль" required="true"/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-outline-dark btn-block">Войти</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
40
src/main/resources/templates/loginError.html
Normal file
40
src/main/resources/templates/loginError.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en"
|
||||||
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav layout:fragment="navbar">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<a class="navbar-brand" href="/"><span class="ui-menuitem-text"><i
|
||||||
|
class="fa fa-plane fa-4" aria-hidden="true"></i> Balance</span></a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<ul id="messages" class="feedback-panel">
|
||||||
|
<div class="alert alert-danger" role="alert">Ошибка входа</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div id="signin" class="tab-pane active">
|
||||||
|
<form th:action="@{/login}" method="post" class="margined-top-10">
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" name="username" id="username" class="form-control"
|
||||||
|
placeholder="Логин" required="true" autofocus="true"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" name="password" id="password" class="form-control"
|
||||||
|
placeholder="Пароль" required="true"/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-outline-dark btn-block">Войти</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -5,21 +5,36 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
<html
|
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
||||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
|
||||||
layout:decorate="~{default}">
|
layout:decorate="~{default}">
|
||||||
<div class="container" layout:fragment="content">
|
<div class="container" layout:fragment="content">
|
||||||
<div class="row">
|
<div th:each="n : ${news}" class="news">
|
||||||
<div class="col-md-4">
|
<div class="row">
|
||||||
<img class="news-image" src="/img/logo.svg"/>
|
<div class="col-md-4">
|
||||||
</div>
|
<img class="news-image" src="/img/logo.svg"/>
|
||||||
<div class="col-md-8">
|
</div>
|
||||||
<h5 th:text="${news.title}"/>
|
<div class="col-md-8">
|
||||||
<div th:text="${news.text}" class="news-item"></div>
|
<div class="row">
|
||||||
|
<div class="col-md-10">
|
||||||
|
<a th:href="@{'/news/news/' + ${n.id}}" class="link-dark"><h5 th:text="${n.title}"/></a>
|
||||||
|
</div>
|
||||||
|
<div sec:authorize="hasRole('ROLE_ADMIN')" class="col-md-2" style="text-align: right">
|
||||||
|
<a th:href="@{'/news/editNews/' + ${n.id}}" class="link-dark">
|
||||||
|
<i class="fa fa-pencil" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
<a th:href="@{'/news/deleteNews/' + ${n.id}}" class="link-dark"
|
||||||
|
onclick="return confirm('Удалить новость?')">
|
||||||
|
<i class="fa fa-trash" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:text="${n.preview}" class="news-item"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div th:text="${'Опубликовано: ' + #calendars.format(n.date, 'dd.MM.yyyy HH:mm')}"
|
||||||
|
class="news-date"></div>
|
||||||
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
<div th:text="${'Опубликовано: ' + #calendars.format(news.date, 'dd.MM.yyyy HH:mm')}"
|
|
||||||
class="news-date"></div>
|
|
||||||
<a href="javascript:history.back()" class="btn btn-outline-dark" style="text-align: right">Назад</a>
|
|
||||||
</div>
|
</div>
|
||||||
</html>
|
</html>
|
||||||
|
25
src/main/resources/templates/viewNews.html
Normal file
25
src/main/resources/templates/viewNews.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2021 Anton Romanov - All Rights Reserved
|
||||||
|
~ You may use, distribute and modify this code, please write to: romanov73@gmail.com.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
|
||||||
|
<html
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
|
||||||
|
layout:decorate="~{default}">
|
||||||
|
<div class="container" layout:fragment="content">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<img class="news-image" src="/img/logo.svg"/>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h5 th:text="${news.title}"/>
|
||||||
|
<div th:text="${news.text}" class="news-item"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:text="${'Опубликовано: ' + #calendars.format(news.date, 'dd.MM.yyyy HH:mm')}"
|
||||||
|
class="news-date"></div>
|
||||||
|
<a href="javascript:history.back()" class="btn btn-outline-dark" style="text-align: right">Назад</a>
|
||||||
|
</div>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user