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.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Master Spring Security: From Basics to Advanced Customizations

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 password

UserDetailsService 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();.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaAuthenticationCSRFAuthorizationspring-securityPasswordEncoder
Java High-Performance Architecture
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.