In Vaadin 23, how to get a header from the request, for CAC Authentication

I’m migrating from a PrimeFaces app which uses the following block of code to get the header from our user’s CAC:

        LOGGER.debug("Login function.");
        
        //Get handle on HTTPServletRequest object
        HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
        
        String subjectDN = request.getHeader("Our-CertSubject");
        
        if (subjectDN == null){
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Login Error! Please try again.","Please try again."));
            return;
        }
        LOGGER.debug("SubjectDN: " + subjectDN.replaceAll(StringFixes.CR_LF,""));

        StringBuilder sb = new StringBuilder();
        boolean found = false;
        for (char c : subjectDN.toCharArray()){
            if (Character.isDigit(c)){
                sb.append(c);
                found = true;
            } else if(found){
                break;
            }
        }
        
        LOGGER.debug("EDI Number: "+ sb.toString());
        
        subjectDN = sb.toString();
        
        Subject currentUser = SecurityUtils.getSubject();
		
		//...more Shiro processing continues here.

How do I do the getHeader() part in Spring Boot Vaadin?

This should typically be done in the Security Filter Chain. Architecture :: Spring Security

Your response has sent me down an interesting rabbit hole so far. The following code blocks represent my very tenuous grasp of how a SecurityFilterChain might be used to get the value we need from our Request object’s header. Below, Subject and SecurityUtils are Shiro objects which I’m not sure how to translate into Spring Security. Can you walk me through fine-tuning the code below:

@EnableWebSecurity
@Configuration
public class SecurityConfig extends VaadinWebSecurity {

private static class CrmInMemoryUserDetailsManager extends InMemoryUserDetailsManager {

    public CrmInMemoryUserDetailsManager() {
        createUser(new User("user",
                "{noop}userpass",
                Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))));
    }
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    // Authorize access to /images/ without authentication
    http.authorizeRequests().antMatchers("/images/**").permitAll();
    // Set default security policy that permits Vaadin internal requests and
    // denies all other
    super.configure(http);
    setLoginView(http, LoginView.class, "/logout");
}

@Bean
public InMemoryUserDetailsManager userDetailsService() {
    return new CrmInMemoryUserDetailsManager();
}

@Bean
@Override
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
            .addFilterBefore(new CAC_Filter(), AuthenticationFilter.class);
    return http.build();
}

}

public class CAC_Filter implements Filter {

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;

    String subjectDN = request.getHeader("Our-CertSubject");

    if (subjectDN == null) {
        //Login error!!
        return;
    }

    StringBuilder sb = new StringBuilder();
    boolean found = false;
    for (char c : subjectDN.toCharArray()) {
        if (Character.isDigit(c)) {
            sb.append(c);
            found = true;
        } else if (found) {
            break;
        }
    }

    subjectDN = sb.toString();

    Subject currentUser = SecurityUtils.getSubject();

    String userId = currentUser.getPrincipal().toString().replaceAll(StringFixes.CR_LF, "");

    //After Login UserName: userId

    //Get Shiro session for user
    Session userSession = currentUser.getSession();

}

}