由于谷歌杀死了NPAPI插件,我们无法在Chrome中加载小程序,因此基于客户端证书的网络应用程序身份验证方案不再有效。
在寻找替代方案时我发现,因为我们在私人内容上使用SSL,因此可以通过在Web服务器上启用SSL客户端身份验证(在本例中为Tomcat)自动请求SSL客户端证书
我的问题是:一旦我启用了Tomcat的SSL客户端身份验证,就可以从客户端浏览器的个人证书存储区请求此证书:
如何在我的JSF Web应用程序上获取证书信息,以便我可以在首次登录时注册具有该信息的用户并将其与用户ID相关联?
(我假设我不需要担心伪造或过期的证书,因为正确配置的Tomcat会拒绝它们,所以我不需要打扰身份验证/证书验证,但只需检索信息: Tomcat通过使用错误代码重定向到源页面来处理拒绝/身份验证错误
答案 0 :(得分:3)
我将描述一个过程来验证使用JSF 2.2和Spring Security 4.0使用客户端证书的西班牙语DNIe,尽管可以在不使用Spring Security的情况下进行身份验证。
您说您已启用Tomcat的SSL客户端身份验证,因此我猜这意味着您已经使用ROOT证书配置了keyStore。如果您不能为我提供有关Tomcat 7的有效说明。
因此,一旦Tomcat被正确配置为需要客户端证书,并且一旦握手完成,您就必须在应用程序中读取客户端证书:
pom.xml
将以下依赖项添加到pom.xml
:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.0.1.RELEASE</version>
</dependency>
值得一提的是Spring Security 4.0.1绑定到Spring 4.1.X
web.xml
以进行委派
告诉servlet容器,安全性被委托给Spring Security
<security-constraint>
<web-resource-collection>
<web-resource-name>Secured</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
必须要求客户证书:
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>certificate</realm-name>
</login-config>
配置Spring Security过滤器
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>
不要忘记将spring安全文件描述符添加到contextConfigLocation
上下文参数中。
以下是一个大文件,它配置Spring Security以验证客户端证书。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sec="http://www.springframework.org/schema/security"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userDetailsService" class="your.own.UserDetailService">
<property name="dao" ref="userDao" />
</bean>
<bean id="dniPrincipalExtractor" class="your.own.DniePrincipalExtractor">
</bean>
<bean id="x509Filter" class="org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter">
<property name="authenticationManager" ref="authManager" />
<property name="principalExtractor" ref="dniPrincipalExtractor" />
</bean>
<bean id="preauthAuthenticationProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" ref="authenticationUserDetailsService" />
</bean>
<bean id="authenticationUserDetailsService"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="userDetailsService" />
</bean>
<sec:http pattern="/css/**" security="none" />
<sec:http pattern="/error/**" security="none" />
<sec:http pattern="/icons/**" security="none" />
<sec:http pattern="/imgs/**" security="none" />
<sec:http
auto-config="true"
use-expressions="true"
entry-point-ref="forbiddenAuthEP">
<sec:intercept-url pattern="/*" access="permitAll" />
<sec:intercept-url pattern="/xhtml/**" access="hasRole('ROLE_BASIC')" />
<sec:custom-filter ref="x509Filter" position="X509_FILTER"/>
</sec:http>
<sec:authentication-manager alias="authManager">
<sec:authentication-provider ref="preauthAuthenticationProvider" />
</sec:authentication-manager>
</beans>
此文件创建一个安全上下文,需要通过x509Filter
进行登录。此过滤器需要
dniPrincipalExtractor
您需要从用户证书中查找并提取dni的类。 userDetailsService
知道如何使用userDao
在数据库中找到用户。使用此配置,一旦收到客户端证书,预身份验证服务就会从数据库(或其他)中提取DNI并加载用户
此代码暗示您必须构建自己的三个类:
your.own.UserDao
your.own.UserDetailService
必须实施org.springframework.security.core.userdetails.UserDetailsService
。在这里,您必须检索分配给用户的角色或组,以便为用户构建List<GrantedAuthority>
并创建有效的org.springframework.security.core.userdetails.User
。your.own.DniePrincipalExtractor
必须实施org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor
package your.own.package;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import your.own.UserDAO;
public class UserDetailService implements UserDetailsService {
private UserDAO dao = null;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String error = null;
UserDetails result = null;
if(StringUtils.isNotEmpty(username)){
if(dao.findById(username) != null){
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_BASIC", "ROLE_ADMIN");
result = new org.springframework.security.core.userdetails.User(username, "", authorities);
}
}else{
error = "No se ha especificado login para el usuario.";
}
if(result == null){
if(StringUtils.isEmpty(error)){
error = String.format("No se encuentra ningún usuario con login %s", username);
}
throw new UsernameNotFoundException(error);
}
return result;
}
}
为简单起见,我手动放置角色,显然你必须改变它。
现在,您可以通过会话bean或其他任何方式获得经过身份验证的用户
SecurityContextHolder.getContext().getAuthentication().getName()
如果您需要,我可以为您提供更多信息,但我不想写一个非常大的答案。这个答案中没有涉及的事情:
我的第一个实现并没有使用Spring Security,但到目前为止我担心需要提供回退机制,我继续推进Spring Security,尽管我没有告诉你如何在这里做,为简单起见。
希望它有所帮助!