您的位置:首页 > 其它

CAS单点登录

2016-01-28 14:51 393 查看
https://www.ibm.com/developerworks/cn/opensource/os-cn-cas/

/article/3921551.html

资源与环境

apache-tomcat-6.0.44

jdk1.7.0_79

as-server-3.5.2-release

cas-client-3.2.1

cenots 6.5 简体中文

步骤

解压tomcat,安装到/usr/local/目录中
>命令:tar -zxvf apache-tomcat-6.0.44.tar.gz -C /usr/local/

配置 Tomcat 使用 Https 协议

生成服务器端库文件

keytool 是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书

Keytool将密钥(key)和证书(certificates)存在一个称为keystore的文件中

在keystore里,包含两种数据: 密钥实体(Key entity)——密钥(secret key)又或者是私钥和配对公钥(采用非对称加密)

可信任的证书实体(trusted certificate entries)——只包含公钥

命令:keytool -genkey -alias tomcat-server -keyalg RSA -keystore server.keystore

输入密钥库口令: centos

再次输入新口令: centos

您的名字与姓氏是什么?ljh2

您的组织单位名称是什么?ljh2

您的组织名称是什么?ljh2

您所在的城市或区域名称是什么?ljh2

您所在的省/市/自治区名称是什么?ljh2

该单位的双字母国家/地区代码是什么?ljh2

CN=ljh2, OU=ljh2, O=ljh2, L=ljh2, ST=ljh2, C=ljh2是否正确?

输入 的密钥口令

(如果和密钥库口令相同, 按回车):

生成的时候,密码输入后的第一项name一定要写你的完整计算机名称,我的是ljh2

参数说明:

genkey:生成库文件,在没有指定生成位置的情况下,keystore会存在用户系统默认目录

alias:别名

keyalg:指定密钥的算法 (如 RSA DSA(如果不指定默认采用DSA))

keystore:指定密钥库的名称(产生的各类信息将不在.keystore文件中)

其他常用参数说明:

storepass:证书库的访问密码(获取keystore信息所需的密码)

keypass:指定证书的私钥(私钥的密码)

list:显示密钥库中的证书信息

v:显示密钥库中的证书详细信息

export:将别名指定的证书导出到文件

export格式:keytool -export -alias 需要导出的别名 -keystore 指定keystore -file 指定导出的证书位置及证书名称 -storepass 密码

file:参数指定导出到文件的文件名

delete:删除密钥库中某条

keytool -delete -alias 指定需删除的别 -keystore 指定keystore -storepass 密码

printcert:查看导出的证书信息

keytool -printcert -file yushan.crt

keypasswd:修改密钥库中指定条目口令

import:将已签名数字证书导入密钥库

keytool -import -alias 指定导入条目的别名 -keystore 指定keystore -file 需导入的证书

配置tomcat

cd /usr/local/apache-tomcat-6.0.44

vim conf/server.xml

修改83-89行数据

去掉注释,改成如下

<Connector

protocol="org.apache.coyote.http11.Http11Protocol"

port="8443" maxThreads="200"

scheme="https" secure="true" SSLEnabled="true"

keystoreFile="/root/server.keystore" keystorePass="centos"

clientAuth="false" sslProtocol="TLS"/>


说明:

keystoreFile:服务器端库文件位置

keystorePass:证书库的访问密码

布署CAS服务端

下载并解压解压cas-server-3.5.2-release.zip,将cas-server-3.5.2\modules\cas-server-webapp-3.5.2.war,拷贝到tomcat目录下webapp下,重命名为cas.war

启动tomcat

bin/startup.sh

打开浏览器,访问https://ljh2:8443/cas

(ljh2为系统hosts文件中ip映射)

192.168.8.88 ljh1

192.168.8.98 ljh2

192.168.8.108 ljh3

如能正常显示登录页面,表示服务端配置成功,虽然 CAS Server 已经部署成功,但这只是一个缺省的实现,默认用户名与密码一样则登录成功。在实际使用的时候,还需要根据实际概况做扩展和定制,最主要的是扩展认证 (Authentication) 接口和 CAS Server 的界面

服务端扩展认证接口

CAS Server 负责完成对用户的认证工作,它会处理登录时的用户凭证 (Credentials) 信息,用户名/密码对是最常见的凭证信息。CAS Server 可能需要到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户名/密码,还可能通过 LDAP Server 获取等,在这种情况下,CAS 提供了一种灵活但统一的接口和实现分离的方式,实际使用中 CAS 采用哪种方式认证是与 CAS 的基本协议分离开的,用户可以根据认证的接口去定制和扩展。

扩展 AuthenticationHandler

public interface AuthenticationHandler {
//返回 boolean 类型的值,true 表示验证通过,false 表示验证失败
boolean authenticate(Credentials credentials) throws AuthenticationException;
//supports ()方法用于检查所给的包含认证信息的Credentials 是否受当前 AuthenticationHandler 支持
boolean supports(Credentials credentials);
}


CAS3中还提供了对AuthenticationHandler 接口的一些抽象实现,比如,可能需要在执行authenticate() 方法前后执行某些其他操作,那么可以让自己的认证类扩展自抽象类:AbstractPreAndPostProcessingAuthenticationHandler
public abstract class AbstractPreAndPostProcessingAuthenticationHandler
implements AuthenticateHandler{
protected Log log = LogFactory.getLog(this.getClass());
protected boolean preAuthenticate(final Credentials credentials) {
return true;
}
protected boolean postAuthenticate(final Credentials credentials,
final boolean authenticated) {
return authenticated;
}
public final boolean authenticate(final Credentials credentials)
throws AuthenticationException {
if (!preAuthenticate(credentials)) {
return false;
}
final boolean authenticated = doAuthentication(credentials);
return postAuthenticate(credentials, authenticated);
}
protected abstract boolean doAuthentication(final Credentials credentials)
throws AuthenticationException;
}


AbstractPreAndPostProcessingAuthenticationHandler 类新定义了 preAuthenticate() 方法和 postAuthenticate() 方法,而实际的认证工作交由 doAuthentication() 方法来执行。因此,如果需要在认证前后执行一些额外的操作,可以分别扩展 preAuthenticate()和 ppstAuthenticate() 方法,而 doAuthentication() 取代 authenticate() 成为了子类必须要实现的方法

由于实际运用中,最常用的是用户名和密码方式的认证,CAS3 提供了针对该方式的实现

AbstractUsernamePasswordAuthenticationHandler

public abstract class AbstractUsernamePasswordAuthenticationHandler extends

AbstractPreAndPostProcessingAuthenticationHandler{

protected final boolean doAuthentication(final Credentials credentials)

throws AuthenticationException {

return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials);

}

protected abstract boolean authenticateUsernamePasswordInternal(

final UsernamePasswordCredentials credentials) throws AuthenticationException;

protected final PasswordEncoder getPasswordEncoder() {

return this.passwordEncoder;

}

public final void setPasswordEncoder(final PasswordEncoder passwordEncoder) {

this.passwordEncoder = passwordEncoder;

}

}


基于用户名密码的认证方式可直接扩展自 AbstractUsernamePasswordAuthenticationHandler,验证用户名密码的具体操作通过实现 authenticateUsernamePasswordInternal() 方法达到,另外,通常情况下密码会是加密过的,setPasswordEncoder() 方法就是用于指定适当的加密器。

doAuthentication() 方法的参数是 Credentials 类型,这是包含用户认证信息的一个接口,对于用户名密码类型的认证信息,可以直接使用 UsernamePasswordCredentials,如果需要扩展其他类型的认证信息,需要实现Credentials接口,并且实现相应的 CredentialsToPrincipalResolver 接口,其具体方法可以借鉴 UsernamePasswordCredentials 和 UsernamePasswordCredentialsToPrincipalResolver。

JDBC 认证方法

用户的认证信息通常保存在数据库中,前面解压后的cas-server-3.5.2的modules 目录下可以找到包 cas-server-cas-server-support-jdbc-3.5.2.jar,其提供了通过 JDBC 连接数据库进行验证的缺省实现,基于该包的支持,我们只需要做一些配置工作即可实现 JDBC 认证

JDBC 认证方法支持多种数据库

配置 DataStore

打开文件 %CATALINA_HOME%/webapps/cas/WEB-INF/deployerConfigContext.xml,添加一个新的 bean 标签

<bean
id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource">

<property name="driverClassName">

<value>com.mysql.jdbc.Driver</value>

</property>

<property name="url">

<value>jdbc:mysql://ljh:3306/cas</value>

</property>

<property name="username">

<value>root</value>

</property>

<property name="password">

<value>root</value>

</property>

</bean>


配置 AuthenticationHandler

cas-server-support-jdbc-3.5.2.jar提供了 3 个基于 JDBC 的 AuthenticationHandler,分别为 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。其中 BindModeSearchDatabaseAuthenticationHandler 是用所给的用户名和密码去建立数据库连接,根据连接建立是否成功来判断验证成功与否;

QueryDatabaseAuthenticationHandler 通过配置一个 SQL 语句查出密码,与所给密码匹配;SearchModeSearchDatabaseAuthenticationHandler 通过配置存放用户验证信息的表、用户名字段和密码字段,构造查询语句来验证。

使用哪个 AuthenticationHandler,需要在 deployerConfigContext.xml 中设置,默认情况下,CAS 使用一个简单的 username=password 的 AuthenticationHandler,在文件中可以找到如下一行:
<bean
class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePassword

AuthenticationHandler" />
,我们可以将其注释掉,换成我们希望的一个 AuthenticationHandler,比如,使用QueryDatabaseAuthenticationHandler

<bean
class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">

<property name="dataSource" ref="casDataSource" />

<property name="sql"

value="select password from user where username = ? " />

</bean>


另外,由于存放在数据库中的密码通常是加密过的,所以 AuthenticationHandler 在匹配时需要知道使用的加密方法,在 deployerConfigContext.xml 文件中我们可以为具体的 AuthenticationHandler 类配置一个 property,指定加密器类.

部署依赖包

在以上配置完成以后,需要拷贝几个依赖的包到 cas 应用lib下,包括:

cas-server-support-jdbc-3.1.1.jar,数据库驱动包,DataStore与依赖包

扩展 CAS Server 界面

在部署 CAS 之前,我们可能需要定制一套新的 CAS Server 页面,添加一些个性化的内容。最简单的方法就是拷贝一份 default 文件到“cas/WEB-INF/view/jsp ”目录下,比如命名为 new,接下来是实现和修改必要的页面,有 4 个页面是必须的:

casConfirmView.jsp: 当用户选择了“ warn ”时会看到的确认界面

casGenericSuccess.jsp: 在用户成功通过认证而没有目的Service时会看到的界面

casLoginView.jsp: 当需要用户提供认证信息时会出现的界面

casLogoutView.jsp: 当用户结束 CAS 单点登录系统会话时出现的界面

页面定制完过后,还需要做一些配置从而让 CAS 找到新的页面。

拷贝“cas/WEB-INF/classes/default_views.properties”,重命名为“ cas/WEB-INF/classes/new_views.properties ”,并修改其中所有的值到相应新页面。最后是更新“cas/WEB-INF/cas-servlet.xml ”文件中的 viewResolver

修改cas/WEB-INF/cas.properties文件

将cas.viewResolver.basename=default_views改为cas.viewResolver.basename=new_views

扩展Cas服务端登录成功返回更多内容

默认cas服务端在返回给客户端用户信息时,默认只返回用户名,CAS使用一个credentialsToPrincipalResolvers将credentials转成Principal对象,此对象只有一个实现类SimplePrincipal,SimplePrincipal的构造方法接收两个参数,一个是用户的id,一个为用户的其他属性。用户的ID默认为用户登录时使用的用户名。

public
SimplePrincipal(final String id, final Map<String, Object> attributes)


为了给客户端返回更多的属性,我们必须要给Principal的构造方法传递第二个参数。

public
class MyCredentialsToPrincipalResolver implements CredentialsToPrincipalResolver {

private DataSource dataSource;//查询数据库用

public Principal resolvePrincipal(Credentials credentials) {

UsernamePasswordCredentials up = (UsernamePasswordCredentials) credentials;

String name = up.getUsername();

String pwd = up.getPassword();

//User user = "";查询数据库获取更多信息

Map<String,Object> attrs = new HashMap<String,Object>();

attrs.put("username",name);

attrs.put("pwd",pwd);

Principal p = new SimplePrincipal(id,attrs);//封装成包含id的Principal对象

return p;

}

}


服务器验证成功以后,是通过xml形式将结果传递给客户端的,xml的生成由casServiceValidationSuccess.jsp文件负责

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>

<cas:authenticationSuccess>

<cas:user>U001</cas:user>

<cas:attributes>

<cas:pwd>1234</cas:pwd>

<cas:username>Jack</cas:username>

</cas:attributes>

</cas:authenticationSuccess>

</cas:serviceResponse>

部署客户端应用

导出服务端证书

命令: keytool -export -alias tomcat-server -storepass centos -file server.crt -keystore server.keystore

参数说明:

export:将别名指定的证书导出到文件

file:导出到文件的文件名

keypass:证书库的密私口令(centos)

keystore:指定密钥库文件名称

生成文件 中的证书

将服务端证书导入的客户端JDK的证书信任库中

命令:keytool -import -trustcacerts -alias tomcat-server -file server.crt -keystore /usr/local/jdk1.7.0_79/jre/lib/security/cacerts

输入密钥库口令:changeit

是否信任此证书? [否]: y

说明:changeit为java的默认密私,导入成功可以看到证书的详细信息

配置客户端web.xml

<context-param>

<param-name>serverName</param-name>

<param-value>http://ljh:8080</param-value>

</context-param>


<filter>

<filter-name>CAS Filter</filter-name>

<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>

<init-param>

<param-name>casServerLoginUrl</param-name>

<param-value>https://ljh2:8443/cas/login</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>CAS Filter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>


<filter>

<filter-name>casTicketValidationFilter</filter-name>

<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>

<init-param>

<param-name>casServerUrlPrefix</param-name>

<param-value>https://ljh2:8443/cas/</param-value>

</init-param>

<init-param>

<param-name>useSession</param-name>

<param-value>true</param-value>

</init-param>

<init-param>

<param-name>redirectAfterValidation</param-name>

<param-value>true</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>casTicketValidationFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>


<filter>

<filter-name>casHttpServletRequestWrapperFilter</filter-name>

<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>casHttpServletRequestWrapperFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>


<filter>

<filter-name>casAssertionThreadLocalFilter</filter-name>

<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>casAssertionThreadLocalFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>


AuthenticationFilter

用来拦截所有的请求,用以判断用户是否需要通过Cas Server进行认证,

如果需要则将跳转到Cas Server的登录页面。如果不需要进行登录认证,则请求会继续往下执行。

AuthenticationFilter有两个用户必须指定的参数,

casServerLoginUrl:指定Cas Server登录地址,

serverName或service:指定认证成功后需要跳转地址

service和serverName只需要指定一个就可以了。

当两者都指定了,参数service将具有更高的优先级,即将以service指定的参数值为准。

service和serverName的区别在于service指定的是一个确定的URL,认证成功后就会确切的跳转到service指定的URL; 而serverName则是用来指定主机名,其格式为{protocol}:{hostName}:{port},如:https://localhost:8443,

当指定的是serverName时AuthenticationFilter将会把它附加上当前请求的URI,以及对应的查询参数来构造一个确定的URL 。如指定serverName为“http://localhost”,

而当前请求的URI为“/app”,查询参数为“a=b&b=c”,则对应认证成功后的跳转地址将为“http://localhost/app?a=b&b=c”

除了上述必须指定的参数外,AuthenticationFilter还可以指定如下可选参数:

l renew:当指定renew为true时,在请Cas Server时将带上参数“renew=true”,默认为false。 l gateway:指定gateway为true时,在请求Cas

Server时将带上参数“gateway=true”,默认为false。 l artifactParameterName:指定ticket对应的请求参数名称,默认为ticket。

l serviceParameterName:指定service对应的请求参数名称,默认为service

TicketValidationFilter

在请求通过AuthenticationFilter的认证之后,如果请求中携带了参数ticket则将会由TicketValidationFilter来对携带的ticket进行校验。

TicketValidationFilter只是对验证ticket的这一类Filter的统称,其并不对应Cas Client中的一个具体类型。

Cas Client中有多种验证ticket的Filter,都继承自AbstractTicketValidationFilter,它们的验证逻辑都是一致的,

都由AbstractTicketValidationFilter实现,所不同的是使用的TicketValidator不一样

必须指定的参数:

l casServerUrlPrefix:用来指定Cas Server对应URL地址的前缀,如上面示例的“https://ljh2:8443/cas”。

l serverName或service:语义跟前面介绍的一致 可选参数:

l redirectAfterValidation :表示是否验证通过后重新跳转到该URL,但是不带参数ticket,默认为true。

l useSession :在验证ticket成功后会生成一个Assertion对象,

如果useSession为true,则会将该对象存放到Session中。

如果为false,则要求每次请求都需要携带ticket进行验证,显然useSession为false跟redirectAfterValidation为true是冲突的。默认为true。

l exceptionOnValidationFailure :表示ticket验证失败后是否需要抛出异常,默认为true。

l renew:当值为true时将发送“renew=true”到Cas Server,默认为false。

HttpServletRequestWrapperFilter

HttpServletRequestWrapperFilter用于将每一个请求对应的HttpServletRequest封装为其内部定义的CasHttpServletRequestWrapper,该封装类将利用之前保存在Session或request中的Assertion对象重写HttpServletRequest的getUserPrincipal()、getRemoteUser()和isUserInRole()方法。这样在我们的应用中就可以非常方便的从HttpServletRequest中获取到用户的相关信息

AssertionThreadLocalFilter

AssertionThreadLocalFilter是为了方便用户在应用的其它地方获取Assertion对象,其会将当前的Assertion对象存放到当前的线程变量中,那么以后用户在程序的任何地方都可以从线程变量中获取当前Assertion,无需再从Session或request中进行解析。该线程变量是由AssertionHolder持有的,我们在获取当前的Assertion时也只需要通过AssertionHolder的getAssertion()方法获取即可,如:

Assertion assertion = AssertionHolder.getAssertion();

传递登录用户名

CAS 在登录成功过后,会给浏览器回传 Cookie,设置新的到的 Service Ticket。但客户端应用拥有各自的 Session,我们要怎么在各个应用中获取当前登录用户的用户名呢?CAS Client 的 Filter 已经做好了处理,在登录成功后,

AttributePrincipal
principal = (AttributePrincipal) request.getUserPrincipal();

Map attributes = principal.getAttributes();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: