如何在 spring 中只为一个用户角色启用 X509 相互身份验证?

How can I enable X.509 mutual authentification for only one user role in spring?

我构建了一个具有 spring 安全性的 spring 引导 Web 应用程序和一个使用来自 spring 应用程序的 REST 服务的 angular 前端。我现在有两个用户角色:

用户和管理员应通过用户名和密码进行身份验证,这已经在起作用,但我根本没有使用 Spring 用户角色。相反,我通过 angular consumend rest services and tokens(JWT) 构建自己的身份验证。
现在我想添加一个超级管理员,它访问与管理员相同的服务,但必须使用客户端证书 (X.509) 进行身份验证。
如果用户是超级管理员,我如何告诉 Spring 仅使用 X.509 进行身份验证?
我是否必须为此使用 Spring 个用户角色?

解决此问题的一种方法是使用自定义过滤器。在这种情况下,您可以扩展 X509AuthenticationFilter. You can override the filter's doFilter() method and modify the logic to perform a check on the type of user and then either call the doFilter() method from X509AuthenticationFilter or handle your JWT with your own AuthenticationProvider via the AuthenticationManager.

以下代码示例展示了您将如何创建和集成自定义 X509AuthenticationFilter,但并非旨在指导如何启用基于 X509 的身份验证。

示例 WebSecurityConfigurerAdapter:

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

    @Autowired
    private CustomX509AuthenticationFilter customX509AuthenticationFilter;

    @Autowired
    private  AuthenticationProvider jwtAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(jwtAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.x509().x509AuthenticationFilter(customX509AuthenticationFilter);
    }
}

示例自定义 X509AuthenticationFilter:

@Component
public class CustomX509AuthenticationFilter extends X509AuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    @Autowired
    public CustomX509AuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if(isAdminUser(request)){
            super.doFilter(request, response, chain);
        }
        else {
            Authentication unauthenticatedToken = getUserCredentialsFromRequest(request);
            Authentication authenticatedToken = authenticationManager.authenticate(unauthenticatedToken);
            if(authenticatedToken.isAuthenticated()) {
                SecurityContextHolder.getContext().setAuthentication(authenticatedToken);
                chain.doFilter(request, response);
            }
            else {
                throw new BadCredentialsException("Invalid Credentials");
            }
        }
    }

    private Authentication getUserCredentialsFromRequest(ServletRequest request) {
        // logic to retrieve user credentials from request and create initial Authentication
        return ...
    }

    private boolean isAdminUser(ServletRequest request) {
        // logic to determine whether or not user is admin
        return ...
    }
}