预先认证是指用户在进入系统给钱,就已经通过某种机制进行过身份认证,请求中已经附带了身份认证的信息,这时我们只需要从获得这些身份认证信息,并对用户进行授权即可。CAS, X509等都属于这种情况。
Spring Security中专门为这种系统外预先认证的情况提供了工具类,这一章我们来看一下如何使用Pre-Auth处理使用容器Realm认证的用户。
首先在pom.xml中配置jetty所需的Realm。
<userRealms> <userRealm implementation="org.mortbay.jetty.security.HashUserRealm"> <name>Preauth Realm</name> <config>realm.properties</config> </userRealm> </userRealms>
用户,密码,以及权限信息都保存在realm.properties文件中。
admin: admin,ROLE_ADMIN,ROLE_USER user: user,ROLE_USER test: test
我们配置了三个用户,分别是admin, user和test,其中admin拥有ROLE_ADMIN和ROLE_USER权限,user拥有ROLE_USER权限,而test没有任何权限。
下一步在src/webapp/WEB-INF/web.xml中配置登录所需的安全权限。
<login-config> <auth-method>BASIC</auth-method> <realm-name>Preauth Realm</realm-name> </login-config> <security-role> <role-name>ROLE_USER</role-name> </security-role> <security-role> <role-name>ROLE_ADMIN</role-name> </security-role> <security-constraint> <web-resource-collection> <web-resource-name>All areas</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>ROLE_USER</role-name> </auth-constraint> </security-constraint>
这里我们将login-config中的realm-name配置为Preauth Realm,这与刚刚在pom.xml中配置的名称是相同的。而后我们配置了两个安全权限ROLE_USER和ROLE_ADMIN,最后我们现在访问所有资源都需要使用ROLE_USER这个权限。
自此,服务器与应用中的Realm配置完毕,下一步我们需要使用Spring Security与Realm对接。
因为使用容器Realm的Pre-Auth并没有对应的命名空间,所以我们只能使用传统方式,一个一个bean的配置了。
首先要配置springSecurityFilterChain,告诉Spring Security我们会使用哪些过滤器。
<bean id="springSecurityFilterChain" class="org.springframework.security.util.FilterChainProxy"> <sec:filter-chain-map path-type="ant"> <sec:filter-chain pattern="/**" filters="hscif,j2eePreAuthFilter,etf,fsi"/> </sec:filter-chain-map> </bean>
因为将要使用j2eePreAuthFilter,所有默认那些form-login, basic-login, rememberMe都没了用武之地,这里我们只保留HttpSessionContextIntegrationFilter, J2eePreAuthenticatedProcessingFilter, ExceptionTranslationFilter和FilterSecurityInterceptor。其中HttpSessionContextIntegrationFilter用来将session中的用户信息放到SecurityContext中。ExceptionTranslationFilter和FilterSecurityInterceptor负责验证用户权限,并处理验证过程中的异常。
而为了使用j2eePreAuthFilter,我们需要进行如下配置:
<bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider"> <sec:custom-authentication-provider /> <property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService"/> </bean> <bean id="preAuthenticatedUserDetailsService" class="org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService"/> <bean id="j2eeMappableRolesRetriever" class="org.springframework.security.ui.preauth.j2ee.WebXmlMappableAttributesRetriever"> <property name="webXmlInputStream"> <bean factory-bean="webXmlResource" factory-method="getInputStream"/> </property> </bean> <bean id="webXmlResource" class="org.springframework.web.context.support.ServletContextResource"> <constructor-arg ref="servletContext"/> <constructor-arg value="/WEB-INF/web.xml"/> </bean> <bean id="servletContext" class="org.springframework.web.context.support.ServletContextFactoryBean"/> <bean id="j2eePreAuthFilter" class="org.springframework.security.ui.preauth.j2ee.J2eePreAuthenticatedProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationDetailsSource"> <bean class="org.springframework.security.ui.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource"> <property name="mappableRolesRetriever" ref="j2eeMappableRolesRetriever"/> <property name="userRoles2GrantedAuthoritiesMapper"> <bean class="org.springframework.security.authoritymapping.SimpleAttributes2GrantedAuthoritiesMapper"> <property name="convertAttributeToUpperCase" value="true"/> </bean> </property> </bean> </property> </bean> <bean id="preauthEntryPoint" class="org.springframework.security.ui.preauth.PreAuthenticatedProcessingFilterEntryPoint"/>
这里,我们要配置Pre-Auth所需的AuthenticationProvider, EntryPoint, AuthenticatedUserDetailsService并最终组装成一个j2eePreAuthFilter。其中j2eeMappableRolesRetriever会读取我们之前配置的web.xml,从中获得权限信息。
这样,当用户登录时,请求会先被Realm拦截,并要求用户进行登录:
登录成功后,Realm会将用户身份信息绑定到请求中,j2eePreAuthFilter就会从请求中读取身份信息,结合web.xml中定义的权限信息对用户进行授权,并将授权信息录入SecurityContext,之后对用户验证时与之前已没有了任何区别。
这里的preauthEntryPoint会在用户权限不足时起作用,它只会简单返回一个401的拒绝访问响应。
在此我们并不推荐实际中使用这项功能,因为需要对容器进行配置,影响应用的灵活性。
实例在ch109。