凯哥Java 2012-03-15
转载:http://www.hxdw.com/bbs/post/view?bid=60&id=144184&sty=3&keywords=Spring+Security+3
一、环境说明:
Tomcat6.0.20
JDK1.6.x
Spring3.0.0.RELEASE
SpringSecurity3.0.0.RELEASE
CAScas-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:\Tomcat6.0
Path:C:\Java\jdk1.6.0_11\bin;
二、开发步骤
1.制作SSL证书
2.配置CASServer
3.配置CASClient
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.1cas.com
Java代码
127.0.0.1localhost
127.0.0.1cas.boc.com
127.0.0.1localhost
127.0.0.1cas.boc.com
而CAS客户端,也修改hosts文件,但要域要对应服务器端的IP
Java代码
127.0.0.1localhost
192.168.0.2cas.boc.com
127.0.0.1localhost
192.168.0.2cas.boc.com
配置Tomcat的Server.xml文件
Xml代码
<Connectorprotocol="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"/>
<Connectorprotocol="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"/>
开始配置CASServer
解压缩cas-server-3.4.2-release.zip文件
eclipse导入modules文件夹内的cas-server-webapp-3.4.2.war
并改名为casServer
导入后需要配置casServer工程的JavaBuildPath
这时运行会报一个错误,找不到AutowiringSchedulerFactoryBean类
AutowiringSchedulerFactoryBean类是一个Quartz的一个调度程序,定时监控CASServer状态
删除WEB-INF/spring-configuration/applicationContext.xml中的配置
或者
AutowiringSchedulerFactoryBean添加到org.jasig.cas.util包下
(AutowiringSchedulerFactoryBean类在最后附件中可以下载)
如果你对CAS单点登录的工作不是很熟悉,请查看
打开/WEB-INF/deployerConfigContext.xml
Xml代码
<beanid="authenticationManager"
class="org.jasig.cas.authentication.AuthenticationManagerImpl">
<!--
|ThisistheListofCredentialToPrincipalResolversthatidentifywhatPrincipalistryingtoauthenticate.
|TheAuthenticationManagerImplconsiderstheminorder,findingaCredentialToPrincipalResolverwhich
|supportsthepresentedcredentials.
|
|AuthenticationManagerImplusestheseresolversfortwopurposes.First,itusesthemtoidentifythePrincipal
|attemptingtoauthenticatetoCAS/login.Inthedefaultconfiguration,itistheDefaultCredentialsToPrincipalResolver
|thatfillsthisrole.IfyouareusingsomeotherkindofcredentialsthanUsernamePasswordCredentials,youwillneedtoreplace
|DefaultCredentialsToPrincipalResolverwithaCredentialsToPrincipalResolverthatsupportsthecredentialsyouare
|using.
|
|Second,AuthenticationManagerImplusestheseresolverstoidentifyaservicerequestingaproxygrantingticket.
|Inthedefaultconfiguration,itistheHttpBasedServiceCredentialsToPrincipalResolverthatservesthispurpose.
|YouwillneedtochangethislistifyouareidentifyingservicesbysomethingmoreorotherthantheircallbackURL.
+-->
<propertyname="credentialsToPrincipalResolvers">
<list>
<!--
|UsernamePasswordCredentialsToPrincipalResolversupportstheUsernamePasswordCredentialsthatweusefor/login
|bydefaultandproducesSimplePrincipalinstancesconveyingtheusernamefromthecredentials.
|
|Ifyou'vechangedyourLoginFormActiontousecredentialsotherthanUsernamePasswordCredentialsthenyouwillalso
|needtochangethisbeandeclaration(oraddadditionaldeclarations)todeclareaCredentialsToPrincipalResolverthatsupportsthe
|Credentialsyouareusing.
+-->
<bean
class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver"/>
<!--
|HttpBasedServiceCredentialsToPrincipalResolversupportsHttpBasedCredentials.ItsupportstheCAS2.0approachof
|authenticatingservicesbySSLcallback,extractingthecallbackURLfromtheCredentialsandrepresentingitasa
|SimpleServiceidentifiedbythatcallbackURL.
|
|IfyouarerepresentingservicesbysomethingmoreorotherthananHTTPSURLwhereattheyareableto
|receiveaproxycallback,youwillneedtochangethisbeandeclaration(oraddadditionaldeclarations).
+-->
<bean
class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver"/>
</list>
</property>
<!--
|WhereasCredentialsToPrincipalResolversidentifywhoitissomeCredentialsmightauthenticate,
|AuthenticationHandlersactuallyauthenticatecredentials.HerewedeclaretheAuthenticationHandlersthat
|authenticatethePrincipalsthattheCredentialsToPrincipalResolversidentified.CASwilltrythesehandlersinturn
|untilitfindsonethatbothsupportstheCredentialspresentedandsucceedsinauthenticating.
+-->
<propertyname="authenticationHandlers">
<list>
<!--
|ThisistheauthenticationhandlerthatauthenticatesservicesbymeansofcallbackviaSSL,therebyvalidating
|aserversideSSLcertificate.
+-->
<beanclass="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient"/>
<!--
|ThisistheauthenticationhandlerdeclarationthateveryCASdeployerwillneedtochangebeforedeployingCAS
|intoproduction.ThedefaultSimpleTestUsernamePasswordAuthenticationHandlerauthenticatesUsernamePasswordCredentials
|wheretheusernameequalsthepassword.YouwillneedtoreplacethiswithanAuthenticationHandlerthatimplementsyour
|localauthenticationstrategy.Youmightaccomplishthisbycodinganewsuchhandleranddeclaring
|edu.someschool.its.cas.MySpecialHandlerhere,oryoumightuseoneofthehandlersprovidedintheadaptorsmodules.
+-->
<!--<bean
class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler"/>
<beanclass="com.cas.service.CasUsernamePasswordAuthenticationHandler">
<propertyname="userDao"ref="userDao"/>
</bean>-->
<refbean="casAuthenticationHandler"/>
</list>
</property>
</bean>
<beanid="authenticationManager"
class="org.jasig.cas.authentication.AuthenticationManagerImpl">
<!--
|ThisistheListofCredentialToPrincipalResolversthatidentifywhatPrincipalistryingtoauthenticate.
|TheAuthenticationManagerImplconsiderstheminorder,findingaCredentialToPrincipalResolverwhich
|supportsthepresentedcredentials.
|
|AuthenticationManagerImplusestheseresolversfortwopurposes.First,itusesthemtoidentifythePrincipal
|attemptingtoauthenticatetoCAS/login.Inthedefaultconfiguration,itistheDefaultCredentialsToPrincipalResolver
|thatfillsthisrole.IfyouareusingsomeotherkindofcredentialsthanUsernamePasswordCredentials,youwillneedtoreplace
|DefaultCredentialsToPrincipalResolverwithaCredentialsToPrincipalResolverthatsupportsthecredentialsyouare
|using.
|
|Second,AuthenticationManagerImplusestheseresolverstoidentifyaservicerequestingaproxygrantingticket.
|Inthedefaultconfiguration,itistheHttpBasedServiceCredentialsToPrincipalResolverthatservesthispurpose.
|YouwillneedtochangethislistifyouareidentifyingservicesbysomethingmoreorotherthantheircallbackURL.
+-->
<propertyname="credentialsToPrincipalResolvers">
<list>
<!--
|UsernamePasswordCredentialsToPrincipalResolversupportstheUsernamePasswordCredentialsthatweusefor/login
|bydefaultandproducesSimplePrincipalinstancesconveyingtheusernamefromthecredentials.
|
|Ifyou'vechangedyourLoginFormActiontousecredentialsotherthanUsernamePasswordCredentialsthenyouwillalso
|needtochangethisbeandeclaration(oraddadditionaldeclarations)todeclareaCredentialsToPrincipalResolverthatsupportsthe
|Credentialsyouareusing.
+-->
<bean
class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver"/>
<!--
|HttpBasedServiceCredentialsToPrincipalResolversupportsHttpBasedCredentials.ItsupportstheCAS2.0approachof
|authenticatingservicesbySSLcallback,extractingthecallbackURLfromtheCredentialsandrepresentingitasa
|SimpleServiceidentifiedbythatcallbackURL.
|
|IfyouarerepresentingservicesbysomethingmoreorotherthananHTTPSURLwhereattheyareableto
|receiveaproxycallback,youwillneedtochangethisbeandeclaration(oraddadditionaldeclarations).
+-->
<bean
class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver"/>
</list>
</property>
<!--
|WhereasCredentialsToPrincipalResolversidentifywhoitissomeCredentialsmightauthenticate,
|AuthenticationHandlersactuallyauthenticatecredentials.HerewedeclaretheAuthenticationHandlersthat
|authenticatethePrincipalsthattheCredentialsToPrincipalResolversidentified.CASwilltrythesehandlersinturn
|untilitfindsonethatbothsupportstheCredentialspresentedandsucceedsinauthenticating.
+-->
<propertyname="authenticationHandlers">
<list>
<!--
|ThisistheauthenticationhandlerthatauthenticatesservicesbymeansofcallbackviaSSL,therebyvalidating
|aserversideSSLcertificate.
+-->
<beanclass="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient"/>
<!--
|ThisistheauthenticationhandlerdeclarationthateveryCASdeployerwillneedtochangebeforedeployingCAS
|intoproduction.ThedefaultSimpleTestUsernamePasswordAuthenticationHandlerauthenticatesUsernamePasswordCredentials
|wheretheusernameequalsthepassword.YouwillneedtoreplacethiswithanAuthenticationHandlerthatimplementsyour
|localauthenticationstrategy.Youmightaccomplishthisbycodinganewsuchhandleranddeclaring
|edu.someschool.its.cas.MySpecialHandlerhere,oryoumightuseoneofthehandlersprovidedintheadaptorsmodules.
+-->
<!--<bean
class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler"/>
<beanclass="com.cas.service.CasUsernamePasswordAuthenticationHandler">
<propertyname="userDao"ref="userDao"/>
</bean>-->
<refbean="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代码
packagecom.cas.service;
importorg.jasig.cas.authentication.handler.AuthenticationException;
importorg.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
importorg.jasig.cas.authentication.principal.UsernamePasswordCredentials;
importcom.cas.dao.UserDao;
importcom.cas.model.User;
publicclassCasUsernamePasswordAuthenticationHandlerextendsAbstractUsernamePasswordAuthenticationHandler{
/**
*用户信息操作层
*/
privateUserDaouserDao;
@Override
protectedbooleanauthenticateUsernamePasswordInternal(
UsernamePasswordCredentialscredentials)
throwsAuthenticationException{
Useruser=userDao.findUserByName(credentials.getUsername());
if(user==null){returnfalse;}
if(user.getPassword().equals(credentials.getPassword())){
returntrue;
}
returnfalse;
}
publicvoidsetUserDao(UserDaouserDao){
this.userDao=userDao;
}
}
packagecom.cas.service;
importorg.jasig.cas.authentication.handler.AuthenticationException;
importorg.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
importorg.jasig.cas.authentication.principal.UsernamePasswordCredentials;
importcom.cas.dao.UserDao;
importcom.cas.model.User;
publicclassCasUsernamePasswordAuthenticationHandlerextendsAbstractUsernamePasswordAuthenticationHandler{
/**
*用户信息操作层
*/
privateUserDaouserDao;
@Override
protectedbooleanauthenticateUsernamePasswordInternal(
UsernamePasswordCredentialscredentials)
throwsAuthenticationException{
Useruser=userDao.findUserByName(credentials.getUsername());
if(user==null){returnfalse;}
if(user.getPassword().equals(credentials.getPassword())){
returntrue;
}
returnfalse;
}
publicvoidsetUserDao(UserDaouserDao){
this.userDao=userDao;
}
}
接下来修改deployerConfigContext.xml文件的
Xml代码
<sec:user-serviceid="userDetailsService">
<sec:username="user01"password="user01"authorities="ROLE_USER"/>
<sec:username="admin"password="admin"authorities="ROLE_ADMIN"/>
<sec:username="@@THISSHOULDBEREPLACED@@"password="notused"authorities="ROLE_ADMIN"/>
</sec:user-service>
<sec:user-serviceid="userDetailsService">
<sec:username="user01"password="user01"authorities="ROLE_USER"/>
<sec:username="admin"password="admin"authorities="ROLE_ADMIN"/>
<sec:username="@@THISSHOULDBEREPLACED@@"password="notused"authorities="ROLE_ADMIN"/>
</sec:user-service>
这是登录cas管理页面的账号密码(这是一个简单的配置,也可以实现UserDetailsService接口,进行配置。这跟SpringSecurity的用户登录验证接口其实是一个)
配置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
#NamesofrolesallowedtoaccesstheCASservicemanager
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
#NamesofrolesallowedtoaccesstheCASservicemanager
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
启动CasServer
如果一切正常,能看到服务启动
因为配置了
Xml代码
<beanid="scheduler"class="org.jasig.cas.util.AutowiringSchedulerFactoryBean"/>
<beanid="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>
<propkey="/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"/>
<!--
uncommentthistoenablesendingPageRequestevents.
<property
name="interceptors">
<list>
<refbean="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>
<propkey="/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"/>
<!--
uncommentthistoenablesendingPageRequestevents.
<property
name="interceptors">
<list>
<refbean="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端进行配置