Spring Security 3 与 CAS单点登录配置-Server

转载:http://www.hxdw.com/bbs/post/view?bid=60&id=144184&sty=3&keywords=Spring+Security+3

一、环境说明: 
Tomcat 6.0.20 
JDK 1.6.x 
Spring 3.0.0.RELEASE 
Spring Security 3.0.0.RELEASE 
CAS cas-server-3.4.2 
cas-client-3.1.10 
下载地址:http://www.jasig.org/cas/download 

配置环境变量 
JAVA_HOME : C:\Java\jdk1.6.0_11 
CATALINA_HOME : C:\Tomcat 6.0 
Path :C:\Java\jdk1.6.0_11\bin; 

二、开发步骤 
1.制作SSL证书 
2.配置CAS Server 
3.配置CAS Client 
4.安全框架合并 
5.简单测试 

三、开始 
证书制作,请参考 
http://linliangyi2007.javaeye.com/blog/165304 
说明 

*附件是制作证书的批处理程序,可以按照提示完成证书制作。 
*局域网用户,不要使用localhost做这个域名,因为你的证书设置了localhost,其他客户端要从你的域名下证书,而localhost又是客户端的域(可以查看%SystemRoot%\system32\drivers\etc\hosts) 
所以CAS服务器,修改%SystemRoot%\system32\drivers\etc\hosts文件 
增加:127.0.0.1 cas.com 
Java代码 
127.0.0.1 localhost 
127.0.0.1 cas.boc.com 

127.0.0.1 localhost
127.0.0.1 cas.boc.com

而CAS客户端,也修改hosts文件,但要域要对应服务器端的IP 
Java代码 
127.0.0.1 localhost 
192.168.0.2 cas.boc.com 

127.0.0.1 localhost
192.168.0.2 cas.boc.com

配置Tomcat的Server.xml文件 
Xml代码 
<Connector protocol="org.apache.coyote.http11.Http11Protocol" 
port="8443" minSpareThreads="5" maxSpareThreads="75" 
enableLookups="true" disableUploadTimeout="true" 
acceptCount="100" maxThreads="200" scheme="https" secure="true" SSLEnabled="true" 
keystoreFile="%USERPROFILE%/.keystore" 
keystorePass="changeit" clientAuth="false" 
sslProtocol="TLS"/> 

  <Connector protocol="org.apache.coyote.http11.Http11Protocol"
port="8443" minSpareThreads="5" maxSpareThreads="75"
       enableLookups="true" disableUploadTimeout="true" 
       acceptCount="100" maxThreads="200" scheme="https" secure="true" SSLEnabled="true"
       keystoreFile="%USERPROFILE%/.keystore"
       keystorePass="changeit" clientAuth="false"
       sslProtocol="TLS"/>

开始配置CAS Server 
解压缩cas-server-3.4.2-release.zip文件 
eclipse导入modules文件夹内的cas-server-webapp-3.4.2.war 
并改名为casServer 
导入后需要配置casServer工程的Java Build Path 


这时运行会报一个错误,找不到AutowiringSchedulerFactoryBean类 
AutowiringSchedulerFactoryBean类是一个Quartz的一个调度程序,定时监控CAS Server状态 
删除WEB-INF/spring-configuration/applicationContext.xml中的配置 
或者 
AutowiringSchedulerFactoryBean添加到org.jasig.cas.util包下 
(AutowiringSchedulerFactoryBean类在最后附件中可以下载) 

如果你对CAS单点登录的工作不是很熟悉,请查看 
打开/WEB-INF/deployerConfigContext.xml 
Xml代码 
<bean id="authenticationManager" 
class="org.jasig.cas.authentication.AuthenticationManagerImpl"> 
<!-- 
| This is the List of CredentialToPrincipalResolvers that identify what Principal is trying to authenticate. 
| The AuthenticationManagerImpl considers them in order, finding a CredentialToPrincipalResolver which 
| supports the presented credentials. 

| AuthenticationManagerImpl uses these resolvers for two purposes. First, it uses them to identify the Principal 
| attempting to authenticate to CAS /login . In the default configuration, it is the DefaultCredentialsToPrincipalResolver 
| that fills this role. If you are using some other kind of credentials than UsernamePasswordCredentials, you will need to replace 
| DefaultCredentialsToPrincipalResolver with a CredentialsToPrincipalResolver that supports the credentials you are 
| using. 

| Second, AuthenticationManagerImpl uses these resolvers to identify a service requesting a proxy granting ticket. 
| In the default configuration, it is the HttpBasedServiceCredentialsToPrincipalResolver that serves this purpose. 
| You will need to change this list if you are identifying services by something more or other than their callback URL. 
+--> 
<property name="credentialsToPrincipalResolvers"> 
<list> 
<!-- 
| UsernamePasswordCredentialsToPrincipalResolver supports the UsernamePasswordCredentials that we use for /login 
| by default and produces SimplePrincipal instances conveying the username from the credentials. 

| If you've changed your LoginFormAction to use credentials other than UsernamePasswordCredentials then you will also 
| need to change this bean declaration (or add additional declarations) to declare a CredentialsToPrincipalResolver that supports the 
| Credentials you are using. 
+--> 
<bean 
class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" /> 
<!-- 
| HttpBasedServiceCredentialsToPrincipalResolver supports HttpBasedCredentials. It supports the CAS 2.0 approach of 
| authenticating services by SSL callback, extracting the callback URL from the Credentials and representing it as a 
| SimpleService identified by that callback URL. 

| If you are representing services by something more or other than an HTTPS URL whereat they are able to 
| receive a proxy callback, you will need to change this bean declaration (or add additional declarations). 
+--> 
<bean 
class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" /> 
</list> 
</property> 

<!-- 
| Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate, 
| AuthenticationHandlers actually authenticate credentials. Here we declare the AuthenticationHandlers that 
| authenticate the Principals that the CredentialsToPrincipalResolvers identified. CAS will try these handlers in turn 
| until it finds one that both supports the Credentials presented and succeeds in authenticating. 
+--> 
<property name="authenticationHandlers"> 
<list> 
<!-- 
| This is the authentication handler that authenticates services by means of callback via SSL, thereby validating 
| a server side SSL certificate. 
+--> 
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" 
p:httpClient-ref="httpClient" /> 
<!-- 
| This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS 
| into production. The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials 
| where the username equals the password. You will need to replace this with an AuthenticationHandler that implements your 
| local authentication strategy. You might accomplish this by coding a new such handler and declaring 
| edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules. 
+--> 
<!-- <bean 
class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /> 
<bean class="com.cas.service.CasUsernamePasswordAuthenticationHandler"> 
<property name="userDao" ref="userDao" /> 
</bean> --> 
<ref bean="casAuthenticationHandler"/> 
</list> 
</property> 
</bean> 

<bean id="authenticationManager"
    class="org.jasig.cas.authentication.AuthenticationManagerImpl">
    <!--
      | This is the List of CredentialToPrincipalResolvers that identify what Principal is trying to authenticate.
      | The AuthenticationManagerImpl considers them in order, finding a CredentialToPrincipalResolver which 
      | supports the presented credentials.
      |
      | AuthenticationManagerImpl uses these resolvers for two purposes. First, it uses them to identify the Principal
      | attempting to authenticate to CAS /login . In the default configuration, it is the DefaultCredentialsToPrincipalResolver
      | that fills this role. If you are using some other kind of credentials than UsernamePasswordCredentials, you will need to replace
      | DefaultCredentialsToPrincipalResolver with a CredentialsToPrincipalResolver that supports the credentials you are
      | using.
      |
      | Second, AuthenticationManagerImpl uses these resolvers to identify a service requesting a proxy granting ticket. 
      | In the default configuration, it is the HttpBasedServiceCredentialsToPrincipalResolver that serves this purpose. 
      | You will need to change this list if you are identifying services by something more or other than their callback URL.
      +-->
    <property name="credentialsToPrincipalResolvers">
      <list>
        <!--
          | UsernamePasswordCredentialsToPrincipalResolver supports the UsernamePasswordCredentials that we use for /login 
          | by default and produces SimplePrincipal instances conveying the username from the credentials.
          | 
          | If you've changed your LoginFormAction to use credentials other than UsernamePasswordCredentials then you will also
          | need to change this bean declaration (or add additional declarations) to declare a CredentialsToPrincipalResolver that supports the
          | Credentials you are using.
          +-->
        <bean
          class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" />
        <!--
          | HttpBasedServiceCredentialsToPrincipalResolver supports HttpBasedCredentials. It supports the CAS 2.0 approach of
          | authenticating services by SSL callback, extracting the callback URL from the Credentials and representing it as a
          | SimpleService identified by that callback URL.
          |
          | If you are representing services by something more or other than an HTTPS URL whereat they are able to
          | receive a proxy callback, you will need to change this bean declaration (or add additional declarations).
          +-->
        <bean
          class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
      </list>
    </property>

    <!--
      | Whereas CredentialsToPrincipalResolvers identify who it is some Credentials might authenticate, 
      | AuthenticationHandlers actually authenticate credentials. Here we declare the AuthenticationHandlers that
      | authenticate the Principals that the CredentialsToPrincipalResolvers identified. CAS will try these handlers in turn
      | until it finds one that both supports the Credentials presented and succeeds in authenticating.
      +-->
    <property name="authenticationHandlers">
      <list>
        <!--
          | This is the authentication handler that authenticates services by means of callback via SSL, thereby validating
          | a server side SSL certificate.
          +-->
        <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
          p:httpClient-ref="httpClient" />
        <!--
          | This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS 
          | into production. The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials
          | where the username equals the password. You will need to replace this with an AuthenticationHandler that implements your
          | local authentication strategy. You might accomplish this by coding a new such handler and declaring
          | edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules.
          +-->
        <!-- <bean
          class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /> 
          <bean class="com.cas.service.CasUsernamePasswordAuthenticationHandler">
            <property name="userDao" ref="userDao" />
          </bean> -->
          <ref bean="casAuthenticationHandler"/>
      </list>
    </property>
  </bean>

其中authenticationHandlers属性是个关键,它为CAS服务器提供用户认证的依据 
(它只负责用户的认证,不负责受权服务。也就是说,CAS服务器不关心用户角色) 
应该注意的是,authenticationHandlers属性的值是实现了AuthenticationHandler接口类的List,多个应用提供用户账号和密码的数据源,CAS验证时,会在这个List的验证接口中逐一遍历。 
可能会有疑问,当有两个应用A和B,分别有账号abc/123和abc/abc 
当B系统登陆时,使用了A的账户和密码(即同账户不同密码问题),这时,CAS将提供一个有效验证,返回给B应用。这不会乱套? 
其实,当你配置了Client端时,这个问题就能被解释了,其实,CAS为Client提供一个ticket后,Client需要用登陆的账号和密码再验证一次,并且取得用户的角色信息。如果没有权限,则提示401,账号被锁定 


言归正传,这里的casAuthenticationHandler是一个类实现authenticationHandlers接口的类,但你可以继承一个抽象类AbstractUsernamePasswordAuthenticationHandler去实现。 
因为我的cas使用了iBatis.所以直接注入UserDao进行登录有效性验证 
Java代码 
package com.cas.service; 

import org.jasig.cas.authentication.handler.AuthenticationException; 
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler; 
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials; 

import com.cas.dao.UserDao; 
import com.cas.model.User; 

public class CasUsernamePasswordAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler { 

/** 
* 用户信息操作层 
*/ 
private UserDao userDao; 

@Override 
protected boolean authenticateUsernamePasswordInternal( 
UsernamePasswordCredentials credentials) 
throws AuthenticationException { 
User user = userDao.findUserByName(credentials.getUsername()); 

if (user == null) {return false;} 

if (user.getPassword().equals(credentials.getPassword())) { 
return true; 


return false; 


public void setUserDao(UserDao userDao) { 
this.userDao = userDao; 



package com.cas.service;

import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;

import com.cas.dao.UserDao;
import com.cas.model.User;

public class CasUsernamePasswordAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {

  /**
   * 用户信息操作层
   */
  private UserDao userDao;
  
  @Override
  protected boolean authenticateUsernamePasswordInternal(
      UsernamePasswordCredentials credentials)
      throws AuthenticationException {
    User user = userDao.findUserByName(credentials.getUsername());
    
    if (user == null) {return false;}
    
    if (user.getPassword().equals(credentials.getPassword())) {
      return true;
    }
    
    return false;
  }

  public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
  }
}

接下来修改deployerConfigContext.xml文件的 
Xml代码 
<sec:user-service id="userDetailsService"> 
<sec:user name="user01" password="user01" authorities="ROLE_USER" /> 
<sec:user name="admin" password="admin" authorities="ROLE_ADMIN" /> 
<sec:user name="@@THIS SHOULD BE REPLACED@@" password="notused" authorities="ROLE_ADMIN" /> 
</sec:user-service> 

<sec:user-service id="userDetailsService">
  <sec:user name="user01" password="user01" authorities="ROLE_USER" />
  <sec:user name="admin" password="admin" authorities="ROLE_ADMIN" />
<sec:user name="@@THIS SHOULD BE REPLACED@@" password="notused" authorities="ROLE_ADMIN" />
</sec:user-service>

这是登录cas管理页面的账号密码(这是一个简单的配置,也可以实现UserDetailsService接口,进行配置。这跟Spring Security的用户登录验证接口其实是一个) 

配置cas.properties文件 
Xml代码 
#cas.securityContext.serviceProperties.service=http://localhost:8080/casServer/services/j_acegi_cas_security_check 
cas.securityContext.serviceProperties.service=http://cas.boc.com:8080/casServer/services/j_acegi_cas_security_check 
# Names of roles allowed to access the CAS service manager 
cas.securityContext.serviceProperties.adminRoles=ROLE_ADMIN 
cas.securityContext.casProcessingFilterEntryPoint.loginUrl=http://cas.boc.com:8080/casServer/login 
cas.securityContext.ticketValidator.casServerUrlPrefix=http://cas.boc.com:8080/casServer 


cas.themeResolver.defaultThemeName=default 
cas.viewResolver.basename=default_views 

host.name=casServer 

#database.hibernate.dialect=org.hibernate.dialect.OracleDialect 
#database.hibernate.dialect=org.hibernate.dialect.MySQLDialect 
database.hibernate.dialect=org.hibernate.dialect.HSQLDialect 

#cas.securityContext.serviceProperties.service=http://localhost:8080/casServer/services/j_acegi_cas_security_check
cas.securityContext.serviceProperties.service=http://cas.boc.com:8080/casServer/services/j_acegi_cas_security_check
# Names of roles allowed to access the CAS service manager
cas.securityContext.serviceProperties.adminRoles=ROLE_ADMIN
cas.securityContext.casProcessingFilterEntryPoint.loginUrl=http://cas.boc.com:8080/casServer/login
cas.securityContext.ticketValidator.casServerUrlPrefix=http://cas.boc.com:8080/casServer

cas.themeResolver.defaultThemeName=default
cas.viewResolver.basename=default_views

host.name=casServer

#database.hibernate.dialect=org.hibernate.dialect.OracleDialect
#database.hibernate.dialect=org.hibernate.dialect.MySQLDialect
database.hibernate.dialect=org.hibernate.dialect.HSQLDialect

启动Cas Server 
如果一切正常,能看到服务启动 
因为配置了 
Xml代码 
<bean id="scheduler" class="org.jasig.cas.util.AutowiringSchedulerFactoryBean"/> 

<bean id="scheduler" class="org.jasig.cas.util.AutowiringSchedulerFactoryBean"/>

每隔一段时间,控制台将有当前服务信息提示。 

到目前为止,CAS服务器的雏形配置完成。这是一个基本的配置,我们查看cas-servlet.xml 
文件 
Xml代码 
<bean 
id="handlerMappingC" 
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> 
<property 
name="mappings"> 
<props> 
<prop 
key="/logout"> 
logoutController 
</prop> 
<prop 
key="/serviceValidate"> 
serviceValidateController 
</prop> 
<prop 
key="/validate"> 
legacyValidateController 
</prop> 
<prop 
key="/proxy"> 
proxyController 
</prop> 
<prop 
key="/proxyValidate"> 
proxyValidateController 
</prop> 
<prop 
key="/samlValidate"> 
samlValidateController 
</prop> 

<prop 
key="/services/add.html"> 
addRegisteredServiceSimpleFormController 
</prop> 

<prop 
key="/services/edit.html"> 
editRegisteredServiceSimpleFormController 
</prop> 

<prop 
key="/services/loggedOut.html"> 
serviceLogoutViewController 
</prop> 

<prop key="/services/viewStatistics.html"> 
viewStatisticsController 
</prop> 

<prop 
key="/services/*"> 
manageRegisteredServicesMultiActionController 
</prop> 
<prop 
key="/openid/*">openIdProviderController</prop> 
<prop 
key="/authorizationFailure.html">passThroughController</prop> 
</props> 
</property> 
<property 
name="alwaysUseFullPath" value="true" /> 
<!-- 
uncomment this to enable sending PageRequest events. 
<property 
name="interceptors"> 
<list> 
<ref bean="pageRequestHandlerInterceptorAdapter" /> 
</list> 
</property> 
--> 
</bean> 

  <bean
    id="handlerMappingC"
    class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property
      name="mappings">
      <props>
        <prop
          key="/logout">
          logoutController
        </prop>
        <prop
          key="/serviceValidate">
          serviceValidateController
        </prop>
        <prop
          key="/validate">
          legacyValidateController
        </prop>
        <prop
          key="/proxy">
          proxyController
        </prop>
        <prop
          key="/proxyValidate">
          proxyValidateController
        </prop>
        <prop
          key="/samlValidate">
          samlValidateController
        </prop>
        
        <prop
          key="/services/add.html">
          addRegisteredServiceSimpleFormController
        </prop>
        
        <prop
          key="/services/edit.html">
          editRegisteredServiceSimpleFormController
        </prop>
        
        <prop
          key="/services/loggedOut.html">
          serviceLogoutViewController
        </prop>

<prop key="/services/viewStatistics.html">
viewStatisticsController
</prop>
      
        <prop
          key="/services/*">
          manageRegisteredServicesMultiActionController
        </prop>
        <prop
          key="/openid/*">openIdProviderController</prop>
<prop
key="/authorizationFailure.html">passThroughController</prop>
      </props>
    </property>
    <property
      name="alwaysUseFullPath" value="true" />
    <!--
    uncomment this to enable sending PageRequest events. 
    <property
      name="interceptors">
      <list>
        <ref bean="pageRequestHandlerInterceptorAdapter" />
      </list>
    </property>
     -->
  </bean>

这里有很多映射关系,我们关注/services/viewStatistics.html这个地址 
在浏览器中打开(系统会先提示你登陆,如果你跟我的配置一样,那么admin/admin即可登陆) 


里面红色提示内容大体是说 
因为你没有配置提供的服务,所以CAS处于开放模式,一旦你配置了一个服务,CAS将不再是开放模式,任何应用希望提供CAS,则必须注册。如果你要用这个工具,第一件事是需要将自己加入到这个工具中,默认服务管理工具的URL为"http://cas.boc.com:8080/casServer/services/j_acegi_cas_security_check" 

这里我们先不要去管它,因为要做的工作还有很多,这里只不过是刚刚开始。 

下一步对Client端进行配置 

猜你喜欢

转载自rongdmmap-126-com.iteye.com/blog/1415931