Customizing the filter chain
Spring Security uses the concept of the filter chain. This is a sophisticated interceptor that is placed in front of the controllers.
The filter chain is configured by creating a bean of type SecurityFilterChain.
Spring Security provides a fluid API (see builder pattern) that centralizes the application rules.
Access restricted to authenticated users
|
Note
|
For the demo, we are creating a Spring Boot application with some Spring MVC views, backed with the Thymeleaf templating tool. We will also need some extra support for Spring Security. |
1implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6") 1<!DOCTYPE html>
2<html lang="en"
3 xmlns:th="http://www.thymeleaf.org">
4<head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>Home</title>
8</head>
9<body>
10 <h1>Welcome to the Public Home Page</h1>
11 <ul>
12 <li><a th:href="@{/secure}">Go to Secure Page</a></li>
13 <li><a th:href="@{/login}">Login</a></li>
14 </ul>
15</body>
16</html> 1<!DOCTYPE html>
2<html lang="en"
3 xmlns:th="http://www.thymeleaf.org"
4 xmlns:sec="http://www.w3.org/1999/xhtml">
5<head>
6 <meta charset="UTF-8">
7 <meta name="viewport" content="width=device-width, initial-scale=1.0">
8 <title>Secure</title>
9</head>
10<body>
11 <h1>Welcome to the Secure Page</h1>
12 <p>You are logged in as <span sec:authentication="name"></span></p>
13 <p>You are granted these authorities: <span sec:authentication="principal.authorities"></span></p>
14 <ul>
15 <li><a th:href="@{/}">Back to Home</a></li>
16 <li><a th:href="@{/logout}">Logout</a></li>
17 </ul>
18</body>
19</html> 1package fr.emse.tbpwme3.demo.spring.security;
2
3import org.springframework.context.annotation.Bean;
4import org.springframework.context.annotation.Configuration;
5import org.springframework.security.config.Customizer;
6import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7import org.springframework.security.web.SecurityFilterChain;
8
9@Configuration
10public class SecurityConfig {
11
12 @Bean
13 public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
14 http
15 .authorizeHttpRequests(c -> c.requestMatchers("/secure")
16 .authenticated())
17 .authorizeHttpRequests(c -> c.anyRequest()
18 .permitAll())
19 .formLogin(Customizer.withDefaults());
20 return http.build();
21 }
22
23}The code above describes the application’s filter chain.
-
Access to the
/securepath requires authentication. -
Access to all other paths is unrestricted.
-
Authentication is provided via the login form.
1package fr.emse.tbpwme3.demo.spring.security;
2
3import org.springframework.stereotype.Controller;
4import org.springframework.web.bind.annotation.GetMapping;
5
6@Controller
7public class HomeController {
8
9 @GetMapping("/")
10 public String home() {
11 return "home";
12 }
13
14 @GetMapping("/secure")
15 public String secure() {
16 return "secure";
17 }
18
19}Access restricted to users with the required authorities
When the application starts, we create two users, user with the USER role and admin with the USER and ADMIN roles.
1@Bean
2DataSource dataSource() {
3 return new EmbeddedDatabaseBuilder()
4 .setType(H2)
5 .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
6 .build();
7}
8
9@Bean
10UserDetailsManager users(DataSource dataSource) {
11 UserDetails user = User.builder()
12 .username("user")
13 .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
14 .roles("USER")
15 .build();
16 UserDetails admin = User.builder()
17 .username("admin")
18 .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
19 .roles("USER", "ADMIN")
20 .build();
21 JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
22 users.createUser(user);
23 users.createUser(admin);
24 return users;
25}|
Note
|
Both user and admin have password for password.
|
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(c -> c.requestMatchers("/admin")
.hasAuthority("ROLE_ADMIN"))
.authorizeHttpRequests(c -> c.requestMatchers("/secure")
.authenticated())
.authorizeHttpRequests(c -> c.anyRequest()
.permitAll())
.formLogin(Customizer.withDefaults());
return http.build();
}We update the filter chain so that the /admin path requires the ADMIN role.
|
Note
|
hasAuthority("ROLE_ADMIN") is the same as hasRole("ADMIN").
|