@@ -17,6 +17,13 @@ | |||
<dependencies> | |||
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 --> | |||
<dependency> | |||
<groupId>org.thymeleaf.extras</groupId> | |||
<artifactId>thymeleaf-extras-springsecurity5</artifactId> | |||
<version>3.0.4.RELEASE</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web --> | |||
<dependency> |
@@ -1,19 +1,41 @@ | |||
package cordon.bleu; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.security.authentication.AuthenticationManager; | |||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |||
import org.springframework.security.core.Authentication; | |||
import org.springframework.security.core.context.SecurityContextHolder; | |||
import org.springframework.security.web.authentication.WebAuthenticationDetails; | |||
import org.springframework.stereotype.Controller; | |||
import org.springframework.ui.Model; | |||
import org.springframework.web.bind.annotation.GetMapping; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import javax.servlet.http.HttpServletRequest; | |||
@Controller | |||
@RequestMapping("/") | |||
public class HomeController { | |||
@Autowired | |||
AuthenticationManager authenticationManager; | |||
@GetMapping | |||
public String showHome(Model model) { | |||
public String showHome(Model model, HttpServletRequest request) { | |||
authWithAuthManager(request,"Alice","supercoder"); | |||
model.addAttribute("message", "Spring Travel Agency"); | |||
return "home"; | |||
} | |||
public void authWithAuthManager(HttpServletRequest request, String username, String password) { | |||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password); | |||
authToken.setDetails(new WebAuthenticationDetails(request)); | |||
Authentication authentication = authenticationManager.authenticate(authToken); | |||
SecurityContextHolder.getContext().setAuthentication(authentication); | |||
} | |||
} |
@@ -2,7 +2,11 @@ package cordon.bleu.config; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.security.authentication.AuthenticationManager; | |||
import org.springframework.security.config.BeanIds; | |||
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.configuration.EnableWebSecurity; | |||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | |||
import org.springframework.security.crypto.factory.PasswordEncoderFactories; | |||
@@ -10,6 +14,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; | |||
@Configuration | |||
@EnableWebSecurity | |||
@EnableGlobalMethodSecurity(prePostEnabled = true) | |||
public class SecurityConfig extends WebSecurityConfigurerAdapter { | |||
@Bean | |||
@@ -27,7 +32,26 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { | |||
.and() | |||
.withUser("Alice") | |||
.password("{noop}supercoder") | |||
.roles("ADMIN") | |||
.and() | |||
.withUser("Christy") | |||
.password("{noop}supercoder") | |||
.roles("ADMIN"); | |||
} | |||
@Override | |||
protected void configure(HttpSecurity http) throws Exception { | |||
http.authorizeRequests() | |||
.antMatchers("/").permitAll() | |||
.and() | |||
.formLogin(); | |||
} | |||
@Bean(name = BeanIds.AUTHENTICATION_MANAGER) | |||
@Override | |||
public AuthenticationManager authenticationManagerBean() throws Exception { | |||
return super.authenticationManagerBean(); | |||
} | |||
} |
@@ -12,6 +12,7 @@ import org.springframework.web.context.WebApplicationContext; | |||
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; | |||
import org.springframework.web.servlet.config.annotation.EnableWebMvc; | |||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | |||
import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect; | |||
import org.thymeleaf.spring5.SpringTemplateEngine; | |||
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; | |||
import org.thymeleaf.spring5.view.ThymeleafViewResolver; | |||
@@ -57,6 +58,7 @@ public class WebConfig implements WebMvcConfigurer { | |||
// SpringTemplateEngine automatically applies SpringStandardDialect and | |||
// enables Spring's own MessageSource message resolution mechanisms. | |||
SpringTemplateEngine templateEngine = new SpringTemplateEngine(); | |||
templateEngine.addDialect(new SpringSecurityDialect()); | |||
templateEngine.setTemplateResolver(templateResolver()); | |||
// Enabling the SpringEL compiler with Spring 4.2.4 or newer can | |||
// speed up execution in most scenarios, but might be incompatible |
@@ -42,6 +42,11 @@ public class User { | |||
@Size(min = 2, message = "{user.lastname}") | |||
private String lastName; | |||
@Size(min = 5, message = "{user.password}") | |||
private String passWord; | |||
private Boolean enabled; | |||
@OneToOne(mappedBy = "user", fetch = FetchType.LAZY) | |||
private UserRole userRole; | |||
@@ -2,6 +2,7 @@ package cordon.bleu.user; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.http.HttpStatus; | |||
import org.springframework.security.access.prepost.PreAuthorize; | |||
import org.springframework.stereotype.Controller; | |||
import org.springframework.ui.Model; | |||
import org.springframework.validation.BindingResult; | |||
@@ -38,12 +39,16 @@ public class UserController { | |||
return "user/list"; | |||
} | |||
@PreAuthorize("hasRole('ADMIN')") | |||
@GetMapping("/add") | |||
public String add (Model model){ | |||
User user = userService.getNewUser(); | |||
user.setUserRole(new UserRole()); | |||
model.addAttribute("user", new User()); | |||
return "user/edit"; | |||
} | |||
@PreAuthorize("hasRole('ADMIN')") | |||
@PostMapping("/edit") | |||
public String processForm(@Valid @ModelAttribute User user, BindingResult bindingResult){ | |||
if (bindingResult.hasErrors()){ |
@@ -1,9 +1,11 @@ | |||
package cordon.bleu.user; | |||
import org.springframework.security.access.prepost.PreAuthorize; | |||
import org.springframework.stereotype.Service; | |||
import java.util.List; | |||
@PreAuthorize("hasRole('ADMIN')") | |||
public interface UserService { | |||
public List<User> getAll(); | |||
@@ -21,4 +23,6 @@ public interface UserService { | |||
public void delete(User user); | |||
public User getNewUser(); | |||
} |
@@ -57,5 +57,10 @@ public class UserServiceImpl implements UserService { | |||
public void delete(User user) { | |||
userRepository.delete(user); | |||
} | |||
@Override | |||
public User getNewUser(){ | |||
return new User(); | |||
} | |||
} | |||
@@ -5,4 +5,5 @@ tour.future = Start date has to be in the future | |||
tour.duration = Tour duration should be between 7 and 21 days | |||
user.email = Your email is not correct | |||
user.firstname = Your firstname should be at least 2 characters | |||
user.lastname = your lastname should be at least 2 characters | |||
user.lastname = your lastname should be at least 2 characters | |||
user.password = Password should have at least 5 characters |
@@ -1,5 +1,5 @@ | |||
<!DOCTYPE html> | |||
<html lang="en" xmlns:th="http://www.thymeleaf.org"> | |||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml"> | |||
<head> | |||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous"> | |||
<meta charset="UTF-8"> | |||
@@ -12,14 +12,19 @@ | |||
| | |||
\ _ / | |||
-= (_) =- | |||
/ \ _\/_ | |||
| //o\ _\/_ | |||
_____ _ __ __ ____ _ | __/o\\ _ | |||
=-=-_-__=_-= _=_=-=_,-'|"'""-|-,_ | |||
=- _=-=- -_=-=_,-" | | |||
/ \ \/ | |||
| //o\ \/ | |||
__ _ _ _ __ _ | _/o\\ _ | |||
=-=--=-= ==-=,-'|"'""-|-, | |||
=- =-=- -=-=_,-" | | |||
jgs =- =- -=.--" | |||
</pre> | |||
<p><a th:href="@{/tour}">Tours</a> <a th:href="@{/user}"> | User</a></p> | |||
<p> | |||
<a class="btn btn-primary" th:href="@{/tour}">Tours</a> | |||
<a sec:authorize="hasRole('ROLE_ADMIN')" class="btn btn-primary" th:href="@{/user}">Users</a></p> | |||
<p>Active user: <span sec:authentication="name"></span></p> | |||
<p sec:authorize="isAuthenticated()">Roles: <span sec:authentication="principal.authorities">[ROLE_USER, ROLE_ADMIN]</span></p> | |||
</div> | |||
</body> | |||
</html> |
@@ -1,5 +1,5 @@ | |||
<!DOCTYPE html> | |||
<html lang="en" xmlns:th="http://www.thymeleaf.org"> | |||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous"> | |||
@@ -28,7 +28,7 @@ | |||
<p> | |||
<a th:href="@{/tour}" class="btn btn-primary ">← Tours</a> | |||
<a th:href="@{/tour/info/edit/} + ${tourId}" class="btn btn-primary ">Edit</a> | |||
<a sec:authorize="hasRole('ROLE_ADMIN')" th:href="@{/tour/info/edit/} + ${tourId}" class="btn btn-primary ">Edit</a> | |||
</p> | |||
</fieldset> | |||
</form> |
@@ -1,5 +1,5 @@ | |||
<!DOCTYPE html> | |||
<html lang="en" xmlns:th="http://www.thymeleaf.org"> | |||
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous"> | |||
@@ -37,8 +37,8 @@ | |||
<td th:text="${tour.allInclusive} ? 'yes' : 'no'"></td> | |||
<td> | |||
<a class="btn btn-sm btn-primary" th:href="@{/tour/info/} + ${tour.id}">Info</a> | |||
<a class="btn btn-sm btn-primary" th:href="@{/tour/edit/} + ${tour.id}">Edit</a> | |||
<a class="btn btn-sm btn-danger" th:href="@{/tour/delete/} + ${tour.id}">Delete</a> | |||
<a class="btn btn-sm btn-primary" sec:authorize="hasRole('ROLE_ADMIN')"th:href="@{/tour/edit/} + ${tour.id}">Edit</a> | |||
<a class="btn btn-sm btn-danger" sec:authorize="hasRole('ROLE_ADMIN')" th:href="@{/tour/delete/} + ${tour.id}">Delete</a> | |||
</td> | |||
</tr> | |||
</tbody> | |||
@@ -54,7 +54,7 @@ | |||
<a class="btn btn-primary" th:href="@{/}">← Home</a> | |||
<a class="btn btn-primary" th:href="@{/tour/add}">+</a> | |||
<a class="btn btn-primary" sec:authorize="hasRole('ROLE_ADMIN')" th:href="@{/tour/add}">+</a> | |||
</div> | |||
</body> |
@@ -35,6 +35,17 @@ | |||
<div class="alert alert-danger" role="alert" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"></div> | |||
<input type="text" th:field="*{lastName}"> | |||
</div> | |||
<div class="mb-3"> | |||
<label>Password</label> | |||
<div class="alert alert-danger" role="alert" th:if="${#fields.hasErrors('passWord')}" th:errors="*{passWord}"></div> | |||
<input type="password" class="form-control" th:field="*{passWord}"> | |||
</div> | |||
<div class="mb-3"> | |||
<input class="form-check-input" type="checkbox" th:field="*{enabled}" /> | |||
<label class="form-check-label">Enabled</label> | |||
</div> | |||
<div class="mb-3"> | |||
<label>Role</label> | |||
<select name="role" class="form-control"> |