转载: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端进行配置