- 浏览: 5943 次
- 性别:
- 来自: 西安
最新评论
技术背景知识:
JA-SIG CAS服务环境搭建,请参考 :JA-SIG(CAS)学习笔记1
JA-SIG CAS业务架构介绍,请参考 :JA-SIG(CAS)学习笔记2
HTTPS所涉及的Java安全证书知识,请参考 :Java keytool 安全证书学习笔记
CAS技术框架
CAS Server
目前,我们使用的CAS Server 3.1.1的是基于Spring Framework编写的,因此在CAS服务器端的配置管理中,绝大多数是Spring式的Java Bean XML配置。CAS 的服务器提供了一套易于定制的用户认证器接口,用户可以根据自身企业的在线系统的认证方式,来定制自己的认证逻辑。不论是传统的用户名/密码方式,还是基于安全证书的方式;是基于关系数据库的存储,还是采用LDAP服务器,CAS Server给我们提供了这些常用的验证器模板代码,只要稍作修改,便可灵活使用了。
对于广大的中国企业用户而言,另一个需要定制的功能莫过于全中文、企业特色的用户身份认证页面了。CAS Server提供了两套系统界面,一套是默认的CAS英文标准页面,另一套则是专门提供给用户来定制修改的。(PS:老外们做事情就是人性化啊~~)那么对CAS Server端的后续学习,我们将围绕着身份认证模块定制和界面定制这两方面展开。
CAS Client
客户端我们使用的是CAS Client 2.1.1。虽然在官方网站上已出现了3.1.0版本的下载,但该版本地代码已经完全重写,使用的package和类名同2.1.1大相径庭了,最关键的是,该版本暂时没有对应的API说明文档。虽然咖啡我对程序版本怀有极大的“喜新厌旧”的心态,但安全起见,还是先2.1.1吧,相信3.1.0的文档耶鲁大学的大牛们已经在整理了,期待中……
CAS Client2.1.1.jar中的代码是相当精炼的,有兴趣的朋友建议阅读一下源码。Jar包中的代码分成三个大部分
1. edu.yale.its.tp.cas.util 包,其中只有一个工具类 SecureURL.java 用来访问HTTPS URL
2. edu.yale.its.tp.cas.proxy包,用来处理Proxy Authentication代理认证的3个类,其中ProxyTicketReceptor.java是 接收PGT回调的servlet,在下文中我们会提及。
3. edu.yale.its.tp.cas.client包,其中包含了CAS Filter ,Tag Library等主要的认证客户端工具类,我们在后面会进行重点介绍。
针对CAS Client的学习,我们的重点将放在CAS Filter 和ProxyTicketReceptor 的配置以及在Java SE环境下,直接使用 ServiceTicketValidator进行Ticket认证实现上。
CAS服务器端应用
定制适合你的身份认证程序
通过前面的学习,我们了解了CAS具有一个良好而强大的SSO功能框架。接下来,我们要学习如何将实际企业应用中的身份认证同CAS进行整合。
简单的说,要将现有企业应用中的认证集成到CAS Server中,只要实现一个名为AuthenticationHandler的一个认证处理Java接口就行。以下是该接口的源代码:
Java代码
public interface AuthenticationHandler {
/**
* 该方法决定一个受支持的credentials是否是可用的,
* 如果可用,该方法返回true,则说明身份认证通过
*/
boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
* 该方法决定一个credentials是否是当前的handle所支持的
*/
boolean supports(Credentials credentials);
}
public interface AuthenticationHandler {
/**
* 该方法决定一个受支持的credentials是否是可用的,
* 如果可用,该方法返回true,则说明身份认证通过
*/
boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
* 该方法决定一个credentials是否是当前的handle所支持的
*/
boolean supports(Credentials credentials);
}
这里我们要说明一下Credentials这个CAS的概念。所谓Credentials是由外界提供给CAS来证明自身身份的信息,简单的如一个用户名/密码对就是一个Credentials,或者一个经过某种加密算法生成的密文证书也可以是一个Credentials。在程序的实现上,Credentials被声明为一个可序列化的接口,仅仅起着标识作用,源代码如下:
Java代码
public interface Credentials extends Serializable {
// marker interface contains no methods
}
public interface Credentials extends Serializable {
// marker interface contains no methods
}
CAS的API中,已经为我们提供了一个最常用的实现UsernamePasswordCredentials 用户名/密码凭证,代码如下:
Java代码
public class UsernamePasswordCredentials implements Credentials {
/** Unique ID for serialization. */
private static final long serialVersionUID = -8343864967200862794L;
/** The username. */
private String username;
/** The password. */
private String password;
public final String getPassword() {
return this.password;
}
public final void setPassword(final String password) {
this.password = password;
}
public final String getUsername() {
return this.username;
}
public final void setUsername(final String userName) {
this.username = userName;
}
public String toString() {
return this.username;
}
public boolean equals(final Object obj) {
if (obj == null || !obj.getClass().equals(this.getClass())) {
return false;
}
final UsernamePasswordCredentials c = (UsernamePasswordCredentials) obj;
return this.username.equals(c.getUsername())
&& this.password.equals(c.getPassword());
}
public int hashCode() {
return this.username.hashCode() ^ this.password.hashCode();
}
}
public class UsernamePasswordCredentials implements Credentials {
/** Unique ID for serialization. */
private static final long serialVersionUID = -8343864967200862794L;
/** The username. */
private String username;
/** The password. */
private String password;
public final String getPassword() {
return this.password;
}
public final void setPassword(final String password) {
this.password = password;
}
public final String getUsername() {
return this.username;
}
public final void setUsername(final String userName) {
this.username = userName;
}
public String toString() {
return this.username;
}
public boolean equals(final Object obj) {
if (obj == null || !obj.getClass().equals(this.getClass())) {
return false;
}
final UsernamePasswordCredentials c = (UsernamePasswordCredentials) obj;
return this.username.equals(c.getUsername())
&& this.password.equals(c.getPassword());
}
public int hashCode() {
return this.username.hashCode() ^ this.password.hashCode();
}
}
很简单不是吗?就是存储一个用户名和密码的java bean而已。
接下来,我们将一个Credentials传给一个AuthenticationHandler进行认证,首先调用boolean supports(Credentials credentials)方法察看当前传入的Credentials实例,AuthenticationHandler实例现是否支持它?如果支持,再调用boolean authenticate(Credentials credentials)方法进行认证。由于用户名/密码方式是最常用的认证方法,因此CAS为我们提供了一个现成的基于该方式的抽象认证处理类AbstractUsernamePasswordAuthenticationHandler。通常我们只需要继承该类,并实现其中的 authenticateUsernamePasswordInternal方法即可。下面我们给出一个Demo的实现类,它的校验逻辑很简单——仅校验用户名的字符长度是否与密码的相等(这里密码是一个表示长度的整数),如果相等则认为认证通过,请看代码:
Java代码
public class UsernameLengthAuthnHandler
extends AbstractUsernamePasswordAuthenticationHandler {
protected boolean authenticateUsernamePasswordInternal( UsernamePasswordCredentials credentials) throws AuthenticationException {
/*
* 这里我们完全可以用自己的认证逻辑代替,比如将用户名/密码传入一个SQL语句
* 向数据库验证是否有对应的用户账号,这不是我们最经常干的事么?
* 只需要将下面的程序替换掉就OK了!!So easy,so simple!
/
String username = credentials.getUsername();
String password = credentials.getPassword();
String correctPassword = Integer.toString(username.length());
return correctPassword.equals(password);
}
}
public class UsernameLengthAuthnHandler
extends AbstractUsernamePasswordAuthenticationHandler {
protected boolean authenticateUsernamePasswordInternal( UsernamePasswordCredentials credentials) throws AuthenticationException {
/*
* 这里我们完全可以用自己的认证逻辑代替,比如将用户名/密码传入一个SQL语句
* 向数据库验证是否有对应的用户账号,这不是我们最经常干的事么?
* 只需要将下面的程序替换掉就OK了!!So easy,so simple!
/
String username = credentials.getUsername();
String password = credentials.getPassword();
String correctPassword = Integer.toString(username.length());
return correctPassword.equals(password);
}
}
介绍到这里,大家应该清楚如何定制自己的AuthenticationHandler类了吧!这里要附带说明的是,在CAS Server的扩展API中已经提供了大量常用认证形式的实现类,它们同CAS Server的war包一同分发:
cas-server-support-generic-3.1.1.jar ——使用Map记录用户认证信息的实现
cas-server-support-jdbc-3.1.1.jar —— 基于Spring JDBC的数据库实现(我们常用的)
cas-server-support-ldap-3.1.1.jar —— 基于LDAP的用户认证实现
更多其他形式的实现各位看官有兴趣的,可以一一阅读源码。
配置你的身份认证程序
完成了定制认证类的代码编写,接下来就是要让CAS Server来调用它了。在CAS的框架中,对程序的配置都是使用Spring Framework的xml文件,这对于熟悉Spring的程序员而言算驾轻就熟了。
配置文件位于应用部署目录的WEB-INF子目录下——deployerConfigContext.xml。在bean id=authenticationManager 的 authenticationHandlers属性中配置我们的AuthenticationHandlers:
引用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="authenticationManager"
class="org.jasig.cas.authentication.AuthenticationManagerImpl">
。。。
。。。
<property name="authenticationHandlers">
<list>
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref="httpClient" />
<!—下面就是系统默认的验证器配置,你可以替换它,或者增加一个新的handler -->
<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
</list>
</property>
</bean>
。。。
。。。
</beans>
我们发现authenticationHandlers属性是一个list,在这个list中可以配置多个AuthenticationHandlers。这些AuthenticationHandlers形成了一个验证器链,所有提交给CAS的Credentials信息将通过这个验证器链的链式过滤,只要这链中有一个验证器通过了对Credentials的验证,就认为这个Credentials是合法的。这样的设计使得我们可以很轻松的整合不同验证体系的已有应用到同一个CAS上,比如:A验证器负责校验alpha系统提交的Credentials,它是基于LDAP服务器的;B验证器负责校验beta系统提交的Credentials,它是一个传统的RDB用户表认证;C验证器负责校验gamma系统提交的基于RSA证书加密的Credentials。3种完全不同的用户身份认证通过配置就可以统一在同一个CAS服务内,很好很强大,不是吗!!
定制身份验证登录界面
CAS Server在显示界面层view使用了“主题Theme”的概念。在{project.home}/webapp/WEB-INF/view/jsp/目录下,系统默认提供了两套得UI —— default和simple 。default方案使用了CSS等相对复杂得界面元素,而simple方案提供了最简化的界面表示方式。在整个的CAS Server服务器端,有四个界面是我们必须要实现的:
casConfirmView.jsp —— 确认信息(警告信息)页面
casGenericSuccess.jsp —— 登陆成功提示页面
casLoginView.jsp —— 登录输入页面
casLogoutView.jsp —— SSO登出提示页面
这些都是标准的jsp页面,如何实现他们,完全由您说了算,除了名字不能改。
CAS为view的展示提供了3个级别的定制方式,让我们从最直观简单的开始吧。
1. 采用文件覆盖方式:直接修改default中的页面或者将新写好的四个jsp文件覆盖到default目录中。这种方式最直观和简单,但咖啡建议各位在使用这种方式前将原有目录中的文件备份一下,以备不时之需。
2. 修改UI配置文件,定位UI目录:在CAS Server端/webapp/WEB-INF/classes/ 目录下,有一个名为default_views.properties的属性配置文件,你可以通过修改配置文件中的各个页面文件位置,指向你新UI文件,来达到修改页面展示的目的。
3. 修改配置文件的配置文件,这话看起来有点别扭,其实一点不难理解。在方法2中的default_views.properties文件是一整套的UI页面配置。如果我想保存多套的UI页面配置就可以写多个的properties文件来保存这些配置。在CAS Server端/webapp/WEB-INF/目录下有cas-servlet.xml和cas.properties两个文件,cas-servlet.xml使用了cas.properties文件中的cas.viewResolver.basename属性来定义view属性文件的名字,因此你可以选者直接修改cas-servlet.xml中的viewResolver 下的basenames属性,或者修改cas.properties中的cas.viewResolver.basename属性,指定新的properties文件名,这样可以轻松的替换全套UI。
CAS客户端配置及API应用
CASFilter的配置
对于大部分web应用而言,使用CAS集成统一认证是相对简单的事,只要为需要认证的URL配置edu.yale.its.tp.cas.client.filter.CASFilter认证过滤器。下面我们就针对过滤器的配置进行说明。首先参看一下Filter的基本配置:
引用
<web-app>
...
<filter>
<filter-name>CAS Filter</filter-name>
<filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
<param-value>https://secure.its.yale.edu/cas/login<;/param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
<param-value>https://secure.its.yale.edu/cas/serviceValidate<;/param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
<param-value>your server name and port (e.g., www.yale.edu:8080)</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Filter</filter-name>
<url-pattern>/requires-cas-authetication/*</url-pattern>
</filter-mapping>
...
</web-app>
上述配置中的init-param是filter的3个必备的属性,下面这张表则是filter全部属性的详细说明:
ProxyTicketReceptor的配置
大家还记得在前面我们说过的Proxy Authentication中的call back URL吗?ProxyTicketReceptor是部署在client端的一个servlet,提供server端回传PGT和PGTIOU的。它的xml部署如下:
引用
<web-app>
...
<servlet>
<servlet-name>ProxyTicketReceptor</servlet-name>
<servlet-class>edu.yale.its.tp.cas.proxy.ProxyTicketReceptor</servlet-class>
<init-param>
<param-name>edu.yale.its.tp.cas.proxyUrl</param-name>
<param-value>https://secure.its.yale.edu/cas/proxy<;/param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ProxyTicketReceptor</servlet-name>
<url-pattern>/CasProxyServlet</url-pattern>
</servlet-mapping>
...
</webapp>
这里要说明的是它的参数edu.yale.its.tp.cas.proxyUrl。在服务端通过ProxyTicketReceptor将PGT和PGTIOU传给客户端后,ProxyTicketReceptor在进行Proxy Authentication的过程中需要向服务端请求一个ProxyTicket(PT),这个proxyUrl就是服务端的请求入口了。(关于Proxy Authentication的运作原理,参见JA-SIG(CAS)学习笔记2 )
CAS Client端的API应用1.用户可以通过以下两种方式的任意一种,从JSP或servlet中获取通过认证的用户名:
引用
String username = (String)session.getAttribute(CASFilter.CAS_FILTER_USER);
或者
String username = (String)session.getAttribute("edu.yale.its.tp.cas.client.filter.user");
2.获得更完整的受认证用户信息对象CASReceipt Java Bean,可以使用以下语句的任一:
引用
CASReceipt receipt = (CASReceipt )session.getAttribute(CASFilter.CAS_FILTER_RECEIPT);
或者
CASReceipt receipt = (CASReceipt )session.getAttribute("edu.yale.its.tp.cas.client.filter.receipt");
3.手工编码使用CAS Java Object进行用户验证,使用ServiceTicketValidator或者 ProxyTicketValidator(代理认证模式下),在servlet中对用户身份进行验证。
3-1.ServiceTicketValidator
Java代码
import edu.yale.its.tp.cas.client.*;
...
String user = null;
String errorCode = null;
String errorMessage = null;
String xmlResponse = null;
/* instantiate a new ServiceTicketValidator */
ServiceTicketValidator sv = new ServiceTicketValidator();
/* set its parameters */
sv.setCasValidateUrl("https://secure.its.yale.edu/cas/serviceValidate");
sv.setService(urlOfThisService);
sv.setServiceTicket(request.getParameter("ticket"));
String urlOfProxyCallbackServlet = "https://portal.yale.edu/CasProxyServlet";
sv.setProxyCallbackUrl(urlOfProxyCallbackServlet);
/* contact CAS and validate */
sv.validate();
/* if we want to look at the raw response, we can use getResponse() */
xmlResponse = sv.getResponse();
if(sv.isAuthenticationSuccesful()) {
user = sv.getUser();
} else {
errorCode = sv.getErrorCode();
errorMessage = sv.getErrorMessage();
}
/* The user is now authenticated. */
/* If we did set the proxy callback url, we can get proxy tickets with: */
String urlOfTargetService = "http://hkg2.its.yale.edu/someApp/portalFeed";
String proxyTicket = ProxyTicketReceptor.getProxyTicket( sv.getPgtIou() , urlOfTargetService);
import edu.yale.its.tp.cas.client.*;
...
String user = null;
String errorCode = null;
String errorMessage = null;
String xmlResponse = null;
/* instantiate a new ServiceTicketValidator */
ServiceTicketValidator sv = new ServiceTicketValidator();
/* set its parameters */
sv.setCasValidateUrl("https://secure.its.yale.edu/cas/serviceValidate");
sv.setService(urlOfThisService);
sv.setServiceTicket(request.getParameter("ticket"));
String urlOfProxyCallbackServlet = "https://portal.yale.edu/CasProxyServlet";
sv.setProxyCallbackUrl(urlOfProxyCallbackServlet);
/* contact CAS and validate */
sv.validate();
/* if we want to look at the raw response, we can use getResponse() */
xmlResponse = sv.getResponse();
if(sv.isAuthenticationSuccesful()) {
user = sv.getUser();
} else {
errorCode = sv.getErrorCode();
errorMessage = sv.getErrorMessage();
}
/* The user is now authenticated. */
/* If we did set the proxy callback url, we can get proxy tickets with: */
String urlOfTargetService = "http://hkg2.its.yale.edu/someApp/portalFeed";
String proxyTicket = ProxyTicketReceptor.getProxyTicket( sv.getPgtIou() , urlOfTargetService);
3-2.ProxyTicketValidator
Java代码
import edu.yale.its.tp.cas.client.*;
...
String user = null;
String errorCode = null;
String errorMessage = null;
String xmlResponse = null;
List proxyList = null;
/* instantiate a new ProxyTicketValidator */
ProxyTicketValidator pv = new ProxyTicketValidator();
/* set its parameters */
pv.setCasValidateUrl("https://secure.its.yale.edu/cas/proxyValidate");
pv.setService(urlOfThisService);
pv.setServiceTicket(request.getParameter("ticket"));
String urlOfProxyCallbackServlet = "https://portal.yale.edu/CasProxyServlet";
pv.setProxyCallbackUrl(urlOfProxyCallbackServlet);
/* contact CAS and validate */
pv.validate();
/* if we want to look at the raw response, we can use getResponse() */
xmlResponse = pv.getResponse();
/* read the response */
if(pv.isAuthenticationSuccesful()) {
user = pv.getUser();
proxyList = pv.getProxyList();
} else {
errorCode = pv.getErrorCode();
errorMessage = pv.getErrorMessage();
/* handle the error */
}
/* The user is now authenticated. */
/* If we did set the proxy callback url, we can get proxy tickets with this method call: */
String urlOfTargetService = "http://hkg2.its.yale.edu/someApp/portalFeed";
String proxyTicket = ProxyTicketReceptor.getProxyTicket( pv.getPgtIou() , urlOfTargetService);
import edu.yale.its.tp.cas.client.*;
...
String user = null;
String errorCode = null;
String errorMessage = null;
String xmlResponse = null;
List proxyList = null;
/* instantiate a new ProxyTicketValidator */
ProxyTicketValidator pv = new ProxyTicketValidator();
/* set its parameters */
pv.setCasValidateUrl("https://secure.its.yale.edu/cas/proxyValidate");
pv.setService(urlOfThisService);
pv.setServiceTicket(request.getParameter("ticket"));
String urlOfProxyCallbackServlet = "https://portal.yale.edu/CasProxyServlet";
pv.setProxyCallbackUrl(urlOfProxyCallbackServlet);
/* contact CAS and validate */
pv.validate();
/* if we want to look at the raw response, we can use getResponse() */
xmlResponse = pv.getResponse();
/* read the response */
if(pv.isAuthenticationSuccesful()) {
user = pv.getUser();
proxyList = pv.getProxyList();
} else {
errorCode = pv.getErrorCode();
errorMessage = pv.getErrorMessage();
/* handle the error */
}
/* The user is now authenticated. */
/* If we did set the proxy callback url, we can get proxy tickets with this method call: */
String urlOfTargetService = "http://hkg2.its.yale.edu/someApp/portalFeed";
String proxyTicket = ProxyTicketReceptor.getProxyTicket( pv.getPgtIou() , urlOfTargetService);
在这里,我们假设上下文环境中的用户已经通过了CAS登录认证,被重定向到当前的servlet下,我们在servlet中获取ticket凭证,servlet的URL对用户身份进行确认。如果上下文参数中无法获取ticket凭证,我们就认为用户尚未登录,那么,该servlet必须负责将用户重定向到CAS的登录页面去。
初战告捷
到今天为止,我们已经通过JA-SIG学习笔记的1-3部分,对CAS这个开源SSO的框架有了个大体的了解和初步的掌握,希望这些知识能为各位步入CAS殿堂打开一扇的大门。咖啡希望在今后的工作应用中,能同大家一块共同探讨,进一步深入了解CAS。
JA-SIG CAS服务环境搭建,请参考 :JA-SIG(CAS)学习笔记1
JA-SIG CAS业务架构介绍,请参考 :JA-SIG(CAS)学习笔记2
HTTPS所涉及的Java安全证书知识,请参考 :Java keytool 安全证书学习笔记
CAS技术框架
CAS Server
目前,我们使用的CAS Server 3.1.1的是基于Spring Framework编写的,因此在CAS服务器端的配置管理中,绝大多数是Spring式的Java Bean XML配置。CAS 的服务器提供了一套易于定制的用户认证器接口,用户可以根据自身企业的在线系统的认证方式,来定制自己的认证逻辑。不论是传统的用户名/密码方式,还是基于安全证书的方式;是基于关系数据库的存储,还是采用LDAP服务器,CAS Server给我们提供了这些常用的验证器模板代码,只要稍作修改,便可灵活使用了。
对于广大的中国企业用户而言,另一个需要定制的功能莫过于全中文、企业特色的用户身份认证页面了。CAS Server提供了两套系统界面,一套是默认的CAS英文标准页面,另一套则是专门提供给用户来定制修改的。(PS:老外们做事情就是人性化啊~~)那么对CAS Server端的后续学习,我们将围绕着身份认证模块定制和界面定制这两方面展开。
CAS Client
客户端我们使用的是CAS Client 2.1.1。虽然在官方网站上已出现了3.1.0版本的下载,但该版本地代码已经完全重写,使用的package和类名同2.1.1大相径庭了,最关键的是,该版本暂时没有对应的API说明文档。虽然咖啡我对程序版本怀有极大的“喜新厌旧”的心态,但安全起见,还是先2.1.1吧,相信3.1.0的文档耶鲁大学的大牛们已经在整理了,期待中……
CAS Client2.1.1.jar中的代码是相当精炼的,有兴趣的朋友建议阅读一下源码。Jar包中的代码分成三个大部分
1. edu.yale.its.tp.cas.util 包,其中只有一个工具类 SecureURL.java 用来访问HTTPS URL
2. edu.yale.its.tp.cas.proxy包,用来处理Proxy Authentication代理认证的3个类,其中ProxyTicketReceptor.java是 接收PGT回调的servlet,在下文中我们会提及。
3. edu.yale.its.tp.cas.client包,其中包含了CAS Filter ,Tag Library等主要的认证客户端工具类,我们在后面会进行重点介绍。
针对CAS Client的学习,我们的重点将放在CAS Filter 和ProxyTicketReceptor 的配置以及在Java SE环境下,直接使用 ServiceTicketValidator进行Ticket认证实现上。
CAS服务器端应用
定制适合你的身份认证程序
通过前面的学习,我们了解了CAS具有一个良好而强大的SSO功能框架。接下来,我们要学习如何将实际企业应用中的身份认证同CAS进行整合。
简单的说,要将现有企业应用中的认证集成到CAS Server中,只要实现一个名为AuthenticationHandler的一个认证处理Java接口就行。以下是该接口的源代码:
Java代码
public interface AuthenticationHandler {
/**
* 该方法决定一个受支持的credentials是否是可用的,
* 如果可用,该方法返回true,则说明身份认证通过
*/
boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
* 该方法决定一个credentials是否是当前的handle所支持的
*/
boolean supports(Credentials credentials);
}
public interface AuthenticationHandler {
/**
* 该方法决定一个受支持的credentials是否是可用的,
* 如果可用,该方法返回true,则说明身份认证通过
*/
boolean authenticate(Credentials credentials) throws AuthenticationException;
/**
* 该方法决定一个credentials是否是当前的handle所支持的
*/
boolean supports(Credentials credentials);
}
这里我们要说明一下Credentials这个CAS的概念。所谓Credentials是由外界提供给CAS来证明自身身份的信息,简单的如一个用户名/密码对就是一个Credentials,或者一个经过某种加密算法生成的密文证书也可以是一个Credentials。在程序的实现上,Credentials被声明为一个可序列化的接口,仅仅起着标识作用,源代码如下:
Java代码
public interface Credentials extends Serializable {
// marker interface contains no methods
}
public interface Credentials extends Serializable {
// marker interface contains no methods
}
CAS的API中,已经为我们提供了一个最常用的实现UsernamePasswordCredentials 用户名/密码凭证,代码如下:
Java代码
public class UsernamePasswordCredentials implements Credentials {
/** Unique ID for serialization. */
private static final long serialVersionUID = -8343864967200862794L;
/** The username. */
private String username;
/** The password. */
private String password;
public final String getPassword() {
return this.password;
}
public final void setPassword(final String password) {
this.password = password;
}
public final String getUsername() {
return this.username;
}
public final void setUsername(final String userName) {
this.username = userName;
}
public String toString() {
return this.username;
}
public boolean equals(final Object obj) {
if (obj == null || !obj.getClass().equals(this.getClass())) {
return false;
}
final UsernamePasswordCredentials c = (UsernamePasswordCredentials) obj;
return this.username.equals(c.getUsername())
&& this.password.equals(c.getPassword());
}
public int hashCode() {
return this.username.hashCode() ^ this.password.hashCode();
}
}
public class UsernamePasswordCredentials implements Credentials {
/** Unique ID for serialization. */
private static final long serialVersionUID = -8343864967200862794L;
/** The username. */
private String username;
/** The password. */
private String password;
public final String getPassword() {
return this.password;
}
public final void setPassword(final String password) {
this.password = password;
}
public final String getUsername() {
return this.username;
}
public final void setUsername(final String userName) {
this.username = userName;
}
public String toString() {
return this.username;
}
public boolean equals(final Object obj) {
if (obj == null || !obj.getClass().equals(this.getClass())) {
return false;
}
final UsernamePasswordCredentials c = (UsernamePasswordCredentials) obj;
return this.username.equals(c.getUsername())
&& this.password.equals(c.getPassword());
}
public int hashCode() {
return this.username.hashCode() ^ this.password.hashCode();
}
}
很简单不是吗?就是存储一个用户名和密码的java bean而已。
接下来,我们将一个Credentials传给一个AuthenticationHandler进行认证,首先调用boolean supports(Credentials credentials)方法察看当前传入的Credentials实例,AuthenticationHandler实例现是否支持它?如果支持,再调用boolean authenticate(Credentials credentials)方法进行认证。由于用户名/密码方式是最常用的认证方法,因此CAS为我们提供了一个现成的基于该方式的抽象认证处理类AbstractUsernamePasswordAuthenticationHandler。通常我们只需要继承该类,并实现其中的 authenticateUsernamePasswordInternal方法即可。下面我们给出一个Demo的实现类,它的校验逻辑很简单——仅校验用户名的字符长度是否与密码的相等(这里密码是一个表示长度的整数),如果相等则认为认证通过,请看代码:
Java代码
public class UsernameLengthAuthnHandler
extends AbstractUsernamePasswordAuthenticationHandler {
protected boolean authenticateUsernamePasswordInternal( UsernamePasswordCredentials credentials) throws AuthenticationException {
/*
* 这里我们完全可以用自己的认证逻辑代替,比如将用户名/密码传入一个SQL语句
* 向数据库验证是否有对应的用户账号,这不是我们最经常干的事么?
* 只需要将下面的程序替换掉就OK了!!So easy,so simple!
/
String username = credentials.getUsername();
String password = credentials.getPassword();
String correctPassword = Integer.toString(username.length());
return correctPassword.equals(password);
}
}
public class UsernameLengthAuthnHandler
extends AbstractUsernamePasswordAuthenticationHandler {
protected boolean authenticateUsernamePasswordInternal( UsernamePasswordCredentials credentials) throws AuthenticationException {
/*
* 这里我们完全可以用自己的认证逻辑代替,比如将用户名/密码传入一个SQL语句
* 向数据库验证是否有对应的用户账号,这不是我们最经常干的事么?
* 只需要将下面的程序替换掉就OK了!!So easy,so simple!
/
String username = credentials.getUsername();
String password = credentials.getPassword();
String correctPassword = Integer.toString(username.length());
return correctPassword.equals(password);
}
}
介绍到这里,大家应该清楚如何定制自己的AuthenticationHandler类了吧!这里要附带说明的是,在CAS Server的扩展API中已经提供了大量常用认证形式的实现类,它们同CAS Server的war包一同分发:
cas-server-support-generic-3.1.1.jar ——使用Map记录用户认证信息的实现
cas-server-support-jdbc-3.1.1.jar —— 基于Spring JDBC的数据库实现(我们常用的)
cas-server-support-ldap-3.1.1.jar —— 基于LDAP的用户认证实现
更多其他形式的实现各位看官有兴趣的,可以一一阅读源码。
配置你的身份认证程序
完成了定制认证类的代码编写,接下来就是要让CAS Server来调用它了。在CAS的框架中,对程序的配置都是使用Spring Framework的xml文件,这对于熟悉Spring的程序员而言算驾轻就熟了。
配置文件位于应用部署目录的WEB-INF子目录下——deployerConfigContext.xml。在bean id=authenticationManager 的 authenticationHandlers属性中配置我们的AuthenticationHandlers:
引用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="authenticationManager"
class="org.jasig.cas.authentication.AuthenticationManagerImpl">
。。。
。。。
<property name="authenticationHandlers">
<list>
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref="httpClient" />
<!—下面就是系统默认的验证器配置,你可以替换它,或者增加一个新的handler -->
<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
</list>
</property>
</bean>
。。。
。。。
</beans>
我们发现authenticationHandlers属性是一个list,在这个list中可以配置多个AuthenticationHandlers。这些AuthenticationHandlers形成了一个验证器链,所有提交给CAS的Credentials信息将通过这个验证器链的链式过滤,只要这链中有一个验证器通过了对Credentials的验证,就认为这个Credentials是合法的。这样的设计使得我们可以很轻松的整合不同验证体系的已有应用到同一个CAS上,比如:A验证器负责校验alpha系统提交的Credentials,它是基于LDAP服务器的;B验证器负责校验beta系统提交的Credentials,它是一个传统的RDB用户表认证;C验证器负责校验gamma系统提交的基于RSA证书加密的Credentials。3种完全不同的用户身份认证通过配置就可以统一在同一个CAS服务内,很好很强大,不是吗!!
定制身份验证登录界面
CAS Server在显示界面层view使用了“主题Theme”的概念。在{project.home}/webapp/WEB-INF/view/jsp/目录下,系统默认提供了两套得UI —— default和simple 。default方案使用了CSS等相对复杂得界面元素,而simple方案提供了最简化的界面表示方式。在整个的CAS Server服务器端,有四个界面是我们必须要实现的:
casConfirmView.jsp —— 确认信息(警告信息)页面
casGenericSuccess.jsp —— 登陆成功提示页面
casLoginView.jsp —— 登录输入页面
casLogoutView.jsp —— SSO登出提示页面
这些都是标准的jsp页面,如何实现他们,完全由您说了算,除了名字不能改。
CAS为view的展示提供了3个级别的定制方式,让我们从最直观简单的开始吧。
1. 采用文件覆盖方式:直接修改default中的页面或者将新写好的四个jsp文件覆盖到default目录中。这种方式最直观和简单,但咖啡建议各位在使用这种方式前将原有目录中的文件备份一下,以备不时之需。
2. 修改UI配置文件,定位UI目录:在CAS Server端/webapp/WEB-INF/classes/ 目录下,有一个名为default_views.properties的属性配置文件,你可以通过修改配置文件中的各个页面文件位置,指向你新UI文件,来达到修改页面展示的目的。
3. 修改配置文件的配置文件,这话看起来有点别扭,其实一点不难理解。在方法2中的default_views.properties文件是一整套的UI页面配置。如果我想保存多套的UI页面配置就可以写多个的properties文件来保存这些配置。在CAS Server端/webapp/WEB-INF/目录下有cas-servlet.xml和cas.properties两个文件,cas-servlet.xml使用了cas.properties文件中的cas.viewResolver.basename属性来定义view属性文件的名字,因此你可以选者直接修改cas-servlet.xml中的viewResolver 下的basenames属性,或者修改cas.properties中的cas.viewResolver.basename属性,指定新的properties文件名,这样可以轻松的替换全套UI。
CAS客户端配置及API应用
CASFilter的配置
对于大部分web应用而言,使用CAS集成统一认证是相对简单的事,只要为需要认证的URL配置edu.yale.its.tp.cas.client.filter.CASFilter认证过滤器。下面我们就针对过滤器的配置进行说明。首先参看一下Filter的基本配置:
引用
<web-app>
...
<filter>
<filter-name>CAS Filter</filter-name>
<filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
<param-value>https://secure.its.yale.edu/cas/login<;/param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
<param-value>https://secure.its.yale.edu/cas/serviceValidate<;/param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
<param-value>your server name and port (e.g., www.yale.edu:8080)</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Filter</filter-name>
<url-pattern>/requires-cas-authetication/*</url-pattern>
</filter-mapping>
...
</web-app>
上述配置中的init-param是filter的3个必备的属性,下面这张表则是filter全部属性的详细说明:
ProxyTicketReceptor的配置
大家还记得在前面我们说过的Proxy Authentication中的call back URL吗?ProxyTicketReceptor是部署在client端的一个servlet,提供server端回传PGT和PGTIOU的。它的xml部署如下:
引用
<web-app>
...
<servlet>
<servlet-name>ProxyTicketReceptor</servlet-name>
<servlet-class>edu.yale.its.tp.cas.proxy.ProxyTicketReceptor</servlet-class>
<init-param>
<param-name>edu.yale.its.tp.cas.proxyUrl</param-name>
<param-value>https://secure.its.yale.edu/cas/proxy<;/param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ProxyTicketReceptor</servlet-name>
<url-pattern>/CasProxyServlet</url-pattern>
</servlet-mapping>
...
</webapp>
这里要说明的是它的参数edu.yale.its.tp.cas.proxyUrl。在服务端通过ProxyTicketReceptor将PGT和PGTIOU传给客户端后,ProxyTicketReceptor在进行Proxy Authentication的过程中需要向服务端请求一个ProxyTicket(PT),这个proxyUrl就是服务端的请求入口了。(关于Proxy Authentication的运作原理,参见JA-SIG(CAS)学习笔记2 )
CAS Client端的API应用1.用户可以通过以下两种方式的任意一种,从JSP或servlet中获取通过认证的用户名:
引用
String username = (String)session.getAttribute(CASFilter.CAS_FILTER_USER);
或者
String username = (String)session.getAttribute("edu.yale.its.tp.cas.client.filter.user");
2.获得更完整的受认证用户信息对象CASReceipt Java Bean,可以使用以下语句的任一:
引用
CASReceipt receipt = (CASReceipt )session.getAttribute(CASFilter.CAS_FILTER_RECEIPT);
或者
CASReceipt receipt = (CASReceipt )session.getAttribute("edu.yale.its.tp.cas.client.filter.receipt");
3.手工编码使用CAS Java Object进行用户验证,使用ServiceTicketValidator或者 ProxyTicketValidator(代理认证模式下),在servlet中对用户身份进行验证。
3-1.ServiceTicketValidator
Java代码
import edu.yale.its.tp.cas.client.*;
...
String user = null;
String errorCode = null;
String errorMessage = null;
String xmlResponse = null;
/* instantiate a new ServiceTicketValidator */
ServiceTicketValidator sv = new ServiceTicketValidator();
/* set its parameters */
sv.setCasValidateUrl("https://secure.its.yale.edu/cas/serviceValidate");
sv.setService(urlOfThisService);
sv.setServiceTicket(request.getParameter("ticket"));
String urlOfProxyCallbackServlet = "https://portal.yale.edu/CasProxyServlet";
sv.setProxyCallbackUrl(urlOfProxyCallbackServlet);
/* contact CAS and validate */
sv.validate();
/* if we want to look at the raw response, we can use getResponse() */
xmlResponse = sv.getResponse();
if(sv.isAuthenticationSuccesful()) {
user = sv.getUser();
} else {
errorCode = sv.getErrorCode();
errorMessage = sv.getErrorMessage();
}
/* The user is now authenticated. */
/* If we did set the proxy callback url, we can get proxy tickets with: */
String urlOfTargetService = "http://hkg2.its.yale.edu/someApp/portalFeed";
String proxyTicket = ProxyTicketReceptor.getProxyTicket( sv.getPgtIou() , urlOfTargetService);
import edu.yale.its.tp.cas.client.*;
...
String user = null;
String errorCode = null;
String errorMessage = null;
String xmlResponse = null;
/* instantiate a new ServiceTicketValidator */
ServiceTicketValidator sv = new ServiceTicketValidator();
/* set its parameters */
sv.setCasValidateUrl("https://secure.its.yale.edu/cas/serviceValidate");
sv.setService(urlOfThisService);
sv.setServiceTicket(request.getParameter("ticket"));
String urlOfProxyCallbackServlet = "https://portal.yale.edu/CasProxyServlet";
sv.setProxyCallbackUrl(urlOfProxyCallbackServlet);
/* contact CAS and validate */
sv.validate();
/* if we want to look at the raw response, we can use getResponse() */
xmlResponse = sv.getResponse();
if(sv.isAuthenticationSuccesful()) {
user = sv.getUser();
} else {
errorCode = sv.getErrorCode();
errorMessage = sv.getErrorMessage();
}
/* The user is now authenticated. */
/* If we did set the proxy callback url, we can get proxy tickets with: */
String urlOfTargetService = "http://hkg2.its.yale.edu/someApp/portalFeed";
String proxyTicket = ProxyTicketReceptor.getProxyTicket( sv.getPgtIou() , urlOfTargetService);
3-2.ProxyTicketValidator
Java代码
import edu.yale.its.tp.cas.client.*;
...
String user = null;
String errorCode = null;
String errorMessage = null;
String xmlResponse = null;
List proxyList = null;
/* instantiate a new ProxyTicketValidator */
ProxyTicketValidator pv = new ProxyTicketValidator();
/* set its parameters */
pv.setCasValidateUrl("https://secure.its.yale.edu/cas/proxyValidate");
pv.setService(urlOfThisService);
pv.setServiceTicket(request.getParameter("ticket"));
String urlOfProxyCallbackServlet = "https://portal.yale.edu/CasProxyServlet";
pv.setProxyCallbackUrl(urlOfProxyCallbackServlet);
/* contact CAS and validate */
pv.validate();
/* if we want to look at the raw response, we can use getResponse() */
xmlResponse = pv.getResponse();
/* read the response */
if(pv.isAuthenticationSuccesful()) {
user = pv.getUser();
proxyList = pv.getProxyList();
} else {
errorCode = pv.getErrorCode();
errorMessage = pv.getErrorMessage();
/* handle the error */
}
/* The user is now authenticated. */
/* If we did set the proxy callback url, we can get proxy tickets with this method call: */
String urlOfTargetService = "http://hkg2.its.yale.edu/someApp/portalFeed";
String proxyTicket = ProxyTicketReceptor.getProxyTicket( pv.getPgtIou() , urlOfTargetService);
import edu.yale.its.tp.cas.client.*;
...
String user = null;
String errorCode = null;
String errorMessage = null;
String xmlResponse = null;
List proxyList = null;
/* instantiate a new ProxyTicketValidator */
ProxyTicketValidator pv = new ProxyTicketValidator();
/* set its parameters */
pv.setCasValidateUrl("https://secure.its.yale.edu/cas/proxyValidate");
pv.setService(urlOfThisService);
pv.setServiceTicket(request.getParameter("ticket"));
String urlOfProxyCallbackServlet = "https://portal.yale.edu/CasProxyServlet";
pv.setProxyCallbackUrl(urlOfProxyCallbackServlet);
/* contact CAS and validate */
pv.validate();
/* if we want to look at the raw response, we can use getResponse() */
xmlResponse = pv.getResponse();
/* read the response */
if(pv.isAuthenticationSuccesful()) {
user = pv.getUser();
proxyList = pv.getProxyList();
} else {
errorCode = pv.getErrorCode();
errorMessage = pv.getErrorMessage();
/* handle the error */
}
/* The user is now authenticated. */
/* If we did set the proxy callback url, we can get proxy tickets with this method call: */
String urlOfTargetService = "http://hkg2.its.yale.edu/someApp/portalFeed";
String proxyTicket = ProxyTicketReceptor.getProxyTicket( pv.getPgtIou() , urlOfTargetService);
在这里,我们假设上下文环境中的用户已经通过了CAS登录认证,被重定向到当前的servlet下,我们在servlet中获取ticket凭证,servlet的URL对用户身份进行确认。如果上下文参数中无法获取ticket凭证,我们就认为用户尚未登录,那么,该servlet必须负责将用户重定向到CAS的登录页面去。
初战告捷
到今天为止,我们已经通过JA-SIG学习笔记的1-3部分,对CAS这个开源SSO的框架有了个大体的了解和初步的掌握,希望这些知识能为各位步入CAS殿堂打开一扇的大门。咖啡希望在今后的工作应用中,能同大家一块共同探讨,进一步深入了解CAS。
相关推荐
Spring Boot是Spring框架的一个模块,它简化了基于Spring应用程序的创建和部署过程。Spring Boot提供了快速启动Spring应用程序的能力,通过自动配置、微服务支持和独立运行的特性,使得开发者能够专注于业务逻辑,而不是配置细节。Spring Boot的核心思想是约定优于配置,它通过自动配置机制,根据项目中添加的依赖自动配置Spring应用。这大大减少了配置文件的编写,提高了开发效率。Spring Boot还支持嵌入式服务器,如Tomcat、Jetty和Undertow,使得开发者无需部署WAR文件到外部服务器即可运行Spring应用。 Java是一种广泛使用的高级编程语言,由Sun Microsystems公司(现为Oracle公司的一部分)在1995年首次发布。Java以其“编写一次,到处运行”(WORA)的特性而闻名,这一特性得益于Java虚拟机(JVM)的使用,它允许Java程序在任何安装了相应JVM的平台上运行,而无需重新编译。Java语言设计之初就是为了跨平台,同时具备面向对象、并发、安全和健壮性等特点。 Java语言广泛应用于企业级应用、移动应用、桌面应用、游戏开发、云计算和物联网等领域。它的语法结构清晰,易于学习和使用,同时提供了丰富的API库,支持多种编程范式,包括面向对象、命令式、函数式和并发编程。Java的强类型系统和自动内存管理减少了程序错误和内存泄漏的风险。随着Java的不断更新和发展,它已经成为一个成熟的生态系统,拥有庞大的开发者社区和持续的技术创新。Java 8引入了Lambda表达式,进一步简化了并发编程和函数式编程的实现。Java 9及以后的版本继续在模块化、性能和安全性方面进行改进,确保Java语言能够适应不断变化的技术需求和市场趋势。 MySQL是一个关系型数据库管理系统(RDBMS),它基于结构化查询语言(SQL)来管理和存储数据。MySQL由瑞典MySQL AB公司开发,并于2008年被Sun Microsystems收购,随后在2010年,Oracle公司收购了Sun Microsystems,从而获得了MySQL的所有权。MySQL以其高性能、可靠性和易用性而闻名,它提供了多种特性来满足不同规模应用程序的需求。作为一个开源解决方案,MySQL拥有一个活跃的社区,不断为其发展和改进做出贡献。它的多线程功能允许同时处理多个查询,而其优化器则可以高效地执行复杂的查询操作。 随着互联网和Web应用的快速发展,MySQL已成为许多开发者和公司的首选数据库之一。它的可扩展性和灵活性使其能够处理从小规模应用到大规模企业级应用的各种需求。通过各种存储引擎,MySQL能够适应不同的数据存储和检索需求,从而为用户提供了高度的定制性和性能优化的可能性。
精选毕设项目-新浪读书
智慧农业平台解决方案
精选毕设项目-小程序地图Demo
实验目的 在本实验中,通过对事件和互斥体对象的了解,来加深对 Windows Server 2016 线程同步的理解。 1)回顾系统进程、线程的有关概念,加深对 Windows Server 2016 线程的理解; 2)了解事件和互斥体对象; 3)通过分析实验程序,了解管理事件对象的API; 4)了解在进程中如何使用事件对象; 5)了解在进程中如何使用互斥体对象; 6)了解父进程创建子进程的程序设计方法。 程序清单 清单2-1 1.// event 项目 2.#include <windows.h> 3.#include <iostream> 4.using namespace std; 5. 6.// 以下是句柄事件。实际中很可能使用共享的包含文件来进行通讯 7.static LPCTSTR g_szContinueEvent = "w2kdg.EventDemo.event.Continue"; 8. 9.// 本方法只是创建了一个进程的副本,以子进程模式 (由命令行指定) 工作 10.BOOL CreateChild() 11.{
三相VIENNA整流,维也纳整流器simulink仿真 输入电压220v有效值 输出电压800v纹波在1%以内 0.1s后系统稳定 功率因数>0.95 电流THD<5% 开关频率20k 图一为拓扑,可以看到功率因数和THD以及输出电压 图二为直流输出电压 图三四为a相电压电流 图五为控制等计算的总体框图 图六为svpwm调制框图 图七为双闭环控制图八为输出调制波 可作为电力电子方向入门学习~~
chromedriver-linux64_122.0.6251.0
一、实验目的 实验1.1 Windows“任务管理器”的进程管理 通过在Windows任务管理器中对程序进程进行响应的管理操作,熟悉操作系统进程管理的概念,学习观察操作系统运行的动态性能。 实验1.2 Windows Server 2016进程的“一生” 1)通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉 操作系统的进程概念,理解Windows Server 2016进程的“一生”; 2)通过阅读和分析实验程序,学习创建进程、观察进程和终止进程的程序设计方法。 1.// proccreate项目 2.#include <windows.h> 3.#include <iostream> 4.#include <stdio.h> 5.using namespace std; 6. 7.// 创建传递过来的进程的克隆过程并赋与其ID值 8.void StartClone(int nCloneID) { 9. // 提取用于当前可执行文件的文件名 10. TCHAR szFilename[MAX_PATH]; 11
MATLAB环境下一种基于稀疏优化的瞬态伪影消除算法 程序运行环境为MATLAB R2018A,执行一种基于稀疏优化的瞬态伪影消除算法。 GRAY = [1 1 1] * 0.7; subplot(4, 1, 4) line(n, y, 'color', GRAY, 'lineWidth', 1) line(n, y - x, 'color', 'black'); legend('Raw data', 'Corrected data') xlim([0 N]) xlabel('Time (n)') 压缩包=数据+程序+参考。
多机系统的暂态稳定仿真 MATLAB编程 针对多机电力系统,通过编程,计算当发生故障时,多台发电机的功角曲线(pv节点发电机与平衡节点发电机的功角差),通过功角曲线来分析判断多机系统的暂态稳定性。 注: 可指定故障发生位置及故障清除时间 下面以IEEE30节点系统为例
Spring Boot是Spring框架的一个模块,它简化了基于Spring应用程序的创建和部署过程。Spring Boot提供了快速启动Spring应用程序的能力,通过自动配置、微服务支持和独立运行的特性,使得开发者能够专注于业务逻辑,而不是配置细节。Spring Boot的核心思想是约定优于配置,它通过自动配置机制,根据项目中添加的依赖自动配置Spring应用。这大大减少了配置文件的编写,提高了开发效率。Spring Boot还支持嵌入式服务器,如Tomcat、Jetty和Undertow,使得开发者无需部署WAR文件到外部服务器即可运行Spring应用。 Java是一种广泛使用的高级编程语言,由Sun Microsystems公司(现为Oracle公司的一部分)在1995年首次发布。Java以其“编写一次,到处运行”(WORA)的特性而闻名,这一特性得益于Java虚拟机(JVM)的使用,它允许Java程序在任何安装了相应JVM的平台上运行,而无需重新编译。Java语言设计之初就是为了跨平台,同时具备面向对象、并发、安全和健壮性等特点。 Java语言广泛应用于企业级应用、移动应用、桌面应用、游戏开发、云计算和物联网等领域。它的语法结构清晰,易于学习和使用,同时提供了丰富的API库,支持多种编程范式,包括面向对象、命令式、函数式和并发编程。Java的强类型系统和自动内存管理减少了程序错误和内存泄漏的风险。随着Java的不断更新和发展,它已经成为一个成熟的生态系统,拥有庞大的开发者社区和持续的技术创新。Java 8引入了Lambda表达式,进一步简化了并发编程和函数式编程的实现。Java 9及以后的版本继续在模块化、性能和安全性方面进行改进,确保Java语言能够适应不断变化的技术需求和市场趋势。 MySQL是一个关系型数据库管理系统(RDBMS),它基于结构化查询语言(SQL)来管理和存储数据。MySQL由瑞典MySQL AB公司开发,并于2008年被Sun Microsystems收购,随后在2010年,Oracle公司收购了Sun Microsystems,从而获得了MySQL的所有权。MySQL以其高性能、可靠性和易用性而闻名,它提供了多种特性来满足不同规模应用程序的需求。作为一个开源解决方案,MySQL拥有一个活跃的社区,不断为其发展和改进做出贡献。它的多线程功能允许同时处理多个查询,而其优化器则可以高效地执行复杂的查询操作。 随着互联网和Web应用的快速发展,MySQL已成为许多开发者和公司的首选数据库之一。它的可扩展性和灵活性使其能够处理从小规模应用到大规模企业级应用的各种需求。通过各种存储引擎,MySQL能够适应不同的数据存储和检索需求,从而为用户提供了高度的定制性和性能优化的可能性。
精选毕设项目-鱼缸表盘系统小程序
在科技与司法的交响曲中,智慧法院应运而生,成为新时代司法服务的新篇章。它不仅仅是一个概念,更是对法院传统工作模式的一次深刻变革。智慧法院通过移动信息化技术,为法院系统注入了强大的生命力,有效缓解了案多人少的矛盾,让司法服务更加高效、便捷。 立案、调解、审判,每一个阶段都融入了科技的智慧。在立案阶段,智慧法院利用区块链技术实现可信存证,确保了电子合同的合法性和安全性,让交易双方的身份真实性、交易安全性得到了有力见证。这不仅极大地缩短了立案时间,还为后续审判工作奠定了坚实的基础。在调解阶段,多元调解服务平台借助人工智能、自然语言处理等前沿技术,实现了矛盾纠纷的快速化解。无论是矛盾类型的多元化,还是化解主体的多元化,智慧法院都能提供一站式、全方位的服务,让纠纷解决更加高效、和谐。而在审判阶段,智能立案、智能送达、智能庭审、智能判决等一系列智能化手段的应用,更是让审判活动变得更加智能化、集约化。这不仅提高了审判效率,还确保了审判质量的稳步提升。 更为引人注目的是,智慧法院还构建了一套完善的执行体系。移动执行指挥云平台的建设,让执行工作变得更加精准、高效。执行指挥中心和信息管理中心的一体化应用,实现了信息的实时传输和交换,为执行工作提供了强有力的支撑。而执行指挥车的配备,更是让执行现场通讯信号得到了有力保障,应急通讯能力得到了显著提升。这一系列创新举措的实施,不仅让执行难问题得到了有效解决,还为构建诚信社会、保障金融法治化营商环境提供了有力支撑。智慧法院的出现,让司法服务更加贴近民心,让公平正义的阳光更加温暖人心。
内容概要:本文针对计算机网络这门课程的期末复习,全面介绍了多个关键点和重要概念。主要内容涵盖了计算机网络的基本概念、OSI七层模型及其每一层的具体职责和协议、详细的TCP/IP协议介绍,尤其是三次握手和四次挥手机制、IP地址(IPv4 和 IPv6)的概念和子网划分的技术、静态路由和动态路由的区别及其路由选择算法、TCP和UDP作为两种主要传输层协议的功能区别、各种常用的应用层协议如HTTP、HTTPS、FTP、SMTP等,此外还包括了一些关于网络性能优化的关键参数以及常见的网络安全措施。所有理论均配有相应的案例分析帮助深入理解和巩固知识点。 适合人群:正在准备计算机网络相关考试的学生,或希望深入理解计算机网络架构和原理的人群。 使用场景及目标:为用户提供详尽的期末复习指南,助力理解复杂的技术概念并提高解决具体应用问题的能力,同时通过实例演示使学习变得更加直观。 其他说明:强调不仅要记住公式和定义,更要关注概念背后的运作逻辑及实际应用情况来达到良好的复习效果。
精选毕设项目-移动端商城
本文介绍了基于Python的B站视频的数据分析可视化系统设计与实现。该系统帮助用户深入了解B站视频的趋势,并通过数据分析和可视化技术展示相关信息。利用Python的网络爬虫技术获取B站上的视频数据,包括视频标题、上传者、播放量、点赞数等信息。借助数据分析库Pandas对获取的数据进行处理和分析,例如计算了不同用户视频发布个数、粉丝量、视频长度、视频观阅人数,还分析了不同视频的舆情分布和流行趋势。接着,利用可视化库Echarts将分析结果呈现为图表,例如柱状图、饼图、折线图等,以便用户直观地理解数据。为了提供更加个性化的服务,系统还集成了协同过滤算法推荐功能,根据用户的历史观看记录和偏好,推荐可能感兴趣的视频。最后,设计并实现了一个交互式的用户界面,用户可以通过界面选择感兴趣的话题和日期范围,系统将动态展示相关视频的数据分析结果。通过本系统,用户可以更好地了解B站视频的特点和趋势,同时享受到个性化的视频推荐服务,为用户提供了一个便捷而全面的数据分析工具。 感兴趣自行下载学习!
标题 "MPU6050.zip" 暗示了这个压缩包可能包含了与MPU6050陀螺仪和加速度传感器相关的资源。MPU6050是一款广泛应用的惯性测量单元(IMU),它能检测设备在三个轴上的角速度和线性加速度,常用于运动控制、姿态估算、导航等领域。 描述中只提到了"MPU6050.zip",没有提供额外信息,但我们可以通过标签 "stm32cubemx" 来推测,这个压缩包里的内容可能与STM32系列微控制器以及使用STM32CubeMX配置工具有关。STM32CubeMX是一款强大的配置工具,用户可以利用它来初始化STM32微控制器的外设,生成相应的初始化代码。 在压缩包的文件名列表中,我们看到以下几个文件: 1. mpu6050.c:这是一个C源文件,通常包含了与MPU6050交互的驱动程序代码。在这个文件里,开发者可能会定义函数来初始化传感器、读取数据、处理中断等。 2. mpu6050.h:这是对应的头文件,包含了函数声明、常量定义和结构体等,供其他模块调用时包含,以实现对MPU60。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。
IPSO-SVR 改进粒子群算法优化支持向量机的多变量回归预测 Matlab语言 1.多变量单输出,通过非线性权重递减方式对粒子群算法进行改进,优化SVR中的两个参数,评价指标包括R2、MAE、MSE、MAPE,效果如图所示,可完全满足您的需求~ 2.直接替Excel数据即可用,注释清晰,适合新手小白[火] 3.附赠测试数据,输入格式如图3所示,可直接运行 4.仅包含模型代码 5.模型只是提供一个衡量数据集精度的方法,因此无法保证替数据就一定得到您满意的结果~
精选项目-天气预报带后端
精选毕设项目-自助查勘