前一遍讲的是SSO的简单配置,项目中有时会用到SSO+LoginFrom 两种登录方式的情况,
Spring security同样可以实现,在之前SSO的基础上加入FORM_LOGIN_FILTER过滤器即可
上配置:
<security:http auto-config="false" use-expressions="false" entry-point-ref="ssoAuthenticationEntryPoint">
<security:custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="metadataSourceFilter" />
<security:access-denied-handler error-page="/ssoLogin.jsp"/>
<security:custom-filter position="PRE_AUTH_FILTER" ref="ssoAuthenticatedFilter" />
<security:custom-filter position="LOGOUT_FILTER" ref="ssoLogoutFilter" />
<span style="color:#ff6666;"><security:custom-filter ref="usernamePasswordFilter" position="FORM_LOGIN_FILTER" /></span>
<security:csrf disabled="true"/> <!-- token-repository-ref="csrfTokenRepository" request-matcher-ref="csrfRequiresMatcher"-->
<security:headers>
<security:frame-options disabled="true" /> <!--policy="SAMEORIGIN" iframe安全等级-->
</security:headers>
</security:http>
<!--SSO验证入口-->
<bean id="ssoAuthenticationEntryPoint" class="com.XX.eip.saif.security.SSOAuthenticationEntryPoint">
<property name="loginUrl" value="/ssoLogin.jsp" /> <!-- //SSO登录地址 -->
</bean>
<!--SSO认证过滤器-->
<bean id="ssoAuthenticatedFilter" class="com.XX.eip.saif.security.SSOAuthenticatedFilter">
<property name="securityKey" value="Hqsn/qU5iYY=" />
<property name="securityIV" value="Q9TMVAdPK0k=" />
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<!--验证管理器 只能定义一个-->
<security:authentication-manager alias="authenticationManager" > <!--erase-credentials="false" -->
<!--SSO验证管理器-->
<security:authentication-provider ref="authenticationProvider"/>
<!--用户密码验证管理器-->
<span style="color:#ff0000;"><security:authentication-provider ref="userPasswordAuthenticationProvider"/></span>
</security:authentication-manager>
<!--SSO用户验证器-->
<bean id="authenticationProvider" 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="userDetailsServiceImpl"/>
</bean>
<bean id="userDetailsServiceImpl" class="com.XX.platform.security.UserDetailsServiceImpl"/>
<!--sso登出拦截器-->
<bean id="ssoLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="/ssoLogout.jsp"/>
<constructor-arg>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</constructor-arg>
</bean>
<!-- 用户名密码登录拦截器 -->
<bean id="usernamePasswordFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationSuccessHandler">
<bean class=" com.XX.platform.security.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/" />
<property name="alwaysUseDefaultTargetUrl" value="true"/>
</bean>
</property>
<property name="authenticationFailureHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login.jsp?error=1" />
</bean>
</property>
</bean>
<!--用户密码验证器-->
<bean id="userPasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="hideUserNotFoundExceptions" value="false" />
<property name="userDetailsService" ref="userDetailsServiceImpl" />
<property name="passwordEncoder" ref="passwordEncoder" />
<property name="saltSource" ref="saltSource" />
</bean>
<bean id="passwordEncoder" class="com.XX.platform.security.Md5PasswordEncoder" />
<bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
<property name="userPropertyToUse" value="username"/>
</bean>
<!--intercept-url过滤器-->
<bean id="metadataSourceFilter" class="com.XX.platform.security.MetadataSourceSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="securityMetadataSource" ref="metadataSource" />
</bean>
<bean id="accessDecisionManager" class="com.XX.platform.security.AccessDecisionManager"/>
<bean id="metadataSource" init-method="loadResourceConfig" class="com.XX.platform.security.MetadataSource"/>
<bean id="csrfTokenRepository" class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository" />
<bean id="csrfRequiresMatcher" class="com.XX.platform.security.CsrfRequiresMatcher" />
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:config/messages-security" />
</bean>
这里容易遇到的问题是
authentication-manager的配置, 这个不能配置两个,只会有一个生效,
但是里面的子节点authentication-provider却是可以配置多个的, 查看源代码 org.springframework.security.authentication.ProviderManager,
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class toTest = authentication.getClass();
Object lastException = null;
Authentication result = null;
boolean debug = logger.isDebugEnabled();
Iterator e = this.getProviders().iterator();
while(e.hasNext()) {
AuthenticationProvider provider = (AuthenticationProvider)e.next();
if(provider.supports(toTest)) {
if(debug) {
logger.debug("Authentication attempt using " + provider.getClass().getName());
}
try {
result = provider.authenticate(authentication);
if(result != null) {
this.copyDetails(authentication, result);
break;
}
} catch (AccountStatusException var11) {
this.prepareException(var11, authentication);
throw var11;
} catch (InternalAuthenticationServiceException var12) {
this.prepareException(var12, authentication);
throw var12;
} catch (AuthenticationException var13) {
lastException = var13;
}
}
}
。。。。。。。。
}
从while循环就可以明白原理了, 实际上是循环配置的 authentication-provider, 逐个验证,遇到验证通过的即终止break, 所以集成两个登录方式在这里就可以实现各自的验证了