Master Spring Security: From Basics to Advanced Customizations
This comprehensive guide walks you through Spring Security fundamentals, authentication flow, project setup, custom user details, password encoding, login handling, 403 error pages, remember‑me functionality, method security annotations, and CSRF protection, providing clear code examples for each topic.
Spring Security Overview
Spring Security is a highly customizable security framework that leverages Spring IoC/DI and AOP to provide declarative access control, reducing repetitive security code.
Core features: authentication and authorization
Spring Security Authentication Process
Project Setup
Import Dependency
Spring Security is integrated into Spring Boot; simply add the starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>Access Page
After adding the starter, all requests are intercepted. Unauthenticated users are redirected to the default login page. Access the application at: http://localhost:8080/ Default username: user. Password is printed in the console on startup.
Custom Username and Password
Modify application.yml
# Static user, typically for internal network authentication
spring:
security:
user:
name: test # static username
password: test # static passwordUserDetailsService Details
When no configuration is provided, Spring Security generates a default user. In real projects, credentials are fetched from a database, requiring a custom UserDetailsService implementation:
@Component
public class UserSecurity implements UserDetailsService {
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userService.login(userName);
if (user == null) {
throw new UsernameNotFoundException("用户名错误");
}
return new org.springframework.security.core.userdetails.User(
userName, user.getPassword(), AuthorityUtils.createAuthorityList()
);
}
}PasswordEncoder Details
PasswordEncoder Interface
The PasswordEncoder handles password hashing and verification. Spring Security provides several implementations.
Custom PasswordEncoder
/**
* Credential matcher for authentication.
* encode – encrypt plain password to cipher text.
* matches – verify plain and cipher texts.
*/
public class MyMD5PasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
return toHexString(digest.digest(charSequence.toString().getBytes()));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(encode(charSequence));
}
private String toHexString(byte[] tmp) {
StringBuilder builder = new StringBuilder();
for (byte b : tmp) {
String s = Integer.toHexString(b & 0xFF);
if (s.length() == 1) {
builder.append("0");
}
builder.append(s);
}
return builder.toString();
}
}Register the encoder bean:
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // Spring Security built‑in
}Login Configuration
Method 1 – Forward
http.formLogin()
.usernameParameter("name")
.passwordParameter("pswd")
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.failureForwardUrl("/failure")
.successForwardUrl("/toMain");Method 2 – Redirect
http.formLogin()
.usernameParameter("name")
.passwordParameter("pswd")
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/toMain", true)
.failureUrl("/failure");Method 3 – Custom Handlers
Custom failure handler:
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
private String url;
private boolean isRedirect;
public MyAuthenticationFailureHandler(String url, boolean isRedirect) {
this.url = url;
this.isRedirect = isRedirect;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
if (isRedirect) {
response.sendRedirect(url);
} else {
request.getRequestDispatcher(url).forward(request, response);
}
}
}Custom success handler:
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
private String url;
private boolean isRedirect;
public MyAuthenticationSuccessHandler(String url, boolean isRedirect) {
this.url = url;
this.isRedirect = isRedirect;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
if (isRedirect) {
response.sendRedirect(url);
} else {
request.getRequestDispatcher(url).forward(request, response);
}
}
}403 Access Denied Handling
Implement AccessDeniedHandler to return a custom HTML page:
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(
"<html><body><div style='width:800px;text-align:center;margin:auto;font-size:24px'>权限不足,请联系管理员</div></body></html>"
);
response.getWriter().flush();
}
}Configure in the security config:
http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);Remember‑Me (Persistent Login)
http.rememberMe()
.rememberMeParameter("remember-me")
.tokenValiditySeconds(14*24*60*60)
.rememberMeCookieName("remember-me")
.tokenRepository(persistentTokenRepository)
.userDetailsService(userSecurity);Token repository bean:
@Bean
public PersistentTokenRepository persistentTokenRepository(DataSource dataSource) {
JdbcTokenRepositoryImpl repo = new JdbcTokenRepositoryImpl();
repo.setDataSource(dataSource);
return repo;
}Spring Security Annotations
@Secured
Role check – method can be accessed only if the user has the specified role (must be prefixed with ROLE_ ).
@Secured({"ROLE_管理员", "ROLE_访客"})
@RequestMapping("/toMain")
public String toMain() {
return "main";
}@PreAuthorize
Permission check before method execution.
@PreAuthorize("hasAnyRole('ROLE_管理员','ROLE_访客')")
@RequestMapping("/toMain")
public String toMain() {
return "main";
}@PostAuthorize
Permission check after method execution.
@PostAuthorize("hasRole('ROLE_管理员')")
@PreAuthorize("hasAuthority('admin:write')")
@RequestMapping("/toMain")
public String toMain() {
return "main";
}Enable method security in the configuration class with @EnableGlobalMethodSecurity (set securedEnabled, prePostEnabled as needed).
Spring Security & Thymeleaf Integration
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>CSRF Protection in Spring Security
What is CSRF?
Cross‑Site Request Forgery is an attack where a malicious site tricks a user's browser into sending unwanted requests to a trusted site where the user is authenticated, exploiting the fact that browsers automatically include cookies (including session IDs) with each request.
In Spring Security, CSRF protection is enabled by default; it can be disabled when necessary (e.g., for stateless APIs) using http.csrf().disable();.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
