hfghfgh
定义自己的CAS验证机制
到这里,估计很多朋友肯定想问如何自定义自己的验证机制,比如使用咱们最常用的数据库:用户名/密码,验证的方式,
呵呵,别着急,下面就来和大家说一说如何来自定义CAS Server端的验证机制,咱们来一起实现一个基于mysql数据库 用户名/密码 验证的列子。
首先先和大家介绍一下CAS的扩展认证接口
CAS Server 负责完成对用户的认证工作,它会处理登录时的用户凭证 (Credentials) 信息,用户名/密码对是最常见的凭证信息。CAS Server 可能需要到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户名/密码,还可能通过 LDAP Server 获取等,在这种情况下,CAS 提供了一种灵活但统一的接口和实现分离的方式,实际使用中 CAS 采用哪种方式认证是与 CAS 的基本协议分离开的,用户可以根据认证的接口去定制和扩展
扩展 AuthenticationHandler
CAS 提供扩展认证的核心是 AuthenticationHandler 接口,该接口定义如下:
Code
public interface AuthenticationHandler {
/**
* Method to determine if the credentials supplied are valid.
* @param credentials The credentials to validate.
* @return true if valid, return false otherwise.
* @throws AuthenticationException An AuthenticationException can contain
* details about why a particular authentication request failed.
*/
boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
* Method to check if the handler knows how to handle the credentials
* provided. It may be a simple check of the Credentials class or something
* more complicated such as scanning the information contained in the
* Credentials object.
* @param credentials The credentials to check.
* @return true if the handler supports the Credentials, false othewrise.
*/
boolean supports(Credentials credentials);
}
该接口定义了 2 个需要实现的方法,supports ()方法用于检查所给的包含认证信息的Credentials 是否受当前 AuthenticationHandler 支持;而 authenticate() 方法则担当验证认证信息的任务,这也是需要扩展的主要方法,根据情况与存储合法认证信息的介质进行交互,返回 boolean 类型的值,true 表示验证通过,false 表示验证失败。
CAS3中还提供了对AuthenticationHandler 接口的一些抽象实现,比如,可能需要在执行authenticate() 方法前后执行某些其他操作,那么可以让自己的认证类扩展下面的抽象类:
Code
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 提供了针对该方式的实现,如下所示:
Code
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。
了解一下上面一段原理后,咱们来写这个实际的例子吧:
cas-server-3.1.1-release.zip 包解开后,在 modules 目录下可以找到包 cas-server-support-jdbc-3.1.1.jar,其提供了通过 JDBC 连接数据库进行验证的缺省实现,基于该包的支持,我们只需要做一些配置工作即可实现 JDBC 认证。
[1].给出mysql建表语句,很简单,就一张user表,其他数据库也一样。
Code
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(20) NOT NULL,
`password` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'arix04', 'e10adc3949ba59abbe56e057f20f883e');
这里插了一条记录,用户名/密码:arix04/123456;使用的md5加密。
[2].配置
DataSource
找到tomcat/webapps/cas/WEB-INF目录(这里建议在做一下操作时先删除cas.war,只留下已经解包的cas工程)
找到deployerConfigContext.xml文件插入下面这段:
Code
<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://localhost:3306/cas_test</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>root</value>
</property>
</bean>
[3].配置 AuthenticationHandler
在 cas-server-support-jdbc-3.1.1.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
deployerConfigContext.xml文件插入下面这段:
Code
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref="casDataSource" />
<property name="sql" value="select password from user where username = ?" />
<property name="passwordEncoder" ref="myPasswordEncoder"/>
</bean>
上面这段,sql定义了一个查询语句,用来判断用户名,密码是否存在,myPasswordEncoder是我自定义的一个密码的加密类,实现了passwordEncoder接口及其 encode() 方法。
[4].配置passwordEncoder
deployerConfigContext.xml文件插入下面这段:
<bean id="myPasswordEncoder" class="org.jasig.cas.authentication.handler.MyPasswordEncoder"/>
[5].MyPasswordEncoder
给出源码,大家自己编译成class吧,然后把MyPasswordEncoder.class放到
Tomcat 6.0\webapps\cas\WEB-INF\lib\cas-server-core-3.3.1.jar中相应的包下,jar包用winrar打开后,直接把class拖到相应目录下即可。
Code
package org.jasig.cas.authentication.handler;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.util.StringUtils;
// Referenced classes of package org.jasig.cas.authentication.handler:
// PasswordEncoder
public final class MyPasswordEncoder
implements PasswordEncoder
{
public MyPasswordEncoder(){};
public String encode(String password)
{
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] strTemp = password.getBytes();
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
public final static String MD5(String s) {
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] strTemp = s.getBytes();
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
public static Date getDateByString(String dateString) {
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.parse(dateString);
} catch (Exception e) {
return null;
}
}
public static String getDateString(Date date) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.format(date);
}
}
[6].放入必要的Jar
将开发准备中的need.rar解压后的Jar文件、cas-server-support-jdbc-3.3.1.jar copy到Tomcat 6.0\webapps\cas\WEB-INF\lib\下面,如果你是用的其他数据库,则需要放入相应的数据库驱动包,
我这里给出的是mysql的驱动包。
[7].新建2个测试servlet
分别在上面SSO_Pro1,SSO_Pro2中新建class
WelcomePage
Code
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import edu.yale.its.tp.cas.client.filter.CASFilter;
import edu.yale.its.tp.cas.client.filter.CASFilterRequestWrapper;
public class WelcomePage extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Welcome to SSO_Pro1 sample System!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Welcome to SSO_Pro1 sample System!</h1>");
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request);
out.println("<p>The logon user:" + reqWrapper.getRemoteUser() + "</p>");
HttpSession session=request.getSession();
out.println("<p>The logon user:" + session.getAttribute(CASFilter.CAS_FILTER_USER) + "</p>");
out.println("<p>The logon user:" + session.getAttribute("edu.yale.its.tp.cas.client.filter.user") + "</p>");
out.println("</body>");
out.println("</html>");
}
}
web.xml中添加
Code
<servlet>
<servlet-name>WelcomePage</servlet-name>
<servlet-class>servlet.WelcomePage</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>WelcomePage</servlet-name>
<url-pattern>/servlet/welcomePage</url-pattern>
</servlet-mapping>
[8]OK,测试一下
所有的配置结束后,下面我们启动tomcat来测试一下这个demo,浏览器中输入
http://www.test.com:8080/SSO_Pro1/WelcomePage
将跳转到CAS Server的登录界面,
输入用户名密码:arix04/123456,登录成功的话则跳转到
同样,浏览器中输入
http://www.test.com:8080/SSO_Pro2/WelcomePage
直接进入:
到这里,我们的例子已经基本完成了。这里省略了CAS Server的自定义界面配置,有兴趣的朋友可以自己研究一下了。
如果你的测试程序是在不同机器上面部署的话,那么你还需要注意一下:
与 CAS Server 建立信任关系
假设 CAS Server 单独部署在一台机器 A,而客户端应用部署在机器 B 上,由于客户端应用与 CAS Server 的通信采用 SSL,因此,需要在 A 与 B 的 JRE 之间建立信任关系。
首先与 A 机器一样,要生成 B 机器上的证书,配置 Tomcat 的 SSL 协议。其次,下载http://blogs.sun.com/andreas/entry/no_more_unable_to_find 的 InstallCert.java,运行“ java InstallCert compA:8443 ”命令,并且在接下来出现的询问中输入 1。这样,就将 A 添加到了 B 的 trust store 中。如果多个客户端应用分别部署在不同机器上,那么每个机器都需要与 CAS Server 所在机器建立信任关系。
三、备注
表格 1. CASFilter 必需的参数
参数名
作用
edu.yale.its.tp.cas.client.filter.loginUrl
指定 CAS 提供登录页面的 URL
edu.yale.its.tp.cas.client.filter.validateUrl
指定 CAS 提供 service ticket 或 proxy ticket 验证服务的 URL
edu.yale.its.tp.cas.client.filter.serverName
指定客户端的域名和端口,是指客户端应用所在机器而不是 CAS Server 所在机器,该参数或 serviceUrl 至少有一个必须指定
edu.yale.its.tp.cas.client.filter.serviceUrl
该参数指定过后将覆盖 serverName 参数,成为登录成功过后重定向的目的地址
表格 2. CASFilter 可选参数
参数名
作用
edu.yale.its.tp.cas.client.filter.proxyCallbackUrl
用于当前应用需要作为其他服务的代理(proxy)时获取 Proxy Granting Ticket 的地址
edu.yale.its.tp.cas.client.filter.authorizedProxy
用于允许当前应用从代理处获取 proxy tickets,该参数接受以空格分隔开的多个 proxy URLs,但实际使用只需要一个成功即可。当指定该参数过后,需要修改 validateUrl 到 proxyValidate,而不再是 serviceValidate
edu.yale.its.tp.cas.client.filter.renew
如果指定为 true,那么受保护的资源每次被访问时均要求用户重新进行验证,而不管之前是否已经通过
edu.yale.its.tp.cas.client.filter.wrapRequest
如果指定为 true,那么 CASFilter 将重新包装 HttpRequest,并且使 getRemoteUser() 方法返回当前登录用户的用户名
edu.yale.its.tp.cas.client.filter.gateway
指定 gateway 属性
传递登录用户名
CAS 在登录成功过后,会给浏览器回传 Cookie,设置新的到的 Service Ticket。但客户端应用拥有各自的 Session,我们要怎么在各个应用中获取当前登录用户的用户名呢?CAS Client 的 Filter 已经做好了处理,在登录成功后,就可以直接从 Session 的属性中获取,如清单 11 所示:
在 Java 中通过 Session 获取登录用户名
// 以下两者都可以
session.getAttribute(CASFilter.CAS_FILTER_USER);
session.getAttribute("edu.yale.its.tp.cas.client.filter.user");
通过 JSTL 获取登录用户名
<c:out value="${sessionScope[CAS:'edu.yale.its.tp.cas.client.filter.user']}"/>
另外,CAS 提供了一个 CASFilterRequestWrapper 类,该类继承自HttpServletRequestWrapper,主要是重写了 getRemoteUser() 方法,只要在前面配置 CASFilter 的时候为其设置“ edu.yale.its.tp.cas.client.filter.wrapRequest ”参数为 true,就可以通过 getRemoteUser() 方法来获取登录用户名,具体方法如下所示:
通过 CASFilterRequestWrapper 获取登录用户名
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request);
out.println("The logon user:" + reqWrapper.getRemoteUser());
到这里,估计很多朋友肯定想问如何自定义自己的验证机制,比如使用咱们最常用的数据库:用户名/密码,验证的方式,
呵呵,别着急,下面就来和大家说一说如何来自定义CAS Server端的验证机制,咱们来一起实现一个基于mysql数据库 用户名/密码 验证的列子。
首先先和大家介绍一下CAS的扩展认证接口
CAS Server 负责完成对用户的认证工作,它会处理登录时的用户凭证 (Credentials) 信息,用户名/密码对是最常见的凭证信息。CAS Server 可能需要到数据库检索一条用户帐号信息,也可能在 XML 文件中检索用户名/密码,还可能通过 LDAP Server 获取等,在这种情况下,CAS 提供了一种灵活但统一的接口和实现分离的方式,实际使用中 CAS 采用哪种方式认证是与 CAS 的基本协议分离开的,用户可以根据认证的接口去定制和扩展
扩展 AuthenticationHandler
CAS 提供扩展认证的核心是 AuthenticationHandler 接口,该接口定义如下:
Code
public interface AuthenticationHandler {
/**
* Method to determine if the credentials supplied are valid.
* @param credentials The credentials to validate.
* @return true if valid, return false otherwise.
* @throws AuthenticationException An AuthenticationException can contain
* details about why a particular authentication request failed.
*/
boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
* Method to check if the handler knows how to handle the credentials
* provided. It may be a simple check of the Credentials class or something
* more complicated such as scanning the information contained in the
* Credentials object.
* @param credentials The credentials to check.
* @return true if the handler supports the Credentials, false othewrise.
*/
boolean supports(Credentials credentials);
}
该接口定义了 2 个需要实现的方法,supports ()方法用于检查所给的包含认证信息的Credentials 是否受当前 AuthenticationHandler 支持;而 authenticate() 方法则担当验证认证信息的任务,这也是需要扩展的主要方法,根据情况与存储合法认证信息的介质进行交互,返回 boolean 类型的值,true 表示验证通过,false 表示验证失败。
CAS3中还提供了对AuthenticationHandler 接口的一些抽象实现,比如,可能需要在执行authenticate() 方法前后执行某些其他操作,那么可以让自己的认证类扩展下面的抽象类:
Code
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 提供了针对该方式的实现,如下所示:
Code
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。
了解一下上面一段原理后,咱们来写这个实际的例子吧:
cas-server-3.1.1-release.zip 包解开后,在 modules 目录下可以找到包 cas-server-support-jdbc-3.1.1.jar,其提供了通过 JDBC 连接数据库进行验证的缺省实现,基于该包的支持,我们只需要做一些配置工作即可实现 JDBC 认证。
[1].给出mysql建表语句,很简单,就一张user表,其他数据库也一样。
Code
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(20) NOT NULL,
`password` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'arix04', 'e10adc3949ba59abbe56e057f20f883e');
这里插了一条记录,用户名/密码:arix04/123456;使用的md5加密。
[2].配置
DataSource
找到tomcat/webapps/cas/WEB-INF目录(这里建议在做一下操作时先删除cas.war,只留下已经解包的cas工程)
找到deployerConfigContext.xml文件插入下面这段:
Code
<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://localhost:3306/cas_test</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>root</value>
</property>
</bean>
[3].配置 AuthenticationHandler
在 cas-server-support-jdbc-3.1.1.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
deployerConfigContext.xml文件插入下面这段:
Code
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref="casDataSource" />
<property name="sql" value="select password from user where username = ?" />
<property name="passwordEncoder" ref="myPasswordEncoder"/>
</bean>
上面这段,sql定义了一个查询语句,用来判断用户名,密码是否存在,myPasswordEncoder是我自定义的一个密码的加密类,实现了passwordEncoder接口及其 encode() 方法。
[4].配置passwordEncoder
deployerConfigContext.xml文件插入下面这段:
<bean id="myPasswordEncoder" class="org.jasig.cas.authentication.handler.MyPasswordEncoder"/>
[5].MyPasswordEncoder
给出源码,大家自己编译成class吧,然后把MyPasswordEncoder.class放到
Tomcat 6.0\webapps\cas\WEB-INF\lib\cas-server-core-3.3.1.jar中相应的包下,jar包用winrar打开后,直接把class拖到相应目录下即可。
Code
package org.jasig.cas.authentication.handler;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.util.StringUtils;
// Referenced classes of package org.jasig.cas.authentication.handler:
// PasswordEncoder
public final class MyPasswordEncoder
implements PasswordEncoder
{
public MyPasswordEncoder(){};
public String encode(String password)
{
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] strTemp = password.getBytes();
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
public final static String MD5(String s) {
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] strTemp = s.getBytes();
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
public static Date getDateByString(String dateString) {
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.parse(dateString);
} catch (Exception e) {
return null;
}
}
public static String getDateString(Date date) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.format(date);
}
}
[6].放入必要的Jar
将开发准备中的need.rar解压后的Jar文件、cas-server-support-jdbc-3.3.1.jar copy到Tomcat 6.0\webapps\cas\WEB-INF\lib\下面,如果你是用的其他数据库,则需要放入相应的数据库驱动包,
我这里给出的是mysql的驱动包。
[7].新建2个测试servlet
分别在上面SSO_Pro1,SSO_Pro2中新建class
WelcomePage
Code
package servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import edu.yale.its.tp.cas.client.filter.CASFilter;
import edu.yale.its.tp.cas.client.filter.CASFilterRequestWrapper;
public class WelcomePage extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Welcome to SSO_Pro1 sample System!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Welcome to SSO_Pro1 sample System!</h1>");
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request);
out.println("<p>The logon user:" + reqWrapper.getRemoteUser() + "</p>");
HttpSession session=request.getSession();
out.println("<p>The logon user:" + session.getAttribute(CASFilter.CAS_FILTER_USER) + "</p>");
out.println("<p>The logon user:" + session.getAttribute("edu.yale.its.tp.cas.client.filter.user") + "</p>");
out.println("</body>");
out.println("</html>");
}
}
web.xml中添加
Code
<servlet>
<servlet-name>WelcomePage</servlet-name>
<servlet-class>servlet.WelcomePage</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>WelcomePage</servlet-name>
<url-pattern>/servlet/welcomePage</url-pattern>
</servlet-mapping>
[8]OK,测试一下
所有的配置结束后,下面我们启动tomcat来测试一下这个demo,浏览器中输入
http://www.test.com:8080/SSO_Pro1/WelcomePage
将跳转到CAS Server的登录界面,
输入用户名密码:arix04/123456,登录成功的话则跳转到
同样,浏览器中输入
http://www.test.com:8080/SSO_Pro2/WelcomePage
直接进入:
到这里,我们的例子已经基本完成了。这里省略了CAS Server的自定义界面配置,有兴趣的朋友可以自己研究一下了。
如果你的测试程序是在不同机器上面部署的话,那么你还需要注意一下:
与 CAS Server 建立信任关系
假设 CAS Server 单独部署在一台机器 A,而客户端应用部署在机器 B 上,由于客户端应用与 CAS Server 的通信采用 SSL,因此,需要在 A 与 B 的 JRE 之间建立信任关系。
首先与 A 机器一样,要生成 B 机器上的证书,配置 Tomcat 的 SSL 协议。其次,下载http://blogs.sun.com/andreas/entry/no_more_unable_to_find 的 InstallCert.java,运行“ java InstallCert compA:8443 ”命令,并且在接下来出现的询问中输入 1。这样,就将 A 添加到了 B 的 trust store 中。如果多个客户端应用分别部署在不同机器上,那么每个机器都需要与 CAS Server 所在机器建立信任关系。
三、备注
表格 1. CASFilter 必需的参数
参数名
作用
edu.yale.its.tp.cas.client.filter.loginUrl
指定 CAS 提供登录页面的 URL
edu.yale.its.tp.cas.client.filter.validateUrl
指定 CAS 提供 service ticket 或 proxy ticket 验证服务的 URL
edu.yale.its.tp.cas.client.filter.serverName
指定客户端的域名和端口,是指客户端应用所在机器而不是 CAS Server 所在机器,该参数或 serviceUrl 至少有一个必须指定
edu.yale.its.tp.cas.client.filter.serviceUrl
该参数指定过后将覆盖 serverName 参数,成为登录成功过后重定向的目的地址
表格 2. CASFilter 可选参数
参数名
作用
edu.yale.its.tp.cas.client.filter.proxyCallbackUrl
用于当前应用需要作为其他服务的代理(proxy)时获取 Proxy Granting Ticket 的地址
edu.yale.its.tp.cas.client.filter.authorizedProxy
用于允许当前应用从代理处获取 proxy tickets,该参数接受以空格分隔开的多个 proxy URLs,但实际使用只需要一个成功即可。当指定该参数过后,需要修改 validateUrl 到 proxyValidate,而不再是 serviceValidate
edu.yale.its.tp.cas.client.filter.renew
如果指定为 true,那么受保护的资源每次被访问时均要求用户重新进行验证,而不管之前是否已经通过
edu.yale.its.tp.cas.client.filter.wrapRequest
如果指定为 true,那么 CASFilter 将重新包装 HttpRequest,并且使 getRemoteUser() 方法返回当前登录用户的用户名
edu.yale.its.tp.cas.client.filter.gateway
指定 gateway 属性
传递登录用户名
CAS 在登录成功过后,会给浏览器回传 Cookie,设置新的到的 Service Ticket。但客户端应用拥有各自的 Session,我们要怎么在各个应用中获取当前登录用户的用户名呢?CAS Client 的 Filter 已经做好了处理,在登录成功后,就可以直接从 Session 的属性中获取,如清单 11 所示:
在 Java 中通过 Session 获取登录用户名
// 以下两者都可以
session.getAttribute(CASFilter.CAS_FILTER_USER);
session.getAttribute("edu.yale.its.tp.cas.client.filter.user");
通过 JSTL 获取登录用户名
<c:out value="${sessionScope[CAS:'edu.yale.its.tp.cas.client.filter.user']}"/>
另外,CAS 提供了一个 CASFilterRequestWrapper 类,该类继承自HttpServletRequestWrapper,主要是重写了 getRemoteUser() 方法,只要在前面配置 CASFilter 的时候为其设置“ edu.yale.its.tp.cas.client.filter.wrapRequest ”参数为 true,就可以通过 getRemoteUser() 方法来获取登录用户名,具体方法如下所示:
通过 CASFilterRequestWrapper 获取登录用户名
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request);
out.println("The logon user:" + reqWrapper.getRemoteUser());
相关推荐
独子棋demo.rar
云安全联盟软件定义边界SDP标准规范2.0202239页.pdf
Uniapp开发的微商个人相册多端小程序源码。使用 HBuilder X 导入本地项目,修改小程序AppID,以及Uni-app应用标识,调试发布即可。 小程序源码特点: 1、首页进行相册展示,采用分页 2、列表页面以文字形式进行分类,管理员可进行添加,修改和排序 3、每个列表下有多个相册,管理员可进行添加,修改和排序 4、每个相册有多张图片,有小图和大图模式进行切换 5、相册中可以长按图片进行选择删除和设为封面 6、相册可以进行分享 7、我的页面有管理员登录,联系客服等功能
内容概要:本文详细介绍了基于FPGA的144输出通道可切换电压源系统的设计与实现,涵盖系统总体架构、FPGA硬件设计、上位机软件设计以及系统集成方案。系统由上位机控制软件(PC端)、FPGA控制核心和高压输出模块(144通道)三部分组成。FPGA硬件设计部分详细描述了Verilog代码实现,包括PWM生成模块、UART通信模块和温度监控模块。硬件设计说明中提及了FPGA选型、PWM生成方式、通信接口、高压输出模块和保护电路的设计要点。上位机软件采用Python编写,实现了设备连接、命令发送、序列控制等功能,并提供了一个图形用户界面(GUI)用于方便的操作和配置。 适合人群:具备一定硬件设计和编程基础的电子工程师、FPGA开发者及科研人员。 使用场景及目标:①适用于需要精确控制多通道电压输出的实验环境或工业应用场景;②帮助用户理解和掌握FPGA在复杂控制系统中的应用,包括PWM控制、UART通信及多通道信号处理;③为研究人员提供一个可扩展的平台,用于测试和验证不同的电压源控制算法和策略。 阅读建议:由于涉及硬件和软件两方面的内容,建议读者先熟悉FPGA基础知识和Verilog语言,同时具备一定的Python编程经验。在阅读过程中,应结合硬件电路图和代码注释,逐步理解系统的各个组成部分及其相互关系。此外,实际动手搭建和调试该系统将有助于加深对整个设计的理解。
地级市政府通过制定相关政策来推动数字经济的发展和数字政府的建设。这些政策可能包括鼓励企业数字化转型、促进数字技术创新、加强数字基础设施建设、优化数字政务服务等方面的内容。政策制定的频率和力度,可以在一定程度上反映政府对数字领域的关注度。 在地级市政府数字关注度的背景下,词频分析成为了一种有效的工具,用以衡量政府文件和宣传资料中涉及数字技术和数字化转型相关词汇的频次,进而揭示政府对这一领域的关注程度和重视方向。 数据名称:地级市-政府数字关注度、词频
内容概要:本文详细探讨了在Android平台上进行图像模板匹配的技术挑战和解决方案,特别是在处理不同尺寸和旋转角度的目标物时的方法。文中介绍了使用OpenCV构建图像金字塔、处理旋转模板以及利用NEON指令集优化性能的具体实现。此外,文章还讨论了在armeabi-v7a和arm64-v8a这两种主要ARM架构下的优化技巧,如内存对齐、SIMD指令优化、RenderScript并行处理等。作者分享了许多实践经验,包括如何避免常见的性能瓶颈和兼容性问题。 适合人群:有一定Android开发经验,尤其是熟悉OpenCV和NDK编程的中级及以上开发者。 使用场景及目标:适用于需要在移动设备上进行高效图像识别的应用开发,如实时视频流中的物体检测、游戏内的道具识别等。目标是提高模板匹配的速度和准确性,同时确保在不同硬件配置下的稳定性和兼容性。 其他说明:文章提供了丰富的代码片段和实际案例,帮助读者更好地理解和应用所介绍的技术。特别强调了在不同ARM架构下的优化策略,为开发者提供了宝贵的参考资料。
内容概要:本文探讨了一种改进的粒子群优化(PSO)算法在微电网多目标优化调度中的应用。传统PSO在解决此类复杂问题时常陷入局部最优解,而改进版通过引入动态惯性因子和自适应变异操作,显著提升了算法性能。文中详细介绍了这两种改进措施的具体实现方法及其对算法收敛性和解质量的影响。此外,还展示了该算法在实际微电网调度任务中的表现,特别是在权衡经济成本与环境效益方面的能力。 适合人群:从事电力系统优化、智能电网研究的专业人士以及对进化算法感兴趣的学者和技术人员。 使用场景及目标:适用于需要进行高效能源管理的场合,如分布式发电系统的规划与运行。主要目的是寻找既能降低成本又能减少环境污染的最佳调度方案。 其他说明:文中提供了大量伪代码片段帮助读者理解具体的技术细节,并强调了参数调节对于最终结果的重要性。同时指出,该方法不仅限于微电网领域,还可以扩展应用于其他类型的优化问题。
Delphi 12.3控件之TeeChart Offline Keygen.7z
内容概要:本文详细介绍了如何利用MATLAB进行屈光度计算及其数据处理方法。首先解释了屈光度的基本概念和计算公式,接着展示了如何通过MATLAB代码读取、清理和转换焦距数据为屈光度,并进行了必要的单位转换。针对可能出现的异常值和噪声,文中提供了有效的数据清洗手段。此外,还探讨了如何对屈光度数据进行统计分析以及可视化呈现,如绘制趋势图和散点图等。最后,提到了将MATLAB代码转化为C++代码以便集成到硬件系统的高级应用。 适合人群:从事光学研究、眼科医疗设备开发的技术人员,以及对MATLAB有兴趣的学习者。 使用场景及目标:适用于需要精确处理和分析光学数据的研究机构或企业,旨在提高屈光度计算的效率和准确性,确保数据质量的同时优化实验结果。 其他说明:文中不仅涵盖了基本的操作步骤,还包括了许多实用的小贴士和技术细节,有助于读者更好地理解和掌握相关内容。同时强调了单位一致性的重要性,提醒开发者注意潜在的问题。
349421c2-4955-4132-b4da-808a3a171bfe.pdf
1744300906657718_download.jsp
【内容概要】 本文详细解析了企业筛选简历的“三重门”系统,包括ATS系统初筛、HR复核和业务部门终极评估三个阶段。首先,ATS系统作为关键词匹配引擎,强调了关键词的重要性及其优化方法;其次,HR在6秒内通过“薄片判断”评估简历的职业连贯性、成就量化和岗位匹配度;最后,业务部门则侧重于技术能力和文化适配性的综合评估。文章还揭示了各环节中的心理学原理和认知偏差,并提供了针对性的优化建议。 【适合人群】 正在求职或有求职打算的职场人士,尤其是希望提升简历通过率的求职者。 【使用场景及目标】 ①帮助求职者理解企业筛选简历的具体流程; ②提供简历优化的具体方法,如关键词优化、成就量化、案例准备等; ③指导求职者如何根据不同阶段的评审特点调整简历内容。 【其他说明】 文章结合了最新的招聘趋势研究报告和心理学理论,强调简历不仅是通过筛选的工具,更是展示个人能力和价值的平台。求职者应充分利用这些心理规律,打造更具吸引力的简历,为后续面试做好铺垫。
内容概要:本文详细介绍了使用PFC2D5.0进行二维岩石单轴压缩模拟的具体方法和代码实现。首先,通过设定模型的基本参数如颗粒生成、粘结设置、加载控制等,构建了一个完整的岩石样品模型。接着,深入探讨了加载过程中应力应变曲线的变化规律以及能量分析的方法,包括弹性应变能、动能和耗散能的监测。此外,还提供了裂隙统计的技术手段,能够精确捕捉岩石内部裂隙的发展情况。最后,强调了参数调整对模拟效果的影响,并给出了优化建议。 适合人群:从事岩土工程、地质力学研究的专业人士和技术爱好者。 使用场景及目标:适用于需要深入了解岩石力学特性的研究人员,帮助他们掌握PFC2D软件的应用技巧,提升科研能力。同时,也为相关领域的学生提供了一套实用的学习资料。 其他说明:文中提供的代码可以直接应用于PFC2D5.0环境,便于用户快速上手并进行实验验证。通过对不同参数的调整,可以模拟多种类型的岩石破坏行为,为实际工程项目提供理论支持。
内容概要:本文详细介绍了如何使用Fluent进行激光焊接的数值模拟,重点讲解了锥形高斯热源的建模方法。文章首先解释了锥形高斯热源的特点及其与普通高斯热源的区别,然后给出了具体的UDF代码实现,包括热源强度的计算、热流衰减的控制以及热源移动的实现。此外,还讨论了网格划分、材料参数设置、常见错误排查和优化技巧等方面的内容。通过实例和操作视频,帮助读者快速掌握激光焊接数值模拟的方法和技术要点。 适合人群:具有一定CFD基础并希望深入学习激光焊接数值模拟的研究人员和工程师。 使用场景及目标:适用于需要精确模拟激光焊接过程的研究项目或工业应用,旨在提高模拟精度,减少试验成本,优化焊接工艺参数。 其他说明:文中提供了大量实用的操作技巧和注意事项,如网格划分建议、材料参数选择、UDF代码调试等,有助于解决实际操作中可能遇到的问题。同时,附带的操作视频和GitHub上的完整案例包也为初学者提供了宝贵的学习资源。
序列化.md
"ResumePlatformFront 笔试面试全攻略与资源宝典"——一站式前端求职解决方案!精选高频笔试真题解析、大厂面试经验分享、实战项目模板及技能进阶指南,助你系统攻克前端求职难关。从简历优化到Offer谈判,覆盖求职全流程,配套免费资源库持续更新。无论应届生还是进阶开发者,这里都是你斩获心仪Offer的强力后盾!
weixin205微信小程序线上教育商城ssm(文档+源码)_kaic
内容概要:本文详细介绍了如何利用COMSOL软件构建岩石损伤与温度、渗流耦合的多物理场模型。首先解释了温度变化引起岩石膨胀/收缩以及渗流压力改变裂纹发展的物理机制,并通过PDE方程组进行描述。接着展示了具体的实现方法,如定义损伤变量、设置导热系数和渗透率随损伤变化的关系,以及引入温度修正的Mohr-Coulomb准则。文中还讨论了求解器配置技巧,强调了非线性收敛问题的解决方案。此外,作者分享了一些实际建模过程中遇到的问题及解决经验,如参数选择不当导致的模型发散等。 适合人群:从事岩土工程、地质工程及相关领域的研究人员和技术人员,特别是对多物理场耦合仿真感兴趣的学者。 使用场景及目标:适用于需要深入理解岩石在温度、渗流和应力共同作用下的损伤演化规律的研究项目。目标是帮助读者掌握COMSOL中多物理场耦合模型的建立方法,提高数值模拟的准确性。 其他说明:文章不仅提供了理论背景,还包括大量实用的代码片段和调试建议,有助于读者更好地理解和应用所学知识。
2023-04-06-项目笔记-第四百六十四阶段-课前小分享_小分享1.坚持提交gitee 小分享2.作业中提交代码 小分享3.写代码注意代码风格 4.3.1变量的使用 4.4变量的作用域与生命周期 4.4.1局部变量的作用域 4.4.2全局变量的作用域 4.4.2.1全局变量的作用域_1 4.4.2.462局变量的作用域_462- 2025-04-10
内容概要:本文详细介绍了基于滑膜观测器的永磁同步电机(PMSM)无传感器控制技术及其在MATLAB/Simulink中的仿真实现。首先阐述了PMSM的特点及其在现代工业中的重要地位,接着重点讲解了转子磁场定向矢量控制(FOC)的工作原理,特别是电流环的设计和电压解耦的作用。然后深入探讨了一阶滑膜观测器的实现方法,展示了如何通过电机的电压和电流信号估计转子位置和速度。最后,通过搭建完整的Simulink仿真模型并运行仿真,评估了控制策略的性能,并提供了配套的英文文献以供进一步研究。 适合人群:从事电机控制系统设计的研发工程师和技术爱好者,尤其是对无传感器控制技术和滑膜观测器感兴趣的读者。 使用场景及目标:适用于希望深入了解PMSM无传感器控制技术的工程师,旨在帮助他们掌握滑膜观测器的设计和实现,提高系统的可靠性和降低成本。同时,也为后续的实际应用和优化提供了理论依据和技术支持。 其他说明:文中提供的代码片段和仿真模型有助于读者更好地理解和实践相关技术,而配套的英文文献则为深入研究提供了宝贵的参考资料。