From 5f13305e9593e56a77ffc0cf8c5854dcd3a4c912 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Wed, 9 Mar 2022 18:04:09 +0400 Subject: [PATCH 1/7] #3 -- add user classes, configs and pages --- build.gradle | 4 +- data/db.mv.db | Bin 32768 -> 49152 bytes .../ru/ulstu/configuration/Constants.java | 12 ++ .../ulstu/configuration/MvcConfiguration.java | 1 + .../PasswordEncoderConfiguration.java | 13 +++ .../configuration/SecurityConfiguration.java | 86 +++++++++++++++ src/main/java/ru/ulstu/model/User.java | 65 +++++++++++ src/main/java/ru/ulstu/model/UserRole.java | 50 +++++++++ .../ru/ulstu/model/UserRoleConstants.java | 6 + src/main/java/ru/ulstu/model/UserSession.java | 103 ++++++++++++++++++ .../java/ru/ulstu/user/IpAddressResolver.java | 23 ++++ .../ru/ulstu/user/UserNotFoundException.java | 7 ++ .../java/ru/ulstu/user/UserRepository.java | 15 +++ .../ru/ulstu/user/UserRoleRepository.java | 7 ++ src/main/java/ru/ulstu/user/UserService.java | 42 +++++++ .../ulstu/user/UserSessionLoginHandler.java | 44 ++++++++ .../ulstu/user/UserSessionLogoutHandler.java | 48 ++++++++ .../ru/ulstu/user/UserSessionRepository.java | 13 +++ .../ru/ulstu/user/UserSessionService.java | 40 +++++++ src/main/java/ru/ulstu/user/UserUtils.java | 24 ++++ src/main/resources/templates/admin.html | 6 +- src/main/resources/templates/error/403.html | 13 +++ src/main/resources/templates/error/404.html | 13 +++ src/main/resources/templates/error/500.html | 13 +++ src/main/resources/templates/login.html | 45 ++++++++ 25 files changed, 687 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ru/ulstu/configuration/Constants.java create mode 100644 src/main/java/ru/ulstu/configuration/PasswordEncoderConfiguration.java create mode 100644 src/main/java/ru/ulstu/configuration/SecurityConfiguration.java create mode 100644 src/main/java/ru/ulstu/model/User.java create mode 100644 src/main/java/ru/ulstu/model/UserRole.java create mode 100644 src/main/java/ru/ulstu/model/UserRoleConstants.java create mode 100644 src/main/java/ru/ulstu/model/UserSession.java create mode 100644 src/main/java/ru/ulstu/user/IpAddressResolver.java create mode 100644 src/main/java/ru/ulstu/user/UserNotFoundException.java create mode 100644 src/main/java/ru/ulstu/user/UserRepository.java create mode 100644 src/main/java/ru/ulstu/user/UserRoleRepository.java create mode 100644 src/main/java/ru/ulstu/user/UserService.java create mode 100644 src/main/java/ru/ulstu/user/UserSessionLoginHandler.java create mode 100644 src/main/java/ru/ulstu/user/UserSessionLogoutHandler.java create mode 100644 src/main/java/ru/ulstu/user/UserSessionRepository.java create mode 100644 src/main/java/ru/ulstu/user/UserSessionService.java create mode 100644 src/main/java/ru/ulstu/user/UserUtils.java create mode 100644 src/main/resources/templates/error/403.html create mode 100644 src/main/resources/templates/error/404.html create mode 100644 src/main/resources/templates/error/500.html create mode 100644 src/main/resources/templates/login.html diff --git a/build.gradle b/build.gradle index e339ff5..11f98dc 100644 --- a/build.gradle +++ b/build.gradle @@ -37,10 +37,10 @@ dependencies { 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-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: '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: 'javax.xml.bind', name:'jaxb-api' implementation group: 'org.javassist', name:'javassist' diff --git a/data/db.mv.db b/data/db.mv.db index 8fe3c2fececf3c4da42c6f2c33870fa4d36b9582..185885d226a55b3495ff230c9d3cfce300bdcf45 100644 GIT binary patch literal 49152 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 delta 3713 zcmdT{&1)M+6yKF>;mED+CfL+*NfadxI4tq(?u@j%mqbnJcY^KKP9Q1C%xcyaRwQFf zA+%5%N^hkf8VaSCQVKm=ac({JRw$Gte?bfFIpi2{ptG}IP>giDFvVJ)jp(-4gk>Hg?3}nLD1FQI>ik~|LVla5kK_mwB;_) zWv6UZElL2%ckwraouPd3sozn^T}F_E6&YHc^0eA@yyPH*f82B+ud6|Cws^Hmc%H8C z-Q{Mp<><(8dCdSAo(;8D?K*YLs^I~Qv>SAJrPgflMhL^}SI4p+tSZY++p_1+}YJ{)#!Nauv$}8%WJOPpyzq1I?{m% zrbMA-xecB0J<*39_F=_+@D>3;po&Uc`)D`XE3{f^&H4Qt6b;!@b2fW0bEqFd9F~Jv zbLQAWcxb_5DIYQ3ekV{Je3d z@>hN`y5M9SrBO;ifDMAMfjrioyucn!9=O6sDm4<)0PpAKle9LU;LC^dijw_0N3wm2 zZkQ02%DsIXo8XgdPIP!C=uq3O!{2y^LxY)-hirOk_~ns5`c>tn%sW-;;dZ^H^VcW} z9@%6cPkk`nb82`_c_o2MDTYfqezNusoYC)M;Dltsq3pxlV3p1o3FlRuQULH67`m9$ zG%z^YgVEe&QY~pxOKe)eq#Rz6<--I^FiFvh9@C19k5tw;4ACj@QI@ineQ;(FWLIyU ziZB^dnYoS11SHc`7AM(br`B_bNdTY^%_ccNfS39so|vKDlX3@$uLW{O01=5I)&VX3~C9F2@Q}cF_E6o0N1gY zinr_7J_tFVX7+ZmWv3{3l}fN4EjU?vb*O{@LC1rcqj);QN}H(Cym1${Me zM9);wWkJsc!E*@UV&TpYcYeD2+1)QTI+aeMk;$k_W14^i6Rwfw7I3&B0f+3rvpcfV z?#L9>m=*ydyF7Mn{Yog2^N*28Cy>aA-6ZnYP$D-2iTra%iG1iwHmj7CWuZrK>D-j OV$K0pzyaApA^$f$GcTh6 diff --git a/src/main/java/ru/ulstu/configuration/Constants.java b/src/main/java/ru/ulstu/configuration/Constants.java new file mode 100644 index 0000000..1cd4983 --- /dev/null +++ b/src/main/java/ru/ulstu/configuration/Constants.java @@ -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; +} diff --git a/src/main/java/ru/ulstu/configuration/MvcConfiguration.java b/src/main/java/ru/ulstu/configuration/MvcConfiguration.java index 2599a53..799c414 100644 --- a/src/main/java/ru/ulstu/configuration/MvcConfiguration.java +++ b/src/main/java/ru/ulstu/configuration/MvcConfiguration.java @@ -20,6 +20,7 @@ import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; public class MvcConfiguration implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login"); registry.addViewController("/index"); registry.addViewController("/admin"); registry.addViewController("/editNews"); diff --git a/src/main/java/ru/ulstu/configuration/PasswordEncoderConfiguration.java b/src/main/java/ru/ulstu/configuration/PasswordEncoderConfiguration.java new file mode 100644 index 0000000..19370da --- /dev/null +++ b/src/main/java/ru/ulstu/configuration/PasswordEncoderConfiguration.java @@ -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(); + } +} diff --git a/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java b/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java new file mode 100644 index 0000000..f81bc87 --- /dev/null +++ b/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java @@ -0,0 +1,86 @@ +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(); + log.debug("Security enabled"); + http.authorizeRequests() + .antMatchers("/").permitAll() + .antMatchers("/login").permitAll() + .antMatchers("/index").permitAll() + .antMatchers("/news/*").permitAll() + .antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN) + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage("/login") + .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); + } + } +} diff --git a/src/main/java/ru/ulstu/model/User.java b/src/main/java/ru/ulstu/model/User.java new file mode 100644 index 0000000..7345316 --- /dev/null +++ b/src/main/java/ru/ulstu/model/User.java @@ -0,0 +1,65 @@ +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 roles; + + public User() { + roles = new HashSet<>(); + } + + 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 getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } +} diff --git a/src/main/java/ru/ulstu/model/UserRole.java b/src/main/java/ru/ulstu/model/UserRole.java new file mode 100644 index 0000000..6e0a45d --- /dev/null +++ b/src/main/java/ru/ulstu/model/UserRole.java @@ -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; + } +} diff --git a/src/main/java/ru/ulstu/model/UserRoleConstants.java b/src/main/java/ru/ulstu/model/UserRoleConstants.java new file mode 100644 index 0000000..cf78f0c --- /dev/null +++ b/src/main/java/ru/ulstu/model/UserRoleConstants.java @@ -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"; +} diff --git a/src/main/java/ru/ulstu/model/UserSession.java b/src/main/java/ru/ulstu/model/UserSession.java new file mode 100644 index 0000000..42e9838 --- /dev/null +++ b/src/main/java/ru/ulstu/model/UserSession.java @@ -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(); + } +} diff --git a/src/main/java/ru/ulstu/user/IpAddressResolver.java b/src/main/java/ru/ulstu/user/IpAddressResolver.java new file mode 100644 index 0000000..0ed8a60 --- /dev/null +++ b/src/main/java/ru/ulstu/user/IpAddressResolver.java @@ -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(); + } + +} diff --git a/src/main/java/ru/ulstu/user/UserNotFoundException.java b/src/main/java/ru/ulstu/user/UserNotFoundException.java new file mode 100644 index 0000000..73d70a5 --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserNotFoundException.java @@ -0,0 +1,7 @@ +package ru.ulstu.user; + +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/ulstu/user/UserRepository.java b/src/main/java/ru/ulstu/user/UserRepository.java new file mode 100644 index 0000000..bc44854 --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserRepository.java @@ -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 findOneByLoginIgnoreCase(String login); + + @EntityGraph(attributePaths = "roles") + User findOneWithRolesById(int id); + + @EntityGraph(attributePaths = "roles") + User findOneWithRolesByLogin(String login); +} diff --git a/src/main/java/ru/ulstu/user/UserRoleRepository.java b/src/main/java/ru/ulstu/user/UserRoleRepository.java new file mode 100644 index 0000000..79c3aa4 --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserRoleRepository.java @@ -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 { +} diff --git a/src/main/java/ru/ulstu/user/UserService.java b/src/main/java/ru/ulstu/user/UserService.java new file mode 100644 index 0000000..3bcb7bb --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserService.java @@ -0,0 +1,42 @@ +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.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.model.User; + +import java.util.Collections; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@Transactional +public class UserService implements UserDetailsService { + private final Logger log = LoggerFactory.getLogger(UserService.class); + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + 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())); + } +} diff --git a/src/main/java/ru/ulstu/user/UserSessionLoginHandler.java b/src/main/java/ru/ulstu/user/UserSessionLoginHandler.java new file mode 100644 index 0000000..dd62ef9 --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserSessionLoginHandler.java @@ -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); + } + } +} diff --git a/src/main/java/ru/ulstu/user/UserSessionLogoutHandler.java b/src/main/java/ru/ulstu/user/UserSessionLogoutHandler.java new file mode 100644 index 0000000..87979d3 --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserSessionLogoutHandler.java @@ -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); + } +} diff --git a/src/main/java/ru/ulstu/user/UserSessionRepository.java b/src/main/java/ru/ulstu/user/UserSessionRepository.java new file mode 100644 index 0000000..54e063a --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserSessionRepository.java @@ -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 findOneBySessionId(String sessionId); + + List findAllByLogoutTimeIsNullAndLoginTimeBefore(Date date); +} diff --git a/src/main/java/ru/ulstu/user/UserSessionService.java b/src/main/java/ru/ulstu/user/UserSessionService.java new file mode 100644 index 0000000..dd06ffa --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserSessionService.java @@ -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); + } +} diff --git a/src/main/java/ru/ulstu/user/UserUtils.java b/src/main/java/ru/ulstu/user/UserUtils.java new file mode 100644 index 0000000..27a453f --- /dev/null +++ b/src/main/java/ru/ulstu/user/UserUtils.java @@ -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; + } +} diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html index f1bcb60..f37ada4 100644 --- a/src/main/resources/templates/admin.html +++ b/src/main/resources/templates/admin.html @@ -5,12 +5,10 @@ --> - + diff --git a/src/main/resources/templates/error/403.html b/src/main/resources/templates/error/403.html new file mode 100644 index 0000000..9eb069c --- /dev/null +++ b/src/main/resources/templates/error/403.html @@ -0,0 +1,13 @@ + + + + + +
+
Доступ запрещён
+
Вернуться на главную
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/error/404.html b/src/main/resources/templates/error/404.html new file mode 100644 index 0000000..d599650 --- /dev/null +++ b/src/main/resources/templates/error/404.html @@ -0,0 +1,13 @@ + + + + + +
+
Страница не найдена
+
Вернуться на главную
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/error/500.html b/src/main/resources/templates/error/500.html new file mode 100644 index 0000000..b77a6cf --- /dev/null +++ b/src/main/resources/templates/error/500.html @@ -0,0 +1,13 @@ + + + + + +
+
Ошибка сервера
+
Вернуться на главную
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..52f3558 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,45 @@ + + + + + + +
+ +
+
+
+
+
+ +
+
+ +
+ + +
+
+
+
+
+ + \ No newline at end of file From 2e5880f1a69c541d2f1cf56152630bc566ac41a0 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 10 Mar 2022 10:10:29 +0400 Subject: [PATCH 2/7] #3 -- add default admin --- .../java/ru/ulstu/SeminarApplication.java | 15 +++++++++ src/main/java/ru/ulstu/model/User.java | 6 ++++ src/main/java/ru/ulstu/user/UserService.java | 32 ++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/main/java/ru/ulstu/SeminarApplication.java b/src/main/java/ru/ulstu/SeminarApplication.java index fb6201f..04b8e36 100644 --- a/src/main/java/ru/ulstu/SeminarApplication.java +++ b/src/main/java/ru/ulstu/SeminarApplication.java @@ -2,10 +2,25 @@ package ru.ulstu; import org.springframework.boot.SpringApplication; 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 public class SeminarApplication { + private final UserService userService; + + public SeminarApplication(UserService userService) { + this.userService = userService; + } + public static void main(String[] args) { SpringApplication.run(SeminarApplication.class, args); } + + @EventListener(ApplicationReadyEvent.class) + public void doSomethingAfterStartup() { + System.out.println("hello world, I have just started up"); + userService.initDefaultAdmin(); + } } diff --git a/src/main/java/ru/ulstu/model/User.java b/src/main/java/ru/ulstu/model/User.java index 7345316..5db0c57 100644 --- a/src/main/java/ru/ulstu/model/User.java +++ b/src/main/java/ru/ulstu/model/User.java @@ -39,6 +39,12 @@ public class User extends BaseEntity { roles = new HashSet<>(); } + public User(String login, String password, Set roles) { + this.login = login; + this.password = password; + this.roles = roles; + } + public String getLogin() { return login; } diff --git a/src/main/java/ru/ulstu/user/UserService.java b/src/main/java/ru/ulstu/user/UserService.java index 3bcb7bb..752f234 100644 --- a/src/main/java/ru/ulstu/user/UserService.java +++ b/src/main/java/ru/ulstu/user/UserService.java @@ -5,22 +5,32 @@ 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(UserRepository userRepository) { + public UserService(PasswordEncoder passwordEncoder, + UserRepository userRepository, + UserRoleRepository userRoleRepository) { + this.passwordEncoder = passwordEncoder; this.userRepository = userRepository; + this.userRoleRepository = userRoleRepository; } public User getUserByLogin(String login) { @@ -39,4 +49,24 @@ public class UserService implements UserDetailsService { .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))); + } + } } From e3d5e4e595e38a9cc6c0f9877a63413807bfa436 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 10 Mar 2022 10:14:42 +0400 Subject: [PATCH 3/7] #3 -- fix buttons visibility --- data/db.mv.db | Bin 49152 -> 45056 bytes .../configuration/TemplateConfiguration.java | 7 +++---- src/main/resources/templates/default.html | 11 +++++++++-- src/main/resources/templates/index.html | 8 ++++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/data/db.mv.db b/data/db.mv.db index 185885d226a55b3495ff230c9d3cfce300bdcf45..07fbdd344e7fa5609578eaf31df93e107f926799 100644 GIT binary patch literal 45056 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 literal 49152 zcmeHQYiwgzUboYDG>>K;-EBKF+jdV)yUl>tT-(>L0NQ3^XW~xcWa1>9ej!|6UuQ0L z?9{#vBR-(M7KFOcvcPV&f<*`cLLgC>J(YvopDr z9kbhxa#r2Rzp0RUA(lM>{$&mij z2xtT}0vZ90fJQ(gpb^jrXaqC@8Uc-fM&J`iU_tx;pEwUR0U7~~fJQ(gpb^jrXaqC@ z8Uc-fMnEH=5jbiD(8UHLB0RZs?`%ST5ll`WT z(fes}$ndKyKjio!&kq(qRQbW?2ZtZ(tSp{8W3YRfD!XT~Uw&@)HNp73u;i5~c$Hzi zO2r!PuCoM28SHF@vC7~av=|3d2~)Drk}Rw-4(3uBGhm@@m}aco>~;|A0gl>6HY+Y? zy$wIiBcpKHRJi5!}8)>VLzKk)1zO+JLTA}X`efly3 z!hR_Fn$ArR8o)InATu|NOrNep!kAIW{Ekm%R*(q}Lni}1C-VuF)!BWlW~5qz)PUnQ zC3TSFLs^_vAj~SX-A|QjrnTcV*V(TFyPj2TIC0|?yJ)nkOV*BmzUtH; zQ4-}vM3I!KtYvS)g%neZnM@}Y;DC81Eu9s*@qNnqy`?>_(R^5 zK}y9#-j2**&WUs;6biq0=62{}sLyz%`mS>7Rnq9YO7>l4`>yf~1Yy%nejyNHk``B_ zj-Es6n83z|3T&J=LR2p#LsT!W(!6?pl@vAds}!%EUu4va{Amhq+(-Wy?K9&kelYnV z%?}xVu=t_M5B5YlH<7j`($$GH&g&ByBU}wFh*0KaqCARv3@d6PfB% z(x@@rMu(ht{3|n6vy;JXl>Uj(QZL%hw2K1T+E~0gb@t z3jyiV1^#ysPoInV@$@ys(x=UI?c;W&Ps;iSeGTUSkMSNy`4R#3s^9Q&lP$`F|9>`r z{*8Mc2_=##Go8ssjz>;JPDV~e?u|@EPDjo}&PL8f?u&dO^2Nygkq06VM$ShrL@q|Y z6nQA}aO9EDxyeV#SFTrzYh-S?<4McK>uZ}Evx`x3xpbveUORc|+~i~A(W##mm9MOp zmS$IvnfZZsr zR!Yld|GB`s0ER`$T4||RS({y25qH!W%X8;Z2Jk3vx1gZt@a9zByjDOO;KkfSaq!KtTvKyD2nP2_cRyuONmPvRQ%W$eA2t01ApYhE5$`MKP*{VKKCH)7J(dLp zupkB&EFYc)zP$I_lR;#^3S?!e3aKOXEjI>PR2XG-0mV2_{H5W7s)AUF9tuU&s*omY z84Fkrfc+!@`{M%)gP_I-YmBUpFb<2$Fu{SnR`C^fVP&>bd3t%Zuu1!yAZ2Di#wX8$ z6$^(J^2~5nP%dy|F?kM5_=T}dI3)h4st^AIEk*|DPu{2K?0UI`?rtB))~{}663L}2 zg{!GzcHJBQX4t7@mi%{K3TGdY4MELPtp-!DcQjS1@!MH^eQiY+~%Vikil!V@kP6 zhi1wEZE#qJM9b@IgQYvg?Lq_m3Uv_+;_|WboRdZFgaQxbcQpAfc5; z{32xTg#%>H&ox?;mGL&M{Laida-fM{LKAmH6Gz+g{ObBzWj#~MCklo1!qeB1*(*k6 z!`xU5M02RR`E8&M00V(?ghKle5crdijsT4ONZJA;uhzZKhK(!2ZyqRq7)Uvz4N~Ii z>xrj|=}P(9Y9Uivyn4No%5D@E53=+MLQ)nfJdXgL_dYs2Fe;(CXUhYxjiaBd^`_B)fS3Vt79C{>3Me=-~^Q$VBM$@d=h>m7U=6 z8X=+3nfo4i=-5PP<=Sav@BMD?4bpqN_r~7Ky|;Sb+k3wE7K!#=@4bub+qn86p8Rs} zR_}K2UEF&GPxoHv-P(I$??oj4;od7z())wnJA2>Qdld<8_uj}DiAvMj1$28%KAQTt0$QMxnZsP1!f*UXP z-bT7taJ&WjdVeL;Fq%*q0R?aM{*qI$_tM_4@BJ#SDTyc#tw6SSDT$~c+Ix+j_uj$1 zAJBYn;1ccL`#R~}LeWTc8+d?NN=om|Xq@zZUoC@H{|?J_3pBk%>E>jztZ3406iR9T z20^y(v#hkVR{;S?jh_y`e{r$ZcD9MT)9G%LTC34&laA{Vyqxo5#KwDT$Mzhr+a^}c z-Elj%+uR~f!}a21!|8Z#wcDWitR0+n$WFJ7-&WgpV`SI$EQh$3-EFuX(sVlwVl~|7 zx{gP>$i32TxlK}Uqdd3Ku*kNx)wD>n)g)Hi?vh=r?RGmcpB6lCIGwIlLmIbwgEYFf z*X_i~e79peq|>U=$HckGNr(~4!&zh3ZCY*OwCR=8KzxAbbU*=dwj7@XyW8%7=sFtJ z5k%KqYn$MvjTU>vbGCO{?Kr78HG=o0t+t1R9u9zt79>!!KoRcLUC>7SUqI zvTcD!9e0~_8(!PBUBKC@xh=9Auo*CTR-BZ(O&hmw&|)moZG&&9{<$td<9TU)h>r2u zvhAvpM0syH9ZDQ+XRYgU@M*zR5~P&S+B}zH3bG-31o8rb+_f6uRGchXj*aG1p%Z)- z$^10Lsn^jE+Grp8IGJlBPX{~&Z*~zA)NWJZ16m;3t@$mG99Ehy^+IVtyX&(H zCU2rPmRU+EuSC%UhCkHOX$DPV$U>GV(`TKgV>P(|Gv%brmi4u7w3;?mKdzPF?oQkB zT(lIs*+Beb5}gLD1JiiunM0bCe-JfrRj2`NuPmf1GD+RvYFoQ5^qE11_RFUDN`{20 z(h5@&<6`M_xlBRqJmS;qG$Fu(D5|%hjhYi?4+O8MyfInj2FgPfR9(4Pv>;nlVrr1J z?G{Xm=Q^BzF43ObwA~uQ^@s(v@&$Z|WKbJPI*b88E67G1@O-;fgUZB6wP8W|2aFR# z5d?MHR+S4cv!sISTuK-Vxe1l(A_T1Nw6~#JU093{ZP}R~f^QTJ-?Ug%J779k(L#Fq zrGhbr8a}^25F%|!>nbz?iq^!x2fN5bidufvK7;W$tPW$0Ulh0uGs5dfaegDEnH2_^ z*BA?Ao?CBpSq_JV8YYz?)NsNsLIOmqcbx|BF<{jw59=`O%)|zUoM|Bhkac&+=o>z2 zG==nasX?Yzg}|CZj9{)=vlUUG5+r&~Y5RD$#wlWYguIYpZcw=Gh?7~`jnX<{Gn^Jx z;f`lPCxw7F9oEG`yreazHdgdsTwie=3j~q&VI6DBrJ9YqK!n^ZV132 z{53?_a=BMK)rMPj+AVZ?fDug=B*LIJ#E?g-ZY*H5ulSsm!Zt8-%f=?OVcakVwp#Qg zXgf9O;0zy1`3b;#e;mLDFqVxWlI%k<>T;m4w4bQCvLB{nAwDXRG|JJG(g8`n#n64Q zmI9bS)^}Q+uG5C#p}i1g)>Vs6fKXG(?4qwsOqv*R6du`lrWS(Vycl7%s5lE17Rm?P zzjFf}frl=&fq@?%UPy5jgC0pJFCT@}0(BNVQEdSo5-Od&EJU;{=;DI`m$2(n>*R=# zaxFkcAg+vInL+2n;GMR+4KBh+`O43PUI-i1#n&uJhuy8Z4c8(VO=umGUZK56TG?QH-h- zDFnJ~Y>XpggFgh7wvfx9kVNJxhzjCxmmi#d_GCDe2%Vd_T5})>4KH#Qw;!0EI~7)L zCU7qlvZmwrPK5BP{%q)6Bt}mC$Ln8v{vUpOI&}7bKltE-GshOWuq+TZ`(|D2aOC*QwVq*Yv+{#&**VCqMq z<4=AoG=bxZ=T^?|TN*I=*I)gw(D?(F2C%^VySX5MF9pyG0zUT(0(8(lUBmO454ukt zZP0z}F&%V&7lZCCKU`i1-Lu|Y|7Zj>0vZ90fJQ(gaA*Yf$KR_F^nU!mT}S+X>Imci zt${V=EdHOb|4$ipxq3V&oiT?kd(I`(w!eBjl{GSsvThjbkB@o-03QGUu8#lL@&Eop z2qhj#$N!6HEVeR3$N$q+FKQIEj{kpVQ^)@YOPc%$;K3kjRgHA~|D=xpXG`w<6&|C5 zV|D!hh*f%Hg7$|6g(~Y0_QlSQDe7>sp=_-Jja=tzjFi}Q9sfUMO~ajp*&>Qq$N%g2 z|BF9kOy|1J9)yOCvy|1J9b@aZD-q+Fl zA`n`Q)6~)XbUlHN-q+FlI(lD6@3Vy+GC*EO@3W;1q;jo#1toA~Gav;19;-al|V|35l#n|F1O_x;=Xf2cj) zu^l}R)8L!@tGoK=mCy}!>&9T$e7QxXb?~nK!G|LlnbkvoKO*?}fRm`pZxN_u<`gp3 zPfUE%jqri4;=!j6KVuB0vZ90fJWeRhk%U#5905G_j|I%xcpKMd& z|EYV#zj}7CUlb6p`anmt{Xaqce`xOb-%L(o8^869(Hr^6Cy(Bv?(v5!zR6$W#EHn{ zz$Sm#5OCe!?N6R;(1xJSODCdyYU0UIXyczTHSuI9{4XmP_iZe2?Bbc57}xvL5C?p7 z&`+29^i5ajuMgrAS^DdP_{wB5XR9+Q>5u>GgX$d5XK*S?&-myWANrY0@om5nA4xp@ z9X;c7muGzXKax0Z#)qdgP5qHXjCS;&MnEH=5zq)60Rqzh5BzV{h4$C~ClXdBT{}Ym zzbf9E%9p!}|3Bp8?2i20aPVFf@0GLam)G&m_o$cuGynh6@jv6f_>V%N{ChG?;KDv% zJW`(eX6QoTiw_MG@I7$8?cUwo?(e{WfxrWo-)gi6z6-CqW%~mL_!Q1zzXtyyA_czd zzk-*M0?C0_@Iwd;A_X{oDgV_w{aC$$*K>i0C2Lhf#sq-AgampHwz{ zNFF=q&m>|E?3TU+-*$YAz+orB#s^oZ6Jx*WlO*T=>H|DYT7BMjMvYszqm4QC6J;r( z6zXOj+JDvl>v;c_;*-b?IJQ~K{Bo1#bDJ8!@%V*8;rF!vzwK!M{|H8ux=4+HMnEH= z5%?@1ApQTq{|@~BYViI)k+$-+>=F9^_CVD1fdAhY9-WjvT7AEdhG~GDsW=u0sXn@C z0Q&y_((ylK{{I=A2mb&5ckz+`o{FjO;t%lu{h0vnmw(D<0#XBB;}A#Ng6DX1CY3N{ zFvsV3Cg3&S?UpkEGVJBkGZBy(VCg3?5fDu11lFm4zGR4;@d=$fnKSg)d~n;bf1;wQ z*b{YHQBPE8M|-@Z?N3JJ2PZ44CcYbq$%<~X)>>?B#S;Swa;YO;@uB^H?f-uk2B?}w hjetf#BcKsD5(K3GANb#a|8EEWe=-OE|0v@B{|}3i4^037 diff --git a/src/main/java/ru/ulstu/configuration/TemplateConfiguration.java b/src/main/java/ru/ulstu/configuration/TemplateConfiguration.java index ca7591b..f9ab6a7 100644 --- a/src/main/java/ru/ulstu/configuration/TemplateConfiguration.java +++ b/src/main/java/ru/ulstu/configuration/TemplateConfiguration.java @@ -7,11 +7,9 @@ package ru.ulstu.configuration; 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.Configuration; -import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect; import org.thymeleaf.spring5.SpringTemplateEngine; import org.thymeleaf.templateresolver.ITemplateResolver; @@ -19,10 +17,11 @@ import org.thymeleaf.templateresolver.ITemplateResolver; public class TemplateConfiguration { @Bean - public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { + public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver, SpringSecurityDialect sec) { final SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.addTemplateResolver(templateResolver); templateEngine.addDialect(new LayoutDialect()); + templateEngine.addDialect(sec); return templateEngine; } } diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index 819bd2a..5d978bd 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -5,8 +5,9 @@ --> - + app-name @@ -43,6 +44,12 @@ +
  • + + +
  • diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index 4b18b01..ec7f651 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -5,9 +5,9 @@ --> - +
    @@ -19,7 +19,7 @@
    -
    +
    From 6132e00c5ec4220551aa022757384045ddfa3b94 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 10 Mar 2022 17:15:04 +0400 Subject: [PATCH 4/7] #3 -- add login error page --- data/db.mv.db | Bin 45056 -> 57344 bytes .../ulstu/configuration/MvcConfiguration.java | 1 + .../configuration/SecurityConfiguration.java | 1 + src/main/resources/templates/login.html | 12 +----- src/main/resources/templates/loginError.html | 40 ++++++++++++++++++ 5 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 src/main/resources/templates/loginError.html diff --git a/data/db.mv.db b/data/db.mv.db index 07fbdd344e7fa5609578eaf31df93e107f926799..bd8aacfce738bf528d59c0e7f43e91b545f72221 100644 GIT binary patch literal 57344 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 R^Ys6i1qJ|f=M4bk{|_qj=7#_P delta 6106 zcmeHL+iw(A9G=}?pxZ!OEVbJbX|Xnien-kwUnq;WjfnD}{DjXp0Ro zZXXm;6C)fU#vt&ZA;y+qj-wJrBOck$fKP-OwY)S#EQKx)Vyu`-DKOjr(5QzQ2C z-l%1y4h;{FSr8!-8u`;zRZEQx40%?@b2QhmQ-{4nqXWZv60nNxjXUvV+nnlzq9~mS z+zLA4i63zvuCyvO zK^D4gfW(y}u3T~DiK`sGvU;Q@lc^@l=*}SAyQb}-GclG+fcRSR!LjxNTV)pTRoZ-& zX}U_34OeM;D5Iyw^W&plmM&Ta4EBy~LB47 zww+dog`%MxkHr(QxPCCmK(5HRZKNT8`3z%m@bQ-$6ToE&(BuS|(QVGq1e9*GgGv)X zcAe%!PC!mdfGZ{71Caoa#end!qwR6BM;{%1WEaZN%7Qs0mg#6tHaokNd(sgB&q0;jt*JFiDhU8%$MD$&%re!^XSg($Z#P&DNx(k%ECKbiJNjR%Vskw438R4wTujkh)$&j;hoRy#|u0WKZlIKLB z=2&awLTcbF6_n^KYm;(A8b;fcu)atvkW2L~9t+on0#PU`41V*hC#qn`eFWt;nvGsW zm_rVUTyyFySn5d;6JYsqX?}u!_Ctc%(&jEvF-;Ss3Pe{ULT!XqZ|mwZsj9OH)rwp} zP&|0yYH1($7vDemWsNA^uoA;rFf83HAf@YsrMs0po;m=XElTF4S%n_H4=@R?YW5|# zA@Szkn9>lhY^Yj5pi*zPd|SNqR;E7QxU#q#G}Lvc5-|mk5$m#E&K@5e3&u4qudel{ z@qYq;u3|5rgX^aD#xNe4O2&T2uTQnK{JcGw9_!n*;kl>x^sZUIwvUWVF2B43_f9XY z*xGdj-<;agw7uVd+*&(3G?1@q2%eVg>cKtJ%}wWa(g5i@_H_NUJEl}EII(I=Eg1%u z*6;CeV|CS|Tj&y$WlK%klRe7ZQ>X6IlfR_S$UPZ)2B|xvJt=vHe@`;c^SUP;N=wAV zzul$^G?Z}w(@=((hH^wTlq0I43^5I5h-)Z=OhXxB8p;vXP==U>Qp7csBCerqB-2oi zsD?5WXehoivFQ$8f*CsIB^dgzdI`Mu6D0DG4*VNQrRjSr=Ik}i_5Bt@x*M1pIuZdp zuqq5eav1YRQWpq`Lz57Zoe*id>RTf=vY?xR$6-+Sh!_7u%2g5I@m2XQ?DHl17A@`;^Xp~eU&!?a)0 zBw?q>@g}g!p`GHHmMy%C(vp0V(+;syA5-eScn>>ueg4A|b;~A7*eMvL?n1=E{Id?G zz>mSg!jy9`TLR zq4yU*Jo$NOrouW53s(yZH|N(a2n%;H)LG%8GG#n$uk? zw-J+5QQ085C~AkAyB;fVu(}f5V6_JZ>+Ny|>(#(u{d$|hdZ*N2Z5IY>w=h^Q1zq}@ zqRc(tq{PW@V*aL<$f2j|y(c=A7IEk)-=(5owP?SFR!f)+D1V)p{h@+78?v9#>eHm^ z3(pvqDvn?wRZ5>{#2IW>c3f_Sdj zAYQNSi&C#gJDGbuiim|${qH3lIe`qbp4X_hGo({(=ZI=MM^s!HGN`sQR7g{8XE05* zouLfXc8-win*xSR75Umuk#0cK*LI4y*JC3Av1sBR0&d_J*gd!t>IglMFEQjp%>U*I b$>wQ^Bq^Z=>7fR>rfus~f-2kE+7f>OD7(Sd diff --git a/src/main/java/ru/ulstu/configuration/MvcConfiguration.java b/src/main/java/ru/ulstu/configuration/MvcConfiguration.java index 799c414..9154347 100644 --- a/src/main/java/ru/ulstu/configuration/MvcConfiguration.java +++ b/src/main/java/ru/ulstu/configuration/MvcConfiguration.java @@ -21,6 +21,7 @@ public class MvcConfiguration implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login"); + registry.addViewController("/loginError"); registry.addViewController("/index"); registry.addViewController("/admin"); registry.addViewController("/editNews"); diff --git a/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java b/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java index f81bc87..8376327 100644 --- a/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java +++ b/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java @@ -53,6 +53,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { .and() .formLogin() .loginPage("/login") + .failureUrl("/loginError") .successHandler(authenticationSuccessHandler) .permitAll() .and() diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 52f3558..20cb039 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -13,11 +13,6 @@
    -
    @@ -30,12 +25,7 @@
    - - +
    diff --git a/src/main/resources/templates/loginError.html b/src/main/resources/templates/loginError.html new file mode 100644 index 0000000..1d08df3 --- /dev/null +++ b/src/main/resources/templates/loginError.html @@ -0,0 +1,40 @@ + + + + + + +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    +
    +
    + + \ No newline at end of file From 2ac1b3f3c1968c7783df2cfdc9822ef90fdf8c3c Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Thu, 10 Mar 2022 17:20:35 +0400 Subject: [PATCH 5/7] #3 -- fix headers for h2 --- data/db.mv.db | Bin 57344 -> 45056 bytes .../configuration/SecurityConfiguration.java | 8 +++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/data/db.mv.db b/data/db.mv.db index bd8aacfce738bf528d59c0e7f43e91b545f72221..2fc3b4de9e4cf466f101f7aa4ec2d385f69dc93c 100644 GIT binary patch delta 5216 zcmd^@-)|IE6vt<_t%a?n)Cg>WKwFBG?WD}_ncWAa0s``*P@!srlsi8bR=3+ywn&I( zDPnK?Bfrww85coJ%a)FTny63^^{{8Tu{^SwQp^-x|LvOn}KKONm z>M;3{Z*#-sh=w~592ja$N|N+catoZ9?5tm&S^a8d<~?h9y|i)7yKt@6CI1Hdw6*eI zm7`i~OYg0uENN0}Vowm}z(k?cumU~2GQGJ;7Jk&wZ;}MkyI{+eWAXqTyOOedt`4HV z)YZ4{AL!q^W4LeozTNxD|581dU&MpT!Y%ZCThA{q!M^Dgb%%T2hIg(UTA3P(CfN;w zk$kbC70=Pu1K`%_RBQi{=vI9z9#Jl)J1W0V7UagwuMgLw-=X?!CoG?Pb8qiOlG@j$ zrOksVwXaLbrw3BisXdc^PD<5GZJy+IoaAjE&zIy7cxyefE*~V5N&2BTR-JzKf_WV- zQeG=#`Qou3CcEL+Q(YT5j%7GlThg4D&U$VD4d<@0|nlP73qV9GFiLm`fDqxK617 z^KaA73or*cj&PlSGit#cu}R>u08|0sh~KOp|GU3wm1j{dWM*|=Q9sc&Z`~D-Y*AD<9*pYl z&B;zev8`gaRfA$*F8j4M#SBg{Gp1M*FG)cT-n@}ss0dvoZbi*i(nEB>t+5|*iZof} zMPh_Nqw20Qn7^wWj=JE*Z7XMWl>)nlV9ky%P8cB#;sn!mlwnR>Aq=6D19re5^!FC- z?=!5w_bBuY))t4D9T8F{HM; zG(kM~;CI``Wh-j=ALVXn?#6uYXE1zYMn^Jt?}mh>@FDNZ&@sVdcW_RNNZPm!ni{BEpiR@DK-wB<+&FRU7zGNXMS}8-HZAg3{%aGo@IUQ8 zEduw=*Y5CBC+W_%l#z{F&hF04o8P>7Gw^?WqML?UvGruQQg5uAt?5{J({41KdX2nTsoE{;s@<42D+$Gp zo65LP{*wqu1SA3y0f~S_Kq4R!kO)WwBmxoviGW1lT}NO}>i_S0A4mcu0ulj*mI^5rfOM>9}S_!!`R_Yg!9$ zm}_=(I;Mpi^?GYsF%@tWh(--tQRCs3vu;l(bxYOElIIlwC2u&jbiG|`O-Iqr{=?n{ z4~4O{@zD{JD5CP-SeO?ro>fEPXzY{?+%U!_XY0>P6YP{4+IYe zAy@F;;C;cv!TWao&eXf9Ql zLysjAmSP);SgfKYl~_zw)mU6FmF-fgoG?md-HOJ_W;A9-O-nTsC0kXrvTCSCvXqG1 zdMWxCN@W&u`N_bqAT+VbNJ*#CbJ+|kre@}|D0E?YW5FV65Ax#E0J&*7eV z40$RxI^ccf4fT6W*455R&xVxLO5GN{(=ub>AtsiPS3lRU_5Ekz$ zXIoeUC0$sLsCr}g4N$7-+kL-kP;ty4I;eTC;p#%DXL&BI{ z&gWpd>+7-Q^Q(GHSvZ$DAI~P1qw1xEVWeWw%v{unuM)YxI-;2rdEmCdz0lGZM-SRj zO&vu>2vOQkhgfwt*9ai;KEUHD>)zfuZT9?JG@qM$Qq|Pi%W;_1&m}V}scho>g#^Q4 zJ2I_HkOd9{C_fzy3YFEv$KVLELTGkzDVsZ&$E%FfeS)+Pr4dDWEnLdZW|zpSNRU6> z0>d5s`9)Y^rOyMf_XDuzP+-&fd`3ym%w8$3T#Uwz`6m|=G40Bei+Qiw?MxP!&5i(~ zZ{8}RL+KfW=)jRSdwyscUYkZi$z=pAe>jFmK~{cyYIxoBZUU77b-bKr1u+kh1S0s`(44DADez+c=t0wCmv(&hc|h?=K*}C2kYblE$DYh;h5W^(jGmi6f4LA(tYqe|C+P)*I4@#&9t3!PeCzOl zP#H$ojR>Y;Ww`L58xc%kJb2{9tAR1!QgQ#@K-K^J9^Y*6`8`jN#9PPxv%$BHKLIbD zJn>h-G2i~ZW8{@3@&g_h5%T#C9J=rC?i%x5xVRs(cmAmJ66$=n^U~I*J1=*>we@u8 zWfbbX(0L8guS4p4@Z__dS30kEUW0pIfTvr}bY9tdX6spa{d-$q2%*kbJFjkiaqCO) z!t0%H!qe}<^=~?_Bm4(me6I5?DC238<(rTR%3&|yGPXVg$yc^M2fw_WkOyvd;BA}_ z9^)6E1wtW#i+mOefSd5^O9*Z}+xafM_XW7V0`zsh&)*?5L1hRic)9Z(O2O81TfejQ zc}Qatp*&~>WP1&h2o;1n&*R^nSK;2balV%z3GLqc4C=fBMZ-(410FyuCZ+SuPy}`U zL@WbW|0>D#3efZ%rkj#UvO<$yhe9#!Uqq1YZ%I~M+Lr(VAT_ez|JL#Odc$5v&PKDn zj>`3Fy@8re3z_S7D~zmqt!Y~=yVY(Wv+Qg*P0OjRA-n3dB51{KwwzMCiu0Kp@T-Y7 z+70+yZ&*$kZ8|N}MviHWv60*kx3$ zm+Flcyx4*ZK!popsF^?!+^IM~AEqn^m95zgF&Okswn4Ab&1XTH*n%S!3c0gVe_W`uLMGyqapfI#y zHlWKKC=RXHaihbiW&%*S9~(~Dfj)se>p)A2pqI)ULJ~j^zNH^|Dh( zn_f2q3@tN)^6i=hx3A)2Ow?{bzd`j+wE;Am7uVMWw0O6yJ0d6HzBlbACJwi=+;%AV zxM0i)oJ(+REeB%?WCQCV;1^)XO|uF;6+sK8Z9(%f)3JVLuet9ayHbIM;6}U9M^L%} zd799t&^Mb7Y};yJ<^!~VXs7J9fcLP_d_FFO1~l5O2Jkg#X3nLw5)L?w z1A;#cOS|SZi69HSggZTD*KD&!8JG-C+}XUo#?^Yw!o!b_O6c8gEF)2qbO4~`WkWXf`Fgz!Lne$$RTBn(k8mO=0zvJD zS)$BKBq{54$|a-=sR-r6g)vygZmh%5YJJLue85=RyvWdfPj z81{IcN^h95Yzhk=7-END!3lB^IDoZ!)2`AP1Ed=6!wLv?B4Ry3PDUXxAertUqbvBZ z&}7`##sV2j6$H{0*a*ZmX|})$n1k5N$z>m%)+j|}96?^-VJc9l?1-QgoUF*31gBmc+DnL9IzLc##qL(`HPNMNQXT}#B*5FTyyZqhP!|c9XZh7c&>GkVc_HG zolTTPaIpF@#7+e!Gd_V~F#vN;)rK};is)bana{in6i88~06P%5 z!2kp1FT-~?%J<@=T6IcxqYjfEzz9udB!WP#vLz26x?!)Q-GNWLlF0@lZpqpNH;f8K zPgb*;1lo>8I`j-(O1UpU-@EGovVbuySR(N`6jog}3|Kr*lpQ_~QO8$=xYs;n7~;YjmI!pZ4Blus>(GlJq};(zn4U2k3>SA~aXPGa$*DRfg4LvH z|0*m_$k2xpDAyB)1Nr45<`&FFC?0Hh`h>RD8zl#EnaK#Hy#O-D0#PP33^w93m^pvax6RP=|FYtWof~hz%9| zlk+Qw-7Ci%h-_6Qa3P>e*2Zup-QX@kxh$kS$T*Rx3ao+%sLMyr{Li@G7lW_$=gT%Q zLbVmd-|6o^^UyxOa5Dz?d_MEcr|uc^!KeCzzR6%1?fZ`xKKS&He*d)Z;4j{O`|SgF zjs1&%x%1%GSZ8|6x3v$hMn^+i{*Ko90Q~&5PISxHiB1OLprFG=-JmUkxc&XEXU6N^JUxy;V#Xv zeD=93eqV4Byb0jLNX{&;JGF@qJgk@x$D$86os`-#7T3zPr#6cpS}&>d%d;02uO!nS z|HOPVclF9zBC>v^HPcQ%8NC?4`glG5l$F9j9zGlS5U!0D2ZiH*$If2)^?@WDJo|~? zzz+hGge3p5Gavcz9v^jl@J;T84_S4 z%*>_?HKrsIMil%jR4r!AW&)E?y{D7iUnWQ$G(&LFN zl%>WJ%4|HVE3+ba6M{^_qzlA?d~@=GV7=u9#LBxVggWyI6*Oj?OY z6A3k)jOwX)dRB*`0m0vz`o${tCAoehv9HPX=?#J}%Jr)zV*q#H)YNI8-|rjyQ=f0| ze}08r@BZ&Mk32F_1EWO5sg><(kyza43!HlY3EvUl-Nbh%Vj8;pD-i_0ok)!Qjg!AB z`K#T9?`!O$(^P_940=h@i%Bmfda>xmrk4sSi{{p&&;>JRXY;hM-pJMSui);-%EDoLE)t&JJf~a zk}%bW$YaK4aQq87^ri1Pts|xkd(PQ(*mIU&8}^*#H%32KhSEp#D?{m{^($&``K7qn zekIZWoMH^HX3Un^^>QDWB^At0&`s@HxjtWCi|`|KIGQkK9}qet-cwnbKBP_QrIbCS z?a+I3j%m8jH}-?azwCR&*EJC1T`5CMDbcQ!(v?bdrIG{$CW};>V2BV-iwInYe*@PM zhK&ql*a&R|tDbQNtDdDaT0KoEtVWuO)9PtL7Zb_-7~F`9{$1ExF%qX2m0q;>k((O7 z>MWgLSA$)X>}s-WiCryrwb`{oi)F=tcqGM}^irZ1YfLl7G;>TVjcM?^GNwoUCBN=h z{hB}KkB@1IF)cZ!*?!XxkF_x)Ho;viqw$1hyD3%G3`I!UR@6-yaXTuc!1+*2l~f@G zbF7$37`j)AZi!_k!0FL@mN3M82}|`TGc3h0+&5uS5VPD=6vUuVO3V~fN>nUU(JTW` ziSnOBKq4R!xMLCEi(}9Ke$!p48e#W5y_jO#rmiP;)_)$MabACkt$8)J<`w+^lV!HL zh1D`yq$#?eEwqv!w+>%?R%aoYH#>_D?bKB?G2y*vu_^lSJd7(aL{+u zQ`8JsQA@hNsOt2dQrTW~H6f2H>8=p2vMClUz&))k#?-c3*e}wrrXEKtis*c7B9E%7 ztF7HJdL$O`Tw$hWLsg{;GczJGg_)(8!pu`tVP+|&F!K~um|2P`%q+zeW}c!7Gfz>4 znWdP*OjBH8rYWv4vp1Q-%u-BY<|(N#!@^AdlL$xzBm%b&0{!~8r~j7({l8L)R?54m z|65E~(fjrP0a{a>C{5k!h*pjnf=UFR!gf#bXJ!zr|4)sdAiDTh;dj#2#gFyr;=$iM zp!<$pM;CXsa{4J#YUSi(s;KpDUn}RF#BBzm!Jx}tFo3^yUMu&k23#vo+=y1*Yc<$$ zb^Ha_s?lqS|x!Yl& zM8D5Y-yR>#51f~VjyuHA!E|T#!JMX~{(t){i6x~H0f~S_;FkgcuK#=bx2OMGp8g+K zDxh-f#;$$;XVL$rxmHBJ|0~1|x0K*tP2CUwar)IXZd%^wNW;)nLH{qmL;C-b@rQf& z6dVT5xqm!AU{Ar+_#!nHJmK?YkNU>G`h?H_=Ajdhiaw6m!}>0D;xBzCwrwnM%?1|n zfPP2K2Gk8(Ar+5s!~R28za08azhsLFrcFJucl49KCGf}SPx{h$z&+WA8DGS2`G4Sc z?l9v)m+b%#Yxi`|9kCsV9V^-nJYNVd->CEdHhk)_RfzaPfNuryI&ys>h8iEKO#}Nv z5Lwi3;taDZ)j4<44A-ub3?*s6bcTf6^X{MiprlHLDmEIX4{3|)!OrW2`nHXc<{a1cOh z26onG(kZf!{?GS*auxTfl|gL$s$$dzG|TP2ch%5kK*NL)xo6pBHJb z5|4P;#bYCw@(@o(@v#vsl~fJW-Fn1#!LVmwp~8n_v^US~R zcEr86heg~wb8u+Hy)%0UN8CGeYDmPrGvm7vaqs(sBJO=-Sj4>_iV^pGQ)mC4x|+Y? z+iT%-2j1}A{nncU&K-!qKRWo_fs*S?Zn?k8?l1d2>tODMe;ao0fapTobuQp7c$ecn z;KUuJ-=|h|Q4yWZJ!fCJhn|GbyTLv59)9mJXKC+bMebwO8;?PO15Wx*R;0d9c<)nJ zM272cff~(n;79@bB zlo)a9=BWgU>Bm!LF$Lb$I1&VVrY6ZT06b-rNPR4&sWczm13J)+o4%>W%Viw>Z~JcC ztcIwf8W6Bi+m)D#I0E4>?GceADCO-G0TcIDlyMq~fSIhgV*oY<@KoF&JW1a)lrrI0 zmcsi6+)@lUvrI^-(XLd2i~*KUv&a}=DIIvmD@CUxi9ah@cyzKJzy92Yxn%);9cC;QLSAi zn}$DsFCOkEm248@sV@v=zbYo404E+)=Y<_C51@BpK? zBJcbjm z&smirTc4zoFO~dTRPvY7y*pRJUaoEK5j6XGU6uY^Gk7QxLEv{UcUKlHk+Ds??|C#t|H(?(O6bX*KK>t#^ZH{<|1S^L|H(Hn_;%HO61O@aczTZ+Ahw%k02=@A zE~@`OA?p7p`%ZCt-`d2a@8qCU+_tU%b4x%nDnGaD5Pse)g{dBNCw*?0mVjZW=ZnYm ziKmhETLSnYqTDv|9v3Tdgh zMN7rDlwx7U2&RS`TQKS&Sh*g8C9%bp#LM~I#pP_@rtnc4@0lty!pt$hcrKSGmX3R2 zk^JK5MRH+;nmdY&5Tdl74zcQPt`UI8eSpVRhDYyedxWCxsdR(PJye4~9U|I^u>T(F zI}D)wbTlYZ|Gx}Js|;4fFqohxbF~i})Q$k6Z{8}RQvXMxxy3?pw6>kQu$szbmOvdI z{T>;VtHs)!Bx$f>JBe0E*E>>Vh@?}EcO44-2Z<%Byhu&AnE({Z*OrH R^Ys6i1qJ|f=M4bk{|_qj=7#_P diff --git a/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java b/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java index 8376327..ea8dc5d 100644 --- a/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java +++ b/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java @@ -40,14 +40,12 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { - http.csrf() - .disable(); + http.csrf().disable(); + http.headers().frameOptions().disable(); log.debug("Security enabled"); http.authorizeRequests() .antMatchers("/").permitAll() - .antMatchers("/login").permitAll() - .antMatchers("/index").permitAll() - .antMatchers("/news/*").permitAll() + .antMatchers("/login", "/index", "/news/*", "/h2-console/*", "/h2-console").permitAll() .antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN) .anyRequest().authenticated() .and() From 76ab0ddf11c7650858f355daf6508ee627af0a6a Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 11 Mar 2022 09:10:48 +0400 Subject: [PATCH 6/7] #3 -- fix routing --- data/db.mv.db | Bin 45056 -> 69632 bytes .../configuration/SecurityConfiguration.java | 2 +- .../ru/ulstu/controller/IndexController.java | 2 +- .../ru/ulstu/controller/NewsController.java | 14 ++++-- .../ru/ulstu/repository/NewsRepository.java | 3 ++ .../java/ru/ulstu/service/NewsService.java | 4 ++ src/main/resources/public/css/main.css | 2 +- src/main/resources/templates/default.html | 2 +- src/main/resources/templates/editNews.html | 2 +- src/main/resources/templates/index.html | 6 +-- src/main/resources/templates/news.html | 41 ++++++++++++------ src/main/resources/templates/viewNews.html | 25 +++++++++++ 12 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 src/main/resources/templates/viewNews.html diff --git a/data/db.mv.db b/data/db.mv.db index 2fc3b4de9e4cf466f101f7aa4ec2d385f69dc93c..232741a16331eb07abff8824e1e6378d5adb1060 100644 GIT binary patch literal 69632 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 literal 45056 zcmeHQU2r2;R+b$rB%aJ9PBO{NWs~fts00$WSSnQn3W{1N9)MIxcC*PQyAM1-v0KQgA`d(P57aIcm1mZs z!gv3=+p;8kl9^;Ov}a1)x4ZAX=X~efbMNgw=ib@Lbh2D;R(^HT=Esuz>&~RAC`z(& zvD3iivX!i~9NTlMld4(E*p^*$GTCIU*;==~Nj16YwAya7!5*yD9j|iHX-!tmjH)VC zZ9EqJMF>O)L6g5K6jMQCQ+E6N##YC8ilQMaW7ud|@$JWfe{75mu+s zX*<=Nx8gf;=pg~>Ap!o8PnIFb$_jOnWQA!`!X!~8>eU{7fc9{Ld}Z26Us%D65Pb)! z%qN>0PGhNMH`;c^gJVkj7IxLM%XKHE>|=>~vwX(h@Xr^W+Wn!2vclm-0%F?w&$8P> zo8TJ59Ab9d=XI#*LHDIL4!SS$8-wnP{L&$3NB#VMdLVt`{y_S8{b{|o{<0S4KW*;3 z&oKt9dE2wMJ-h?VvQ8M7okq1e*IY}fJ*C;UeQ!t@TF>n&zsXtYrBvMHd}MlajvARs zv#fnyXeCRzsks74oL!!pD^4d;iDGGWxl~wOU0j$eBuFlwC({e_rKQDOaegWESa5MQ zpCD%!i)V6+7s%Hs>d1X-95-3udv3VB^9HANoW|C_nFrNZJaH0}vS)EXj9)-n_@3xGWW!2Wz>Pl)+o zK{hCNsc;IEOPG=z$Cbijskkuj-xrtX7ETp$u^+zTiL<#<>4}BK{OWA3G#fmb>5;_P zZ4kw*AUDKs2=1^-WcrJON&!qj24fQt!c#+qP}-XoqpA=+y!@f)XMq0L?O>-O*jb)0 zo?9;L$g_NUHKV4_oXVfp3g)t+KVe!{PF3==ilwcx_OLpHO^!U}^bT

    d41tbr=~5 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 Lm8(}{{lEVMz8BQP diff --git a/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java b/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java index ea8dc5d..f893257 100644 --- a/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java +++ b/src/main/java/ru/ulstu/configuration/SecurityConfiguration.java @@ -45,7 +45,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { log.debug("Security enabled"); http.authorizeRequests() .antMatchers("/").permitAll() - .antMatchers("/login", "/index", "/news/*", "/h2-console/*", "/h2-console").permitAll() + .antMatchers("/login", "/index", "/news/**", "/h2-console/*", "/h2-console").permitAll() .antMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN) .anyRequest().authenticated() .and() diff --git a/src/main/java/ru/ulstu/controller/IndexController.java b/src/main/java/ru/ulstu/controller/IndexController.java index d6f2fc0..9774e53 100644 --- a/src/main/java/ru/ulstu/controller/IndexController.java +++ b/src/main/java/ru/ulstu/controller/IndexController.java @@ -22,7 +22,7 @@ public class IndexController { @GetMapping("/") public String index(Model model) { - model.addAttribute("news", newsService.getAll()); + model.addAttribute("news", newsService.getLast()); return "index"; } } diff --git a/src/main/java/ru/ulstu/controller/NewsController.java b/src/main/java/ru/ulstu/controller/NewsController.java index 3de7221..111a1ca 100644 --- a/src/main/java/ru/ulstu/controller/NewsController.java +++ b/src/main/java/ru/ulstu/controller/NewsController.java @@ -13,12 +13,14 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; import ru.ulstu.model.News; import ru.ulstu.service.NewsService; import javax.validation.Valid; @Controller +@RequestMapping("news") public class NewsController { private final NewsService newsService; @@ -26,6 +28,12 @@ public class NewsController { this.newsService = newsService; } + @GetMapping("/news") + public String index(Model model) { + model.addAttribute("news", newsService.getAll()); + return "news"; + } + @GetMapping("/editNews/{newsId}") public String editNews(@PathVariable(value = "newsId") Integer id, Model model) { model.addAttribute("news", (id != null && id != 0) ? newsService.getById(id) : new News()); @@ -35,7 +43,7 @@ public class NewsController { @GetMapping("/news/{newsId}") public String viewNews(@PathVariable(value = "newsId") Integer id, Model model) { model.addAttribute("news", id != null ? newsService.getById(id) : new News()); - return "news"; + return "viewNews"; } @PostMapping("saveNews") @@ -45,12 +53,12 @@ public class NewsController { } newsService.save(news); - return "redirect:/"; + return "redirect:/news/news"; } @GetMapping("deleteNews/{newsId}") public String delete(@PathVariable(value = "newsId") Integer id) { newsService.delete(id); - return "redirect:/"; + return "redirect:/news/news"; } } diff --git a/src/main/java/ru/ulstu/repository/NewsRepository.java b/src/main/java/ru/ulstu/repository/NewsRepository.java index 3aaf6bf..6bd607a 100644 --- a/src/main/java/ru/ulstu/repository/NewsRepository.java +++ b/src/main/java/ru/ulstu/repository/NewsRepository.java @@ -3,5 +3,8 @@ package ru.ulstu.repository; import org.springframework.data.jpa.repository.JpaRepository; import ru.ulstu.model.News; +import java.util.List; + public interface NewsRepository extends JpaRepository { + List findFirst3ByOrderByDateDesc(); } diff --git a/src/main/java/ru/ulstu/service/NewsService.java b/src/main/java/ru/ulstu/service/NewsService.java index 293d062..3f743f0 100644 --- a/src/main/java/ru/ulstu/service/NewsService.java +++ b/src/main/java/ru/ulstu/service/NewsService.java @@ -46,4 +46,8 @@ public class NewsService { public void delete(Integer id) { newsRepository.deleteById(id); } + + public List getLast() { + return newsRepository.findFirst3ByOrderByDateDesc(); + } } diff --git a/src/main/resources/public/css/main.css b/src/main/resources/public/css/main.css index 359a34c..9b299ad 100644 --- a/src/main/resources/public/css/main.css +++ b/src/main/resources/public/css/main.css @@ -29,6 +29,6 @@ color: black; } -.link-dark, .link-dark:visited, .link-dark:focus { +.link-dark, .link-dark:visited, .link-dark:focus, .link-dark:any-link { color: black; } \ No newline at end of file diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index 5d978bd..3146bbf 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -33,7 +33,7 @@