Backend Development 11 min read

How to Add Domain-Based Verification to Spring Security Login in Spring Boot 3

This article announces a Spring Boot 3 case collection with 90 permanent examples, then walks through extending Spring Security login to include domain verification by defining a User entity, repository, custom filter, security configuration, Thymeleaf login page, controller, and test endpoint, complete with code snippets and screenshots.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Add Domain-Based Verification to Spring Security Login in Spring Boot 3

Spring Boot 3 case collection with over 90 practical articles is now available, promising permanent updates and providing subscribers with MD learning notes and full source code.

Environment: SpringBoot 3.4.0

1. Introduction

Spring Security is a powerful, highly customizable authentication and access‑control framework for Java enterprise applications. It offers authentication, authorization, encryption, session management and integrates seamlessly with Spring MVC.

2. Practical Example

2.1 Define Entity and Repository

<code>@Entity
@Table(name = "s_user")
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String domain;
    private String password;
    // getters, setters
}
</code>
<code>public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsernameAndDomain(String username, String domain);
}
</code>

2.2 Custom Filter

<code>public class ExtraAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        ExtraHttpRequest req = new ExtraHttpRequest(request);
        filterChain.doFilter(req, response);
    }
}
</code>
<code>public class ExtraHttpRequest extends HttpServletRequestWrapper {
    public ExtraHttpRequest(HttpServletRequest request) {
        super(request);
    }
    @Override
    public String getParameter(String name) {
        if (SecurityConfig.LOGIN_NAME_PARAMETER.equals(name)) {
            String username = super.getParameter(SecurityConfig.LOGIN_NAME_PARAMETER);
            String domain = super.getParameter(SecurityConfig.LOGIN_DOMAIN_PARAMETER);
            return username + Character.LINE_SEPARATOR + domain;
        }
        return super.getParameter(name);
    }
}
</code>

2.3 Security Configuration

<code>@Configuration
public class SecurityConfig {
    public static final String LOGIN_NAME_PARAMETER = "username";
    public static final String LOGIN_DOMAIN_PARAMETER = "domain";

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Throwable {
        http.csrf(csrf -> csrf.disable());
        http.authorizeHttpRequests(registry -> {
            registry.requestMatchers("*.html", "*.css", "*.js", "/login").permitAll();
            registry.requestMatchers("/**").authenticated();
        });
        http.formLogin(form -> {
            form.loginPage("/login").usernameParameter(LOGIN_NAME_PARAMETER);
        });
        http.addFilterBefore(extraAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    ExtraAuthenticationFilter extraAuthenticationFilter() {
        return new ExtraAuthenticationFilter();
    }

    @Bean
    PasswordEncoder noopPasswordEncoder() {
        return new PasswordEncoder() {
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return rawPassword != null && encodedPassword != null && rawPassword.equals(encodedPassword);
            }
            @Override
            public String encode(CharSequence rawPassword) {
                return rawPassword.toString();
            }
        };
    }
}
</code>

2.4 Custom UserDetailsService

<code>@Component
public class PackUserDetailsService implements UserDetailsService {
    private final UserRepository userRepository;
    public PackUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String[] info = StringUtils.split(username, String.valueOf(Character.LINE_SEPARATOR));
        return this.userRepository.findByUsernameAndDomain(info[0], info[1]);
    }
}
</code>

2.5 Custom Login Page (Thymeleaf)

Add dependencies:

<code>&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.thymeleaf.extras&lt;/groupId&gt;
    &lt;artifactId&gt;thymeleaf-extras-springsecurity6&lt;/artifactId&gt;
&lt;/dependency&gt;
</code>

Configure Thymeleaf:

<code>spring:
  thymeleaf:
    prefix: classpath:/static/
    suffix: .html
    cache: false
</code>

login.html (saved under classpath:/static):

<code>&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;title&gt;Secure Login&lt;/title&gt;
    &lt;link href="https://cdn.bootcdn.net/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet"&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;form class="form-signin" th:action="@{/login}" method="post"&gt;
        &lt;h2 class="form-signin-heading"&gt;Secure Login&lt;/h2&gt;
        &lt;p th:if="${param.error}" class="error"&gt;错误的用户名/域, 密码&lt;/p&gt;
        &lt;label for="username" class="sr-only"&gt;帐号&lt;/label&gt;
        &lt;input type="text" id="username" name="username" class="form-control" placeholder="用户名" required autofocus/&gt;
        &lt;label for="domain" class="sr-only"&gt;域&lt;/label&gt;
        &lt;input type="text" id="domain" name="domain" class="form-control" placeholder="登录域" required/&gt;
        &lt;label for="password" class="sr-only"&gt;密码&lt;/label&gt;
        &lt;input type="password" id="password" name="password" class="form-control" placeholder="密码" required/&gt;
        &lt;button class="btn btn-sm btn-primary btn-block" type="submit"&gt;登录&lt;/button&gt;
        &lt;a th:href="@{/index}"&gt;返回&lt;/a&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
</code>

2.6 Login Controller

<code>@Controller
public class LoginController {
    @GetMapping("/login")
    public String login() {
        return "login";
    }
}
</code>

2.7 Test Endpoint

<code>@RestController
@RequestMapping("/api")
public class ApiController {
    @GetMapping("/query")
    public ResponseEntity<Object> query() {
        return ResponseEntity.ok("api query success");
    }
}
</code>

Accessing /api/query redirects to the custom login page; after successful authentication the request returns to the original endpoint.

Spring Security default login page
Spring Security default login page
Custom login page with domain field
Custom login page with domain field
Redirect to login
Redirect to login
Successful login returns to /api/query
Successful login returns to /api/query
backend developmentSpring BootThymeleafSpring SecurityCustom AuthenticationDomain Login
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login 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.