Master Spring Security: Custom Request Interception & Login Page
This tutorial demonstrates how to configure Spring Security to selectively intercept requests, permit static resources, enforce role‑based access, and replace the default login page with a custom Thymeleaf view, complete with code examples and screenshots.
Custom Request Interception
By default, Spring Security intercepts every request, including static resources, which is often undesirable. First, create a static resource directory and configure Spring Boot to serve it under a custom prefix.
<code>spring:
mvc:
static-path-pattern: /resources/**
</code>After adding Spring Security back to the project, requests to /resources/** will be redirected to the login page unless explicitly permitted.
<code>@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**")
.permitAll();
http.formLogin();
}
}
</code>To allow static resources while protecting all other endpoints, place the antMatchers rule before the anyRequest rule:
<code>@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/resources/**")
.permitAll();
http.authorizeRequests()
.anyRequest()
.authenticated();
http.formLogin();
}
</code>Reversing the order triggers the following exception:
<code>Caused by: java.lang.IllegalStateException: Can't configure antMatchers after anyRequest
at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-5.3.12.jar:5.3.12]
</code>Define two sample controllers:
<code>@RestController
@RequestMapping("/demos")
public class DemoController {
@GetMapping("home")
public Object home() { return "demos home"; }
}
@RestController
@RequestMapping("/api")
public class ApiController {
@GetMapping("/{id}")
public Object get(@PathVariable("id") Integer id) {
return "获取 - " + id + " - 数据";
}
}
</code>Configure role‑based access so that /demos/** requires the USERS role and /api/** requires the ADMIN role:
<code>http.authorizeRequests().antMatchers("/demos/**").hasRole("USERS");
http.authorizeRequests().antMatchers("/api/**").hasRole("ADMIN");
http.authorizeRequests().anyRequest().authenticated();
http.formLogin();
</code>Define in‑memory users for testing:
<code>@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("guest").password("123456").roles("ADMIN")
.and()
.withUser("test").password("666666").roles("USERS");
}
</code>Use hasAnyRole to grant access when a user possesses any one of several roles:
<code>http.authorizeRequests().antMatchers("/demos/**").hasAnyRole("USERS","AKKF","BLLE");
http.authorizeRequests().antMatchers("/api/**").hasAnyRole("ADMIN","MGR","SYSTEM");
</code>Other useful configurations include assigning the same authority to multiple URIs and controlling access by HTTP method:
<code>http.authorizeRequests().antMatchers("/demos/**","/api/**").hasAnyAuthority("ROLE_USERS","ROLE_ADMIN");
http.authorizeRequests().antMatchers(HttpMethod.GET).permitAll();
</code>Custom Login Page
Add Thymeleaf dependencies to the project:
<code><dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</code>Configure Thymeleaf template location:
<code>spring:
thymeleaf:
prefix: classpath:/templates/
</code>Create login.html under src/main/resources/templates/ . The essential markup includes a form that posts to /login and displays error messages when authentication fails.
<code><div class="loginContainer">
<div class="pageTitle"><h3>认证登录</h3></div>
<div class="loginPanel">
<div class="loginTitle">安全登录</div>
<div class="loginContent">
<form method="post" action="login">
<div class="c-row">
<label>安全帐号</label>
<input type="text" name="username" placeholder="帐号" />
</div>
<div class="c-row">
<label>安全密码</label>
<input type="password" name="password" placeholder="密码" />
</div>
<div class="c-row">
<input type="checkbox" name="remember-me" /><label for="remember-me">记住我</label>
</div>
<div class="c-row" style="margin-top:20px;">
<button type="submit" class="btn btn-sm btn-primary">安全登录</button>
</div>
</form>
<div th:if="${param.error}" class="alert alert-danger">[[${session.SPRING_SECURITY_LAST_EXCEPTION?.message}]]</div>
</div>
</div>
</div>
</code>Map the custom login page with a controller:
<code>@Controller
public class LoginController {
@GetMapping("/custom/login")
public String login() { return "login"; }
}
</code>Finally, tell Spring Security to use this page:
<code>@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/resources/**").permitAll();
http.authorizeRequests().antMatchers("/demos/**").hasRole("USERS");
http.authorizeRequests().antMatchers("/api/**").hasRole("ADMIN");
http.formLogin().loginPage("/custom/login");
}
</code>Summary :
How to configure Spring Security to intercept requests and permit static resources.
How to enforce role‑based access for specific URI patterns.
How to replace the default login page with a custom Thymeleaf view.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.