- 浏览: 72766 次
- 性别:
-
文章分类
最新评论
-
yehayeah:
ljm653467 写道 嗯,很好,可以借鉴,不过我想问的是在 ...
转载:单例设计模式中使用dom4j来完成(数据库配置文件)xml的解析,并完成数据库的连接 -
ljm653467:
嗯,很好,可以借鉴,不过我想问的是在你上面的代码基础上,如果我 ...
转载:单例设计模式中使用dom4j来完成(数据库配置文件)xml的解析,并完成数据库的连接
转载自:http://blog.csdn.net/tancaiyi/article/details/3971585
声明:本人转载只为分享,如涉及商业版权纠纷,请及时与本人联系,必将立即删除,造成不便,还望见谅,谢谢。
Java平台是创建企业应用程序的普遍选择。它所以受欢迎,主要原因之一是在创建Java语言时充分考虑了安全性,而且市场上也普遍认为Java是一种"安全的"语言。Java平台在两个层次上提供安全性:语言层次安全性和企业层次安全性。
1.语言层次安全性
最初的Java(JDK1.2)平台采用沙箱安全模型,基本安全模型由三部分来承担,这三部分构成Java运行环境的三个安全组件,分别是:类加载器,文件校验器,安全管理器
1.1类加载器
类加载器负责从特定位置加载类,类加载器是JVM的看门人,控制着哪些代码被加载或被拒绝,类加载器首先进行安全性检查,它往往从以下几个方面去检查,装入的字节码是否生成指针,装入的字节码是否违反访问限制,装入的字节码是否把对象当作它们本身来访问,它在确保执行代码的安全性方面至关重要。
1.2类文件校验器的校验
类文件校验器负责检查那些无法执行的明显有破坏性的操作,类文件校验器执行的一些检查通常有:变量要在使用之前进行初始化;方法调用和对象引用类型之间要匹配;没有违反访问私有数据和方法的规则;对本地变量的访问都在运行时堆栈内;运行时堆栈是否溢出.如果以上检查中任何一条没有通过,就认为该类遭到了破坏,不被加载。
1.3安全管理器
一旦某个类被类加载器加载到虚拟机中,并由类文件校验器检查过之后,JAVA的第三种安全机制安全管理器就会启动,安全管理器是一个负责控制某个操作是否允许执行的类,安全管理器负责检查包括以下几个方面的操作:当前线程是否能创建一个新的类加载器;当前线程是否能终止JVM的运行;某个类是否能访问另一个类的成员;当前线程是否能访问本地文件;当前线程是否能打开到达外部记住的socket连接;某个类是否能启动打印作业;某个类是否能访问系统剪贴板;某个类是否能访问AWT事件队列;当前线程是否可被信任以打开一顶层窗口。
尽管Java安全的支柱类加载器、类文件校验器、安全管理器每一个都有独特的功能,但它们又相互依赖、相辅相承。共同保证了Java语言的安全性。
2.企业层次的安全特性
即是构建安全的J2EE应用。Java平台在提供语言安全性的同时还提供其他API功能,为企业应用程序提供一个总体的安全性解决方案。下面将介绍几种方案。
2.1 Java加密扩展(JCE)
JCE是一组包,为加密、密钥生成、密钥协商和消息身份验证代码(MAC)算法提供一种框架和实现。JCE支持多种类型的加密,包括对称的、非对称的、块和流密码。在JDK 1.4之前,JCE是一个可选的包,现在它已经成为Java平台的一个标准组成部分。
2.2 Java安全套接字扩展(JSSE)
JSSE是支持安全的Internet通信的一组包。它是实现了SSL和传输层安全(TLS)协议的JAVA技术。它包括用于数据加密、服务器身份验证、消息完整性和可选的客户端身份验证的诸多功能。JSSE已被集成到JDK 1.4以上版本的平台中。
2.3 Java身份验证和授权规范(JAAS)
JAAS通过对运行程序的用户的进行验证,从而达到保护系统的目的。JAAS主要由两个部件构成,认证和授权,JAAS通过一个配置文件来定义认证机制。认证模块是基于可插的认证模块而设计的,它可以运行在客户端和服务器端,授权模块的设计是一个变化的过程,为了对资源的访问请求进行授权,首先需要应用程序认证请求的资源,subject术语来表示请求的资源,用java.security.auth.Subject类来表示subject。subject一旦通过了认证,就会和身份和主体想关联。在JAAS中将主体表示为javax.security.Principal对象,一个subject可能包含多个主题,除了和主题相关联外,subject还可能拥有与安全相关的属性或证书,证书是用户的数据,它包含这样的认证信息即认证subject所拥有的其他服务的信息。基于J2EE的分布式应用程序使用
JAAS一般有两种情况:第一种情况,一个单独的应用系统与一个远程的EJB系统连接,用户必须向应用系统提供证明身份的信息或应用系统向文件和其它的系统来检索可证明身份的信息。这个单独的应用系统将在调用EJB组件之前使用JAAS来验证用户,由应用服务器完成验证的任务。只有当用户通过JAAS的验证之后,客户端程序才可被信任地调用EJB方法。第二种情况,基于Web浏览器的客户端程序连接到Servlet/JSP层,客户端用户将向Servlet/JSP层提供证明身份的信息,而Servlet/JSP层可以采用JAAS验证用户。Web客户端一般可以采
用基本验证、基于表格的验证、摘要验证、证书验证等方式来提供证明身份的信息。这种支持选择不同认证方法的灵活性有助于支持在管理员层实施更为复杂的安全策略,而不是在编程层上去实现。一旦客户端通过应用服务器认证,安全上下文环境能被传播到EJB层。在应用程序中使用JAAS验证通常会涉及到以下几个步骤:
1.创建一个LoginContext的实例。并传递LoginModule配置
文件程序段和CallbackHandler的名称。
2.为了能够获得和处理验证信息,将一个CallbackHandler
对象作为参数传送给LoginContext。
3.通过调用LoginContext的login()方法来进行验证。
4.通过使用login()方法返回的Subject对象实现一些特殊
的功能(假设登录成功)。
举个例子:
LoginModel是jaas的一个核心接口,她负责实施用户认证。同时暴漏了initialize(),login(),commit(),abort(),logout()方法。
package sample;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class ScreenContentLoginModule implements LoginModule {
protected static final Log log = LogFactory.getLog(ScreenContentLoginModule.class);
private Subject subject;
private UsernamePasswordPrincipal principal;
private CallbackHandler callbackhandler;
public ScreenContentLoginModule() {
log.info("ScreenContentLoginModule()................");
}
//在初始化LoginModel后,LoginContext会调用这一方法,从而完成当前LoginModel的初始化工作
public void initialize(Subject subject, CallbackHandler
callbackhandler, Map state, Map options) {
log.info("进入initialize()................");
this.principal = null;
this.subject = subject;
this.callbackhandler = callbackhandler;
}
//用户阶段1,并认证subject的方法,它可能会收集用户的凭证,比如用户名,密码,并将认证结果存储到LoginModel的实例中
public boolean login() throws LoginException {
log.info("进入login()................");
Callback callbacks[] = new Callback[2];
callbacks[0] = new NameCallback("您的登录名:");
callbacks[1] = new PasswordCallback("您的密码:", false);
String username = null;
String password = null;
try {
this.callbackhandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
password = new String(((PasswordCallback) callbacks[1]).getPassword());
} catch (java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException ce) {
throw new LoginException(ce.getCallback().toString());
}
if (username.equals("marissa") || username.equals("scott")) {
this.principal = new UsernamePasswordPrincipal(username,password);
return true;
} else {
return false;
}
}
//表明认证操作成功,会获得阶段1(login())的认证结果,并将这一结果填充到subject中
public boolean commit() throws LoginException {
log.info("进入commit()................");
if(this.principal == null)
return false;
this.subject.getPrincipals().add(this.principal);
return true;
}
//认证操作失败
public boolean abort() throws LoginException {
log.info("进入abort()................");
if(this.principal == null)
return false;
this.principal = null;
return true;
}
//完成subject的销毁工作
public boolean logout() throws LoginException {
log.info("进入logout()................");
if(this.principal == null)
return false;
this.subject.getPrincipals().remove(principal);
this.principal = null;
return true;
}
}
下面是UsernamePasswordCallbackhandler类:
package sample;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class UsernamePasswordCallbackHandler implements CallbackHandler {
protected static final Log log = LogFactory.getLog(UsernamePasswordCallbackHandler.class);
public void handle(Callback callbacks[]) throws IOException, UnsupportedCallbackException {
log.info("进入handle()........................");
for (Callback cb: callbacks) {
if (cb instanceof NameCallback) {
NameCallback nc = (NameCallback) cb;
log.info(nc.getPrompt());
//采集用户名
String username = (new BufferedReader(new InputStreamReader(
System.in))).readLine();
nc.setName(username);
} else if(cb instanceof PasswordCallback){
PasswordCallback pc = (PasswordCallback) cb;
log.info(pc.getPrompt());
//采集用户密码
String password = (new BufferedReader(new InputStreamReader(
System.in))).readLine();
pc.setPassword(password.toCharArray());
}
}
}
}
一旦用户收集到用户账号后NameCallback,PasswordCallback对象都会存储他们,与此同时,上述login()方法会基于账号信构建UsernamePasswordPrincipal对象,并保留在登录模块中,而且login()会返回true,当login方法顺利完成用户凭证信息的收集工作后,commit会被触发,她将UsernamePasswordPrincipal对象摆到Subject对象中。
当login方法未能顺利完成用户凭证信息的收集工作后,abort会被触发,将principal等信息破换掉。当登录用户完满的完成自身的业务操作后便可以考虑退出当前的应用,调用logout方法。下面是Principal对象:
package sample;
import java.security.Principal;
/**
*
* @author worldheart
*
*/
//Acegi中的Authentication接口继承了Principal接口
public class UsernamePasswordPrincipal implements Principal {
private String username;
private String password;
//存储用户名、密码,比如marissa/koala
public UsernamePasswordPrincipal(String username, String password) {
this.username = username;
this.password = password;
}
public String getName() {
return this.username;
}
public String toString() {
return this.username + "->" + this.password;
}
}
为了使用上述登录模块,需要准备一个jaas配置文件:
Loginmodel.conf放在src下面
ScreenContent {
sample.ScreenContentLoginModule required;
};
客户应用:
package sample;
import java.io.File;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class JaasSecurityClient {
protected static final Log log = LogFactory
.getLog(JaasSecurityClient.class);
public static void main(String argv[]) throws LoginException,
SecurityException {
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用户登录到当前应用中
ctx.login();
log.info("当前用户已经通过用户认证");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("启用JAAS用户授权能力");
// log.info("临时目录为," + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("当前用户正在经过JAAS授权操作的考验,并正调用目标业务操作");
// new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出当前已登录marissa用户
ctx.logout();
}
}
在运行客户应用之前还需要提供JVM参数,即引用到loginmoudel.conf配置文件:
-Djava.security.auth.login.config=src/loginmoudel.conf
或者通过javahome/jre/lib/security目录中的java.security配置文件指定上述loginmoudel.conf配置文件:
#login.config.url.l=file:${user.home}/.java.login.config
login.config.url.l=file:d:/eclipse/src/loginmoudel.conf
SecurityContextLoginModule是Acegi内置的一个LoginModel实现,当开发Jaas应用时,用户凭证信息的获取可能来自Acegi,此时,我们便可以采用内置的SecurityContextLoginModel。要使用SecurityContextLoginModule,我们需要在Jaas配置文件中配置它:
ACEGI {
org.acegisecurity.providers.jaas.SecurityContextLoginModule
required ignoreMissingAuthentication=true;
};
客户端应用:
package sample;
import java.io.File;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class AcegiSecurityClient {
protected static final Log log = LogFactory
.getLog(AcegiSecurityClient.class);
public static void main(String argv[]) throws LoginException, SecurityException {
LoginContext ctx = null;
//在实际企业应用中,Authentication对象的构建形式多种多样
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("marissa", "koala"));
ctx = new LoginContext("ACEGI");
// marissa用户登录到当前应用中
ctx.login();
log.info("当前用户已经通过用户认证");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("启用JAAS用户授权能力");
// log.info("临时目录为,"
// + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("当前用户正在经过JAAS授权操作的考验,并正调用目标业务操作");
// new File(
// "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf")
// .exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出当前已登录marissa用户
ctx.logout();
//清除已注册的SecurityContext
SecurityContextHolder.clearContext();
}
}
注意到我们并未为LoginContext提供CallbackHandler对象,由于Acegi负责提供兼容于Principal的Authentication对象,因此用户凭证的收集也不用CallbackHandler操心了。
在运行客户应用之前还需要提供JVM参数,即引用到loginmoudel.conf配置文件:
-Djava.security.auth.login.config=src/loginmoudel.conf
或者通过javahome/jre/lib/security目录中的java.security配置文件指定上述loginmoudel.conf配置文件:
#login.config.url.l=file:${user.home}/.java.login.config
login.config.url.l=file:d:/eclipse/src/loginmoudel.conf
启用Java安全管理器:大部分java开发者都知道,借助如下JVM参数能够启用java安全管理器,-Djava.security.manager。既然如此,我们通过如下JVM参数运行JaasSecurityClient客户端和AcegiSecurityClient客户端:
-Djava.security.manager -Djava.security.auth.login.config=src/loginmodule.conf
但是这样会出错:java.security.auth.login.config.AccessControlException:access denied
出错原因:默认时,直接借助“-Djava.security.manager”启动java安全管理器,JVM会采用javahome/jre/lib/security中的java.policy策略文件,而这一策略文件并未对上述涉及到的各种权限(比如:createLoginContext.ScreenContent,读取acegi.security.strategyJava属性)进行授权因此抛出了异常。
为此我们可以提供新的授权信息jaassecuritypolicy.txt策略文件。由于我们需要同LoginContext进行各类操作因此需要提供相关AuthPermission权限给Acegi
SecurityClient,同时我们使用了Commons-Logging,Log4j管理日志,因此还必须将相应的操作权限给这一客户,在操作日志的过程中,客户应用需要操控的:d:/ddlog.log日志文件因此需要将读写权限授给这一客户应用。
grant codebase "file:./-"{
permission java.io.FilePermission "D:/contactsforchapter8.log", "read, write";
permission javax.security.auth.AuthPermission "createLoginContext";
permission javax.security.auth.AuthPermission "modifyPrincipals";
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/log4j-1.2.14.jar" {
permission java.security.AllPermission;
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/commons-logging-1.0.4.jar" {
permission java.security.AllPermission;
};
实际上java的策略文件编写可以通过policytool工具。
运行JaasSecurityClient客户端应用:
-Djava.security.manager -Djava.security.policy=src/jaassecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
类似的运行AcegiSecurityClient的策略文件:
grant codebase "file:./-"{
permission java.util.PropertyPermission "acegi.security.strategy", "read";
permission java.io.FilePermission "D:/contactsforchapter8.log", "read, write";
permission javax.security.auth.AuthPermission "createLoginContext";
permission javax.security.auth.AuthPermission "modifyPrincipals";
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/log4j-1.2.14.jar" {
permission java.security.AllPermission;
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/commons-logging-1.0.4.jar" {
permission java.security.AllPermission;
};
运行AcegiSecurityClient客户端应用:
-Djava.security.manager -Djava.security.policy=src/acegisecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
启用Jaas的用户授权功能:jaas的授权能力依赖java策略文件,下面提供了另一个版本的jaasSecurityClient客户应用,新增了两行java代码:
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用户登录到当前应用中
ctx.login();
log.info("当前用户已经通过用户认证");
Subject subject = ctx.getSubject();
log.info(subject);
new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
System.getProperty("java.io.tmpdir");
// 退出当前已登录marissa用户
ctx.logout();
此时开发者必须往jaassecuritypolicy.txt策略文件中添加如下权限到其中:
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "READ";
如果客户要求只具有marissa用户才有权利运行上述两行代码,那么应该这样:
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用户登录到当前应用中
ctx.login();
log.info("当前用户已经通过用户认证");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("启用JAAS用户授权能力");
// log.info("临时目录为," + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("当前用户正在经过JAAS授权操作的考验,并正调用目标业务操作");
// new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出当前已登录marissa用户
ctx.logout();
那么jaassecuritypolicy.txt策略文件应该添加如下内容:
grant codebase "file:./-",
Principal sample.UsernamePasswordPrincipal "marissa" {
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "READ";
};
启动jaassecurityclient客户端:
-Djava.security.manager -Djava.security.policy=src/jaassecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
那么对于acegisecurityclient客户应用,acegisecuritypolicy.txt应该增加:
grant codebase "file:./-",
Principal org.acegisecurity.providers.UsernamePasswordAuthenticationToken "marissa" {
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "read";
};
启动:
-Djava.security.manager -Djava.security.policy=src/acegisecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
直击JaasAuthenticationProvider
配置:
<bean id="jaasAuthenticationProvider" class="org.acegisecurity.providers.jaas.JaasAuthenticationProvider">
<property name="authorityGranters"
<bean class="sample.TestAuthorityGranter"/>
</property>
<property name="callbackHandlers"
<list>
<bean class="org.acegisecurity.providers.jaas.JaasNameCallbackHandler"/>
<bean class="org.acegisecurity.providers.jaas.JaasPasswordCallbackHandler"/>
</property>
<property name="loginConfig" value="classpath:acegi.conf"/>
<property name="liginContextName" value="ACEGI"/>
</bean>
另外需要将JaasAuthenticationProvider添加到认证管理器:
acegi.conf的内容:
ACEGI {
sample.TestLoginModule required;
};
注释:authorityGranters属性能够为已经认证用户提供角色映射信息,由于这里的Jaas仅负责用户认证,而授权仍然被acegi接管。TestAuthorityGranter实现类:
package sample;
import java.security.Principal;
import java.util.HashSet;
import java.util.Set;
import org.acegisecurity.providers.jaas.AuthorityGranter;
/**
*
* @author worldheart
*
*/
public class TestAuthorityGranter implements AuthorityGranter {
public Set grant(Principal principal) {
Set<String> rtnSet = new HashSet<String>();
if (principal.getName().equals("TEST_PRINCIPAL")) {
rtnSet.add("ROLE_USER");
rtnSet.add("ROLE_ADMIN");
}
return rtnSet;
}
}
下面是TestLoginModel类:
package sample;
import java.security.Principal;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
/**
*
* @author worldheart
*
*/
public class TestLoginModule implements LoginModule {
private String user;
private String password;
private Subject subject;
public boolean abort() throws LoginException {
return true;
}
public boolean commit() throws LoginException {
return true;
}
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options) {
this.subject = subject;
try {
NameCallback nameCallback = new NameCallback("prompt");
PasswordCallback passwordCallback = new PasswordCallback("prompt",
false);
callbackHandler.handle(new Callback[] {nameCallback, passwordCallback });
user = nameCallback.getName();
password = new String(passwordCallback.getPassword());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean login() throws LoginException {
if (!user.equals("marissa")) {
throw new LoginException("用户名不对");
}
if (!password.equals("koala")) {
throw new LoginException("密码不对");
}
subject.getPrincipals().add(new Principal() {
public String getName() {
return "TEST_PRINCIPAL";
}
});
subject.getPrincipals().add(new Principal() {
public String getName() {
return "NULL_PRINCIPAL";
}
});
return true;
}
public boolean logout() throws LoginException {
return true;
}
}
声明:本人转载只为分享,如涉及商业版权纠纷,请及时与本人联系,必将立即删除,造成不便,还望见谅,谢谢。
Java平台是创建企业应用程序的普遍选择。它所以受欢迎,主要原因之一是在创建Java语言时充分考虑了安全性,而且市场上也普遍认为Java是一种"安全的"语言。Java平台在两个层次上提供安全性:语言层次安全性和企业层次安全性。
1.语言层次安全性
最初的Java(JDK1.2)平台采用沙箱安全模型,基本安全模型由三部分来承担,这三部分构成Java运行环境的三个安全组件,分别是:类加载器,文件校验器,安全管理器
1.1类加载器
类加载器负责从特定位置加载类,类加载器是JVM的看门人,控制着哪些代码被加载或被拒绝,类加载器首先进行安全性检查,它往往从以下几个方面去检查,装入的字节码是否生成指针,装入的字节码是否违反访问限制,装入的字节码是否把对象当作它们本身来访问,它在确保执行代码的安全性方面至关重要。
1.2类文件校验器的校验
类文件校验器负责检查那些无法执行的明显有破坏性的操作,类文件校验器执行的一些检查通常有:变量要在使用之前进行初始化;方法调用和对象引用类型之间要匹配;没有违反访问私有数据和方法的规则;对本地变量的访问都在运行时堆栈内;运行时堆栈是否溢出.如果以上检查中任何一条没有通过,就认为该类遭到了破坏,不被加载。
1.3安全管理器
一旦某个类被类加载器加载到虚拟机中,并由类文件校验器检查过之后,JAVA的第三种安全机制安全管理器就会启动,安全管理器是一个负责控制某个操作是否允许执行的类,安全管理器负责检查包括以下几个方面的操作:当前线程是否能创建一个新的类加载器;当前线程是否能终止JVM的运行;某个类是否能访问另一个类的成员;当前线程是否能访问本地文件;当前线程是否能打开到达外部记住的socket连接;某个类是否能启动打印作业;某个类是否能访问系统剪贴板;某个类是否能访问AWT事件队列;当前线程是否可被信任以打开一顶层窗口。
尽管Java安全的支柱类加载器、类文件校验器、安全管理器每一个都有独特的功能,但它们又相互依赖、相辅相承。共同保证了Java语言的安全性。
2.企业层次的安全特性
即是构建安全的J2EE应用。Java平台在提供语言安全性的同时还提供其他API功能,为企业应用程序提供一个总体的安全性解决方案。下面将介绍几种方案。
2.1 Java加密扩展(JCE)
JCE是一组包,为加密、密钥生成、密钥协商和消息身份验证代码(MAC)算法提供一种框架和实现。JCE支持多种类型的加密,包括对称的、非对称的、块和流密码。在JDK 1.4之前,JCE是一个可选的包,现在它已经成为Java平台的一个标准组成部分。
2.2 Java安全套接字扩展(JSSE)
JSSE是支持安全的Internet通信的一组包。它是实现了SSL和传输层安全(TLS)协议的JAVA技术。它包括用于数据加密、服务器身份验证、消息完整性和可选的客户端身份验证的诸多功能。JSSE已被集成到JDK 1.4以上版本的平台中。
2.3 Java身份验证和授权规范(JAAS)
JAAS通过对运行程序的用户的进行验证,从而达到保护系统的目的。JAAS主要由两个部件构成,认证和授权,JAAS通过一个配置文件来定义认证机制。认证模块是基于可插的认证模块而设计的,它可以运行在客户端和服务器端,授权模块的设计是一个变化的过程,为了对资源的访问请求进行授权,首先需要应用程序认证请求的资源,subject术语来表示请求的资源,用java.security.auth.Subject类来表示subject。subject一旦通过了认证,就会和身份和主体想关联。在JAAS中将主体表示为javax.security.Principal对象,一个subject可能包含多个主题,除了和主题相关联外,subject还可能拥有与安全相关的属性或证书,证书是用户的数据,它包含这样的认证信息即认证subject所拥有的其他服务的信息。基于J2EE的分布式应用程序使用
JAAS一般有两种情况:第一种情况,一个单独的应用系统与一个远程的EJB系统连接,用户必须向应用系统提供证明身份的信息或应用系统向文件和其它的系统来检索可证明身份的信息。这个单独的应用系统将在调用EJB组件之前使用JAAS来验证用户,由应用服务器完成验证的任务。只有当用户通过JAAS的验证之后,客户端程序才可被信任地调用EJB方法。第二种情况,基于Web浏览器的客户端程序连接到Servlet/JSP层,客户端用户将向Servlet/JSP层提供证明身份的信息,而Servlet/JSP层可以采用JAAS验证用户。Web客户端一般可以采
用基本验证、基于表格的验证、摘要验证、证书验证等方式来提供证明身份的信息。这种支持选择不同认证方法的灵活性有助于支持在管理员层实施更为复杂的安全策略,而不是在编程层上去实现。一旦客户端通过应用服务器认证,安全上下文环境能被传播到EJB层。在应用程序中使用JAAS验证通常会涉及到以下几个步骤:
1.创建一个LoginContext的实例。并传递LoginModule配置
文件程序段和CallbackHandler的名称。
2.为了能够获得和处理验证信息,将一个CallbackHandler
对象作为参数传送给LoginContext。
3.通过调用LoginContext的login()方法来进行验证。
4.通过使用login()方法返回的Subject对象实现一些特殊
的功能(假设登录成功)。
举个例子:
LoginModel是jaas的一个核心接口,她负责实施用户认证。同时暴漏了initialize(),login(),commit(),abort(),logout()方法。
package sample;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class ScreenContentLoginModule implements LoginModule {
protected static final Log log = LogFactory.getLog(ScreenContentLoginModule.class);
private Subject subject;
private UsernamePasswordPrincipal principal;
private CallbackHandler callbackhandler;
public ScreenContentLoginModule() {
log.info("ScreenContentLoginModule()................");
}
//在初始化LoginModel后,LoginContext会调用这一方法,从而完成当前LoginModel的初始化工作
public void initialize(Subject subject, CallbackHandler
callbackhandler, Map state, Map options) {
log.info("进入initialize()................");
this.principal = null;
this.subject = subject;
this.callbackhandler = callbackhandler;
}
//用户阶段1,并认证subject的方法,它可能会收集用户的凭证,比如用户名,密码,并将认证结果存储到LoginModel的实例中
public boolean login() throws LoginException {
log.info("进入login()................");
Callback callbacks[] = new Callback[2];
callbacks[0] = new NameCallback("您的登录名:");
callbacks[1] = new PasswordCallback("您的密码:", false);
String username = null;
String password = null;
try {
this.callbackhandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
password = new String(((PasswordCallback) callbacks[1]).getPassword());
} catch (java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException ce) {
throw new LoginException(ce.getCallback().toString());
}
if (username.equals("marissa") || username.equals("scott")) {
this.principal = new UsernamePasswordPrincipal(username,password);
return true;
} else {
return false;
}
}
//表明认证操作成功,会获得阶段1(login())的认证结果,并将这一结果填充到subject中
public boolean commit() throws LoginException {
log.info("进入commit()................");
if(this.principal == null)
return false;
this.subject.getPrincipals().add(this.principal);
return true;
}
//认证操作失败
public boolean abort() throws LoginException {
log.info("进入abort()................");
if(this.principal == null)
return false;
this.principal = null;
return true;
}
//完成subject的销毁工作
public boolean logout() throws LoginException {
log.info("进入logout()................");
if(this.principal == null)
return false;
this.subject.getPrincipals().remove(principal);
this.principal = null;
return true;
}
}
下面是UsernamePasswordCallbackhandler类:
package sample;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class UsernamePasswordCallbackHandler implements CallbackHandler {
protected static final Log log = LogFactory.getLog(UsernamePasswordCallbackHandler.class);
public void handle(Callback callbacks[]) throws IOException, UnsupportedCallbackException {
log.info("进入handle()........................");
for (Callback cb: callbacks) {
if (cb instanceof NameCallback) {
NameCallback nc = (NameCallback) cb;
log.info(nc.getPrompt());
//采集用户名
String username = (new BufferedReader(new InputStreamReader(
System.in))).readLine();
nc.setName(username);
} else if(cb instanceof PasswordCallback){
PasswordCallback pc = (PasswordCallback) cb;
log.info(pc.getPrompt());
//采集用户密码
String password = (new BufferedReader(new InputStreamReader(
System.in))).readLine();
pc.setPassword(password.toCharArray());
}
}
}
}
一旦用户收集到用户账号后NameCallback,PasswordCallback对象都会存储他们,与此同时,上述login()方法会基于账号信构建UsernamePasswordPrincipal对象,并保留在登录模块中,而且login()会返回true,当login方法顺利完成用户凭证信息的收集工作后,commit会被触发,她将UsernamePasswordPrincipal对象摆到Subject对象中。
当login方法未能顺利完成用户凭证信息的收集工作后,abort会被触发,将principal等信息破换掉。当登录用户完满的完成自身的业务操作后便可以考虑退出当前的应用,调用logout方法。下面是Principal对象:
package sample;
import java.security.Principal;
/**
*
* @author worldheart
*
*/
//Acegi中的Authentication接口继承了Principal接口
public class UsernamePasswordPrincipal implements Principal {
private String username;
private String password;
//存储用户名、密码,比如marissa/koala
public UsernamePasswordPrincipal(String username, String password) {
this.username = username;
this.password = password;
}
public String getName() {
return this.username;
}
public String toString() {
return this.username + "->" + this.password;
}
}
为了使用上述登录模块,需要准备一个jaas配置文件:
Loginmodel.conf放在src下面
ScreenContent {
sample.ScreenContentLoginModule required;
};
客户应用:
package sample;
import java.io.File;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class JaasSecurityClient {
protected static final Log log = LogFactory
.getLog(JaasSecurityClient.class);
public static void main(String argv[]) throws LoginException,
SecurityException {
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用户登录到当前应用中
ctx.login();
log.info("当前用户已经通过用户认证");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("启用JAAS用户授权能力");
// log.info("临时目录为," + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("当前用户正在经过JAAS授权操作的考验,并正调用目标业务操作");
// new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出当前已登录marissa用户
ctx.logout();
}
}
在运行客户应用之前还需要提供JVM参数,即引用到loginmoudel.conf配置文件:
-Djava.security.auth.login.config=src/loginmoudel.conf
或者通过javahome/jre/lib/security目录中的java.security配置文件指定上述loginmoudel.conf配置文件:
#login.config.url.l=file:${user.home}/.java.login.config
login.config.url.l=file:d:/eclipse/src/loginmoudel.conf
SecurityContextLoginModule是Acegi内置的一个LoginModel实现,当开发Jaas应用时,用户凭证信息的获取可能来自Acegi,此时,我们便可以采用内置的SecurityContextLoginModel。要使用SecurityContextLoginModule,我们需要在Jaas配置文件中配置它:
ACEGI {
org.acegisecurity.providers.jaas.SecurityContextLoginModule
required ignoreMissingAuthentication=true;
};
客户端应用:
package sample;
import java.io.File;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @author worldheart
*
*/
public class AcegiSecurityClient {
protected static final Log log = LogFactory
.getLog(AcegiSecurityClient.class);
public static void main(String argv[]) throws LoginException, SecurityException {
LoginContext ctx = null;
//在实际企业应用中,Authentication对象的构建形式多种多样
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken("marissa", "koala"));
ctx = new LoginContext("ACEGI");
// marissa用户登录到当前应用中
ctx.login();
log.info("当前用户已经通过用户认证");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("启用JAAS用户授权能力");
// log.info("临时目录为,"
// + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("当前用户正在经过JAAS授权操作的考验,并正调用目标业务操作");
// new File(
// "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf")
// .exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出当前已登录marissa用户
ctx.logout();
//清除已注册的SecurityContext
SecurityContextHolder.clearContext();
}
}
注意到我们并未为LoginContext提供CallbackHandler对象,由于Acegi负责提供兼容于Principal的Authentication对象,因此用户凭证的收集也不用CallbackHandler操心了。
在运行客户应用之前还需要提供JVM参数,即引用到loginmoudel.conf配置文件:
-Djava.security.auth.login.config=src/loginmoudel.conf
或者通过javahome/jre/lib/security目录中的java.security配置文件指定上述loginmoudel.conf配置文件:
#login.config.url.l=file:${user.home}/.java.login.config
login.config.url.l=file:d:/eclipse/src/loginmoudel.conf
启用Java安全管理器:大部分java开发者都知道,借助如下JVM参数能够启用java安全管理器,-Djava.security.manager。既然如此,我们通过如下JVM参数运行JaasSecurityClient客户端和AcegiSecurityClient客户端:
-Djava.security.manager -Djava.security.auth.login.config=src/loginmodule.conf
但是这样会出错:java.security.auth.login.config.AccessControlException:access denied
出错原因:默认时,直接借助“-Djava.security.manager”启动java安全管理器,JVM会采用javahome/jre/lib/security中的java.policy策略文件,而这一策略文件并未对上述涉及到的各种权限(比如:createLoginContext.ScreenContent,读取acegi.security.strategyJava属性)进行授权因此抛出了异常。
为此我们可以提供新的授权信息jaassecuritypolicy.txt策略文件。由于我们需要同LoginContext进行各类操作因此需要提供相关AuthPermission权限给Acegi
SecurityClient,同时我们使用了Commons-Logging,Log4j管理日志,因此还必须将相应的操作权限给这一客户,在操作日志的过程中,客户应用需要操控的:d:/ddlog.log日志文件因此需要将读写权限授给这一客户应用。
grant codebase "file:./-"{
permission java.io.FilePermission "D:/contactsforchapter8.log", "read, write";
permission javax.security.auth.AuthPermission "createLoginContext";
permission javax.security.auth.AuthPermission "modifyPrincipals";
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/log4j-1.2.14.jar" {
permission java.security.AllPermission;
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/commons-logging-1.0.4.jar" {
permission java.security.AllPermission;
};
实际上java的策略文件编写可以通过policytool工具。
运行JaasSecurityClient客户端应用:
-Djava.security.manager -Djava.security.policy=src/jaassecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
类似的运行AcegiSecurityClient的策略文件:
grant codebase "file:./-"{
permission java.util.PropertyPermission "acegi.security.strategy", "read";
permission java.io.FilePermission "D:/contactsforchapter8.log", "read, write";
permission javax.security.auth.AuthPermission "createLoginContext";
permission javax.security.auth.AuthPermission "modifyPrincipals";
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/log4j-1.2.14.jar" {
permission java.security.AllPermission;
};
grant codeBase
"file:D:/eclipse/workspace/contactsforchapter8/context/WEB-INF/lib/commons-logging-1.0.4.jar" {
permission java.security.AllPermission;
};
运行AcegiSecurityClient客户端应用:
-Djava.security.manager -Djava.security.policy=src/acegisecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
启用Jaas的用户授权功能:jaas的授权能力依赖java策略文件,下面提供了另一个版本的jaasSecurityClient客户应用,新增了两行java代码:
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用户登录到当前应用中
ctx.login();
log.info("当前用户已经通过用户认证");
Subject subject = ctx.getSubject();
log.info(subject);
new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
System.getProperty("java.io.tmpdir");
// 退出当前已登录marissa用户
ctx.logout();
此时开发者必须往jaassecuritypolicy.txt策略文件中添加如下权限到其中:
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "READ";
如果客户要求只具有marissa用户才有权利运行上述两行代码,那么应该这样:
LoginContext ctx = null;
ctx = new LoginContext("ScreenContent", new UsernamePasswordCallbackHandler());
//marissa用户登录到当前应用中
ctx.login();
log.info("当前用户已经通过用户认证");
Subject subject = ctx.getSubject();
log.info(subject);
// log.info("启用JAAS用户授权能力");
// log.info("临时目录为," + Subject.doAsPrivileged(subject, new PrivilegedAction() {
// public Object run() {
// log.info("当前用户正在经过JAAS授权操作的考验,并正调用目标业务操作");
// new File("D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf").exists();
// return System.getProperty("java.io.tmpdir");
// }
// }, null));
// 退出当前已登录marissa用户
ctx.logout();
那么jaassecuritypolicy.txt策略文件应该添加如下内容:
grant codebase "file:./-",
Principal sample.UsernamePasswordPrincipal "marissa" {
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "READ";
};
启动jaassecurityclient客户端:
-Djava.security.manager -Djava.security.policy=src/jaassecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
那么对于acegisecurityclient客户应用,acegisecuritypolicy.txt应该增加:
grant codebase "file:./-",
Principal org.acegisecurity.providers.UsernamePasswordAuthenticationToken "marissa" {
permission java.io.FilePermission "D:/eclipse/workspace/contactsforchapter8/src/loginmodule.conf", "read";
permission java.util.PropertyPermission "java.io.tmpdir", "read";
};
启动:
-Djava.security.manager -Djava.security.policy=src/acegisecuriypolicy.txt
-Djava.security.auth.login.config=src/loginmodule.conf
直击JaasAuthenticationProvider
配置:
<bean id="jaasAuthenticationProvider" class="org.acegisecurity.providers.jaas.JaasAuthenticationProvider">
<property name="authorityGranters"
<bean class="sample.TestAuthorityGranter"/>
</property>
<property name="callbackHandlers"
<list>
<bean class="org.acegisecurity.providers.jaas.JaasNameCallbackHandler"/>
<bean class="org.acegisecurity.providers.jaas.JaasPasswordCallbackHandler"/>
</property>
<property name="loginConfig" value="classpath:acegi.conf"/>
<property name="liginContextName" value="ACEGI"/>
</bean>
另外需要将JaasAuthenticationProvider添加到认证管理器:
acegi.conf的内容:
ACEGI {
sample.TestLoginModule required;
};
注释:authorityGranters属性能够为已经认证用户提供角色映射信息,由于这里的Jaas仅负责用户认证,而授权仍然被acegi接管。TestAuthorityGranter实现类:
package sample;
import java.security.Principal;
import java.util.HashSet;
import java.util.Set;
import org.acegisecurity.providers.jaas.AuthorityGranter;
/**
*
* @author worldheart
*
*/
public class TestAuthorityGranter implements AuthorityGranter {
public Set grant(Principal principal) {
Set<String> rtnSet = new HashSet<String>();
if (principal.getName().equals("TEST_PRINCIPAL")) {
rtnSet.add("ROLE_USER");
rtnSet.add("ROLE_ADMIN");
}
return rtnSet;
}
}
下面是TestLoginModel类:
package sample;
import java.security.Principal;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
/**
*
* @author worldheart
*
*/
public class TestLoginModule implements LoginModule {
private String user;
private String password;
private Subject subject;
public boolean abort() throws LoginException {
return true;
}
public boolean commit() throws LoginException {
return true;
}
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options) {
this.subject = subject;
try {
NameCallback nameCallback = new NameCallback("prompt");
PasswordCallback passwordCallback = new PasswordCallback("prompt",
false);
callbackHandler.handle(new Callback[] {nameCallback, passwordCallback });
user = nameCallback.getName();
password = new String(passwordCallback.getPassword());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public boolean login() throws LoginException {
if (!user.equals("marissa")) {
throw new LoginException("用户名不对");
}
if (!password.equals("koala")) {
throw new LoginException("密码不对");
}
subject.getPrincipals().add(new Principal() {
public String getName() {
return "TEST_PRINCIPAL";
}
});
subject.getPrincipals().add(new Principal() {
public String getName() {
return "NULL_PRINCIPAL";
}
});
return true;
}
public boolean logout() throws LoginException {
return true;
}
}
发表评论
-
MyEclipse生成代理方法(generate delegate methods)
2012-08-24 13:27 4314参考资料:《JAVA开发利器My+Eclipse全面详解》 ... -
ResultSet接口的absolute方法
2012-08-10 16:49 1016java.sql.ResultSet接口 boolean ab ... -
转载:单例设计模式中使用dom4j来完成(数据库配置文件)xml的解析,并完成数据库的连接
2012-08-10 15:12 1016转自:http://www.blogjava.ne ... -
.class文件批量反编译
2012-08-06 17:27 890步骤:1、下载jad.exe,并将其置于%JAVA_HOME% ...
相关推荐
- **Realm配置**:用于用户认证和授权,例如基于文件的用户数据库、JAAS集成等。 - **JNDI资源**:提供对数据库连接池、邮件服务等外部资源的访问。 通过深入研究这些版本的Tomcat,开发者不仅可以掌握服务器的基本...
【描述】"j2ee在线购物网实例源码,转载供大家共同学习"表明这是一个共享的学习资源,旨在促进开发者之间的知识交流和技能提升。通过分析和研究这个源码,开发者可以深入理解如何在实际项目中运用J2EE技术栈,包括...
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
,,基于SMO的三相PMSM无速度传感器控制(基于反正切函数) ,核心关键词:SMO(滑模观测器); 三相PMSM(永磁同步电机); 无速度传感器控制; 反正切函数; 控制系统。,基于SMO算法的三相PMSM无速度传感器反正切函数控制
网络文化互动中的舆论引导与危机应对
人力资源+大数据+薪酬报告+涨薪调薪,在学习、工作生活中,越来越多的事务都会使用到报告,通常情况下,报告的内容含量大、篇幅较长。那么什么样的薪酬报告才是有效的呢?以下是小编精心整理的调薪申请报告,欢迎大家分享。相信老板看到这样的报告,一定会考虑涨薪的哦。
内容概要:本文全面探讨了大学生沉迷网络游戏的现状及成因,强调该问题已严重影响大学生的学业和个人发展。据统计显示,中国大学生网络游戏成瘾患病率超过15%,问题广泛且严重。分析指出沉迷原因涵盖个人因素(如自我管理能力缺失、逃避现实压力)、家庭因素(例如家庭教育缺失和家庭氛围不和谐)、学校因素(如大学管理松散和校园文化活动匮乏),以及社会因素(例如网游设计吸引人和监管部门不严)。基于以上成因,提出了多层次综合治理方案,包括但不限于强化家庭教育和沟通、改善大学管理模式、丰富校园文化、加强网络游戏审查力度和社会心理健康辅导等方面的对策。 适用人群:本研究适用于高校辅导员、心理学家、教育政策决策人员,以及关心青年成长的社会各界人士。 使用场景及目标:本文旨在引起社会对该问题的关注,并为教育界和其他相关群体提供了详细的参考资料用于制定相应的干预措施,以减少大学生游戏成瘾情况的发生。此外,也可供家长学习科学育子知识。 其他说明:除了直接提出具体治理办法外,还特别提到了营造健康的网络文化环境的重要性,提倡多方协作共促学生健康发展。同时呼吁进一步加强对网络游戏产业的研究与管理,确保产业的良性发展的同时也能
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
矢量边界,行政区域边界,精确到乡镇街道,可直接导入arcgis使用
TI维也纳整流器设计.rar
自驾游中的手机APP推荐
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
视讯镜头专利复现,基本复现
,,OMRON CP1H PLC脉冲控制三轴伺服, 码垛机,实际项目,程序结构清析,有完整的注释,重复功能做成FB功能块,在其它项目可以导出直接用,MCGS触摸屏程序,有电气CAD图纸。 ,关键词:OMRON CP1H PLC;脉冲控制;三轴伺服;码垛机;程序结构清晰;完整注释;FB功能块;MCGS触摸屏程序;电气CAD图纸。,OMRON PLC三轴伺服脉冲控制程序:结构清晰、注释完整,FB功能块可复用,配合MCGS触摸屏及CAD图纸的实际项目应用
是一款基于JAVA的串口调试工具,支持波特率9600-115200,仅供参考学习使用,
,,CO2激光切割机雕刻机打标机写字机喷涂机巡边机控制软件,包含上位机和控制板,也可源码 视频展示只体现工作流程和加工效果,如果激光功率足够大最快速度能跑到每秒两米 支持文件格式说明: 控制版和上位机通信接口为百兆以太网接口,数据载体为标准TCP协议 1.g代码 2.打印图片 3.plt格式文件 4.激光机在切割有效线条时匀速切割 5.有效线条切割速度和空程速度分别设置 6.空程运行具备加减速控制 7.图片打印时上位机界面实时显示打印进度 8.打开的图片和图形文件可鼠标缩放和拖动 9.图片格式转并保存转完成的指定格式图片 10.手动回原点控制 ,核心关键词: CO2激光切割机; 雕刻机; 打标机; 写字机; 喷涂机; 巡边机; 控制软件; 上位机; 控制板; 源码; 视频展示; 工作流程; 加工效果; 激光功率; 速度; 两秒; 文件格式; g代码; 打印图片; plt格式文件; 有效线条切割; 空程速度设置; 加减速控制; 上位机界面实时显示; 图片缩放和拖动; 图片格式转换; 手动回原点控制。 关键词用分号隔开: CO2激光切割机; 喷涂机; 控制软件; g代码; 图片格式转
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。