关于SpringSecurity的身份验证

511 阅读4分钟

响应式x509身份验证

介绍

X.509证书身份验证最常见的用途是在使用SSL时验证服务器的身份,最常见的是在浏览器中使用HTTPS时。浏览器将自动检查由服务器提供的证书是否已由其维护的受信任的证书颁发机构之一颁发(即数字签名)。

您还可以将SSL与“mutual authentication[相互认证]”一起使用;然后,服务器将从客户端请求一个有效的证书,作为SSL握手的一部分。服务器将通过检查客户端的证书是否由可信任的权威机构签名来对其进行身份验证。如果提供了一个有效的证书,它可以通过应用程序中的servlet API获得。Spring Security X.509模块使用过滤器提取证书。它将证书映射到一个应用程序用户,并加载该用户的授权集,以便与标准Spring Security基础设施一起使用。

在尝试将证书与Spring Security一起使用之前,您应该熟悉如何使用证书并为servlet容器设置客户端身份验证。大部分工作是创建和安装合适的证书和密钥。例如,如果您正在使用Tomcat,那么请阅读这里的说明tomcat.apache.org/tomcat-9.0-…。在使用Spring Security之前让它工作起来是很重要的。

与Servlet X.509身份验证类似,响应式x509身份验证过滤器允许从客户端提供的证书中提取身份验证令牌。

向Web应用程序添加X.509身份验证

启用X.509客户端身份验证非常简单。只需将<x509/>元素添加到 http security 命名空间配置中。

<http>
...
    <x509 subject-principal-regex="CN=(.*?)," user-service-ref="userService"/>;
</http>

x509元素有两个可选属性:

  • subject-principal-regex 用于从证书的主题名称中提取用户名的正则表达式。上面显示了默认值。这是将被传递给UserDetailsService以加载用户权限的用户名。
  • user-service-ref 这是将与X.509一起使用的UserDetailsService的bean Id。如果在应用程序上下文中只定义了一个,则不需要它。

subject-principal-regex应该包含一个组。例如,默认表达式“CN=(.?)”匹配通用名称字段。因此,如果证书中的主题名是"CN=Jimi Hendrix, OU=...",那么将给出一个用户名"Jimi Hendrix"。匹配不区分大小写。因此"emailAddress=(.?) "将匹配"EMAILADDRESS= jimi@hendrix.org,CN=..."给出用户名"jimi@hendrix.org"。如果客户端提供了一个证书,并且成功提取了一个有效的用户名,那么在 security 上下文中应该有一个有效的Authentication对象。如果没有找到证书,或者没有找到相应的用户,那么 security 上下文将保持为空。这意味着您可以轻松地将X.509身份验证与其他选项(如基于表单的登录)一起使用。

Spring Security Samples存储库中有一些预先生成的证书。如果您不想生成自己的SSL,可以使用它们来启用SSL进行测试。文件server.jks包含服务器证书、私钥和颁发证书的机构证书。还有一些来自示例应用程序的用户的客户机证书文件。您可以在浏览器中安装它们以启用SSL客户机身份验证。

下面是一个响应式x509安全配置的例子:

快速开始一个SpringSecurity应用 代码的基础上,添加一个配置类:

package com.hz.ss.config;
​
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
​
import static org.springframework.security.config.Customizer.withDefaults;
​
/**
 * @author Dong
 * @version 1.0
 * @date 2022/5/4
 */
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http.x509(withDefaults())
            .authorizeExchange(exchanges -> exchanges.anyExchange().permitAll());
        return http.build();
    }
}

在上面的配置中,当没有提供principalExtractorauthenticationManager时,将使用默认值。默认的principal extractorSubjectDnX509PrincipalExtractor,它从客户端提供的证书中提取CN(通用名称)字段。默认的身份验证管理器是ReactivePreAuthenticatedAuthenticationManager,它实现用户帐户验证,检查具有principalExtractor引用的名称的用户帐户是否存在,并且它没有被锁定、禁用或过期。

下一个示例演示如何覆盖这些默认值:

package com.hz.ss.config;
​
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
​
import static org.springframework.security.config.Customizer.withDefaults;
​
/**
 * @author Dong
 * @version 1.0
 * @date 2022/5/4
 */
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        SubjectDnX509PrincipalExtractor principalExtractor = new SubjectDnX509PrincipalExtractor();
​
        principalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)");
​
        ReactiveAuthenticationManager authenticationManager = authentication -> {
            authentication.setAuthenticated("Trusted Org Unit".equals(authentication.getName()));
            return Mono.just(authentication);
        };
​
        http.x509(x509 -> x509.principalExtractor(principalExtractor)
                              .authenticationManager(authenticationManager))
            .authorizeExchange(exchanges -> exchanges.anyExchange().authenticated());
        return http.build();
    }
}

在本例中,用户名是从客户端证书的OU字段中提取的,而不包含CN,并且账户查找使用ReactiveUserDetailsService而不是执行所有的。相反,如果提供的证书颁发给名为“Trusted Org Unit[受信任的组织单位]”的OU,则将对请求进行身份验证。

注销

Spring Security默认提供了一个注销端点。登录后,您可以GET /logout来查看默认的注销确认页面,或者您可以POST /logout来初始化注销。这将:

  • 清除ServerCsrfTokenRepositoryServerSecurityContextRepository
  • 重定向到登录页面

通常,您还希望在注销时使会话失效。为了实现这一点,你可以添加WebSessionServerLogoutHandler到你的注销配置,像这样:

package com.hz.ss.config;
​
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
​
import static org.springframework.security.config.Customizer.withDefaults;
​
/**
 * @author Dong
 * @version 1.0
 * @date 2022/5/4
 */
@Configuration
public class SecurityConfig {
    @Bean
    SecurityWebFilterChain http(ServerHttpSecurity http) throws Exception {
        DelegatingServerLogoutHandler logoutHandler = new DelegatingServerLogoutHandler(new WebSessionServerLogoutHandler(), new SecurityContextServerLogoutHandler());
​
        http
            .authorizeExchange((exchange) -> exchange.anyExchange().authenticated())
            .logout((logout) -> logout.logoutHandler(logoutHandler));
​
        return http.build();
    }
}

\