Project Dependencies
Include the following essential dependencies in a Spring Boot project:
spring-boot-starter-parent(parent POM)spring-boot-starter-webspring-boot-starter-securityspring-boot-starter-test(optional, for testing)spring-security-test(opsional, for testing)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
</parent>
<groupId>com.miao</groupId>
<artifactId>SpringSecurity-First-Start</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Quick Start
When the application runs, a default user named user is created, and its generated password appears in the console log.
Basic Controller
package com.miao.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/hello")
public class HelloSecurity {
@RequestMapping("/world")
public String test1() {
return "<h1>Hello Spring Security!</h1>";
}
}
Navigate to http://localhost:8080/hello/world. The default login form at /login intercepts the request. Use /logout to terminate the session.
Authentication Architecture
The authentication flow relies on AbstractAuthenticationProcessingFilter, AbstractUserDetailsAuthenticationProvider, and methods like preAuthenticationChecks and postAuthenticationChecks.
Security Filter Chain Configuration
Spring Security applies authentication to all endpoints by default. To customize which resources require authentication, define a SecurityFilterChain bean. If no such bean exists, the default configuration takes effect.
@Configuration
public class WebSecurityConfigurer {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.build();
}
}
User Authentication Sources
Spring Security supplies an in-memory user store by default. To switch to custom authentication (e.g., database-backed), implement UserDetailsService, AuthenticationProvider, or related beans. Doing so disables the default in-memory user.
In-Memory User Configuration
@Bean
public InMemoryUserDetailsManager userDetailsService(PasswordEncoder encoder) {
UserDetails adminUser = User.withUsername("root")
.password(encoder.encode("123456"))
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(adminUser);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Placeholders such as {noop}, {bcrypt} indicate the password encoding scheme. BCryptPasswordEncoder is recomended for production.
Database-Backed Authentication
Implement UserDetailsService to load user details from a database.
@Component
public class MyUserDetailsService implements UserDetailsService {
private final UserInfoDao userInfoDao;
public MyUserDetailsService(UserInfoDao userInfoDao) {
this.userInfoDao = userInfoDao;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo entity = userInfoDao.findByUsername(username);
if (entity == null) {
return null;
}
return User.withUsername(entity.getUsername())
.password(entity.getPassword())
.roles(entity.getRole())
.build();
}
}
Role-Based Method Security
Enable method-level authorization with @EnableGlobalMethodSecurity(prePostEnabled = true). Use @PreAuthorize on controller methods.
@RestController
public class UserController {
@RequestMapping("/h1")
@PreAuthorize("hasAnyRole('normal', 'admin')")
public String multipleRoles() {
return "Accessible by normal and admin roles";
}
@RequestMapping("/h2")
@PreAuthorize("hasRole('admin')")
public String adminOnly() {
return "Only admin can view this";
}
}
Accompanying configuration:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyWebSecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsService(PasswordEncoder encoder) {
UserDetails normalUser = User.withUsername("normal")
.password(encoder.encode("123456"))
.roles("normal")
.build();
UserDetails adminUser = User.withUsername("admin")
.password(encoder.encode("1234567"))
.roles("admin")
.build();
return new InMemoryUserDetailsManager(normalUser, adminUser);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Custom Login Page
Replace the default login form with a custom HTML page. Ensure the form submits POST to a configurable processing URL, and disables CSRF if necessary.
Login Form
<form action="/dologin" method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password">
<input type="submit" value="Login">
</form>
Security Configuration
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests()
.mvcMatchers("/login", "/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/dologin")
.defaultSuccessUrl("/index", true)
.and()
.csrf().disable()
.build();
}
Custom Authentication Success and Failure Handlers
For APIs or single-page applications, replace redirects with JSON responses by implementing AuthenticationSuccessHandler and AuthenticationFailureHandler.
Success Handler
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException {
Map<String, Object> result = new HashMap<>();
result.put("msg", "Login succeeded");
result.put("status", 200);
result.put("authentication", authentication);
response.setContentType("application/json;charset=utf-8");
response.getWriter().println(new ObjectMapper().writeValueAsString(result));
}
}
Configuration
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests()
.mvcMatchers("/login", "/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.successHandler(new MyAuthenticationSuccessHandler())
.and()
.csrf().disable()
.build();
}
For failures, redirect with failureForwardUrl or failureUrl, or provide a custom AuthenticationFailureHandler to return JSON error details.