I created a usable example to show that you might use @Order
to match the paths.
A SecurityFilterChain
with a smaller @Order number is matched before a SecurityFilterChain with a larger @Order number.
Following this pattern and matching the paths, I allowed "Admin", "Editor" and "User" to access certain paths respectively, and denied as necessary.
The config file was here.
@Bean
@Order(400)
public SecurityFilterChain securityFilterChainUser(HttpSecurity http) throws Exception{
String[] matchedPaths = {
"/user",
"/user/**"
};
http
.csrf(csrf -> csrf.disable())
.securityMatcher(
matchedPaths
)
.authorizeHttpRequests(request ->
request
.requestMatchers(matchedPaths)
.hasAnyRole("ADMIN", "EDITOR", "USER")
.anyRequest()
.authenticated()
)
.sessionManagement(session -> session
.sessionConcurrency((concurrency) -> concurrency
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
)
)
.logout(logout -> logout.logoutUrl("/logout"));
return http.build();
}
@Bean
@Order(500)
public SecurityFilterChain securityFilterChainUserDeny(HttpSecurity http) throws Exception{
String[] matchedPaths = {
"/user",
"/user/**"
};
http
.csrf(csrf -> csrf.disable())
.securityMatcher(
matchedPaths
)
.authorizeHttpRequests(request ->
request
.requestMatchers(matchedPaths)
.denyAll()
)
.sessionManagement(session -> session
.sessionConcurrency((concurrency) -> concurrency
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
)
)
.logout(logout -> logout.logoutUrl("/logout"));
return http.build();
}
@Bean
@Order(600)
public SecurityFilterChain securityFilterChainEditor(HttpSecurity http) throws Exception{
String[] matchedPaths = {
"/editor",
"/editor/**"
};
http
.csrf(csrf -> csrf.disable())
.securityMatcher(
matchedPaths
)
.authorizeHttpRequests(request ->
request
.requestMatchers(matchedPaths)
.hasAnyRole("ADMIN", "EDITOR")
.anyRequest()
.authenticated()
)
.sessionManagement(session -> session
.sessionConcurrency((concurrency) -> concurrency
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
)
)
.logout(logout -> logout.logoutUrl("/logout"));
return http.build();
}
In my example, I used Microsoft SQL Server.
I ran the SQL statement before starting my example application.
CREATE DATABASE springboothasrole COLLATE Latin1_General_100_CS_AI_WS_SC_UTF8;
The PowerShell script to start was here.
$env:processAppDebugging="true";
$env:processAppDataSourceDriverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver";
$env:processAppDatabasePlatform="org.hibernate.dialect.SQLServer2012Dialect";
$env:processAppDataSourceUrl="jdbc:sqlserver://localhost;databaseName=springboothasrole;encrypt=false;trustServerCertificate=false;"
$env:processAppDataSourceUsername="sa";
$env:processAppDataSourcePassword="Your_Password";
./mvnw spring-boot:run;
I opened the browser and tested different paths, e.g.
http://127.0.0.1:8080
http://127.0.0.1:8080/admin
http://127.0.0.1:8080/editor
http://127.0.0.1:8080/user
The permissions were what I expected according to hasAnyRole
in the example. Please see if this helps.