`
grandboy
  • 浏览: 126008 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

编写自己的登录与访问控制模块(转载)

    博客分类:
  • JAAS
阅读更多

 原地址: http://www.smth.org/pc/pccon.php?id=3559&nid=74518&s=all

小按:
第一次写心得笔记,手都有点抖,班门弄斧啊,呵呵~~~欢迎各位大侠扔砖!
本文是一篇学习笔记,概要介绍了Java登录与授权机制及其应用。
在一些用到的API关键字上做了链接,可以在线查相应的文档。

=======================================================================================

     安全性是Java鼓吹得最多的特性之一,的确,Java的安全特性涵盖了从应用级别到语言级别乃至JVM本身。以前大家都知道有个Sandbox,但仅有Sandbox尚不能满足,或者说不能很方便地做到我们所需要的全部安全需求,譬如现在一个系统首先起码需要一个登录功能,更进一步的话,还需要对用户访问资源的行为进行约束,下面我想大致讲一下Java是怎样做这些事情的,基本上是一个总结或者说是“读后感”的性质,同时给出一个简单的实现例子,这个例子其实还是模仿人家的,呵呵……

1.Java的访问控制机制

     谈到访问控制,或者说“授权”,这里有两层含义,一是从资源的角度,这个socket端口是否被允许操作?这个文件是可读的?可写的?还是可执行的?还是以上都行?这就是我们在UNIX下用“ls -l”命令列出当前目录下文件时,那些“-rwx-”之类的含义;二是从访问者的角度,我想通过80端口看Web上新浪欧洲杯的新闻,在这个系统中有没有这个资格?我想播放D盘上一个名为“friends.rm”的视频文件,我得到了访问这个文件的权限了吗?我有运行播放器的权限吗?
     Java在访问控制策略上同时考虑了这两方面内容,你说“不对呀,我用FileOutputStream写文件,用Socket类连接远程主机都用得好好的,没什么限制呀”,这我们得先谈谈什么叫做“安全管理器”(SecurityManger)。安全管理器从JDK 1.0就开始有了,多古老啊!Java从设计的那一天开始就考虑了安全因素,安全管理器是Sandbox的最重要的一个部分,也是访问控制的总协调者,我们能够在通常情况下正常使用网络和文件,那是因为当启动application的时候(注意是application,不是applet!),如果你不加“-Djava.security.manager”选项,JVM是不会启动Sandbox的,这时你可以“为所欲为”,而不会碰到SecurityException之类的异常;一旦加入了“-Djava.security.manager”选项,你就会发现有一连串的异常出现喽!

Exception in thread "main" java.security.AccessControlException: access denied (……)
……

Java内置了一个默认的安全策略,这种情况下安全管理器首先装载的是这个默认的策略,不信啊,不信你检查一下你的“%JAVA_HOME%\jre\lib\security\”目录,是不是有个叫“java.policy”的文件?用notepad打开看看:


// Standard extensions get all permissions by default
grant codeBase "file:${java.home}/lib/ext/*" {
    permission java.security.AllPermission;
};
// default permissions granted to all domains
grant { 
    // Allows any thread to stop itself using the java.lang.Thread.stop()
    // method that takes no argument.
    // Note that this permission is granted by default only to remain
    // backwards compatible.
    // It is strongly recommended that you either remove this permission
    // from this policy file or further restrict it to code sources
    // that you specify, because Thread.stop() is potentially unsafe.
    // See "http://java.sun.com/notes" for more information.
    permission java.lang.RuntimePermission "stopThread";
    // allows anyone to listen on un-privileged ports
    permission java.net.SocketPermission "localhost:1024-", "listen";
    // "standard" properies that can be read by anyone
    permission java.util.PropertyPermission "java.version", "read";
    permission java.util.PropertyPermission "java.vendor", "read";
    permission java.util.PropertyPermission "java.vendor.url", "read";
    permission java.util.PropertyPermission "java.class.version", "read";
    permission java.util.PropertyPermission "os.name", "read";
    permission java.util.PropertyPermission "os.version", "read";
    permission java.util.PropertyPermission "os.arch", "read";
    permission java.util.PropertyPermission "file.separator", "read";
    permission java.util.PropertyPermission "path.separator", "read";
    permission java.util.PropertyPermission "line.separator", "read";
    permission java.util.PropertyPermission "java.specification.version", "read";
    permission java.util.PropertyPermission "java.specification.vendor", "read";
    permission java.util.PropertyPermission "java.specification.name", "read";
    permission java.util.PropertyPermission "java.vm.specification.version", "read";
    permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
    permission java.util.PropertyPermission "java.vm.specification.name", "read";
    permission java.util.PropertyPermission "java.vm.version", "read";
    permission java.util.PropertyPermission "java.vm.vendor", "read";
    permission java.util.PropertyPermission "java.vm.name", "read";
};

可以看到,JVM给沙箱内的application分配的权限仅限于中止线程,监听1024以上的TCP端口,以及对一些系统属性的读取权限,像一般的socket操作和文件操作的权限都没有。
     了解了安全管理器的概念以后我们回到授权问题上来。对用户来说,最担心的莫过于机器中病毒,病毒本质上是一种恶意的程序,所以访问控制首先是要对代码的权限进行控制,上面我一直都在谈Sandbox,也就是所谓的“沙箱”,熟悉Java安全性发展历史的朋友大概对它不会陌生,初期的Java是采用这样一种安全策略,即:本地代码是可信的,而远程代码是不可信的,譬如applet是一种从网络上下载到本地并在浏览器上运行的一段远程代码,因而是不可信的,所以早期的applet被完全置于Sandbox当中,得到的权限是非常有限的;在1.0以后,直至Java 2出现之前,安全策略作了一些灵活的改变,applet不再是完全被歧视的“二等公民”了,因为有了签名applet,用户可以选择信任这种经过签名的applet,从而applet也可以做一些以前被认为是“出格”的事情;到了Java 2,情况又变了,以前一向被信任的本地代码似乎也变得不是那么可靠了,这还真说不准,难保谁不会在你出去跟女朋友逛街的时候,偷偷溜进来在你机器上拷个病毒什么的 ^_^ ,这样本地代码就落到了和远程代码相等同的地位了,这是比较符合现实世界场景的,在Java 2中的安全策略被称之为“可配置的安全策略”,任何代码,只要是通过安全管理器访问,就必须为它预先设定好访问权限,在这个之外的资源还是别的什么东东,对不起,java.security.AccessControlException: access denied…… 此路不通!
    简单总结一下Java安全模型的发展史,大概就是下面的几幅图了:






2.了解几个主要的API

     JAAS的API基本上位于javax.security.auth包及其下属子包中,很容易找到的。

  • javax.security.auth.Subject
         Subject表征系统中一个认证的用户,这个词时而被译为“主题”时而被以为“主体”(下面我要谈到的Principal有时候也被译为“主体”),不管它有几个马甲,反正你就可以看成是在Java中你这个人的影子,你对系统的访问就体现为Subject.doAs()或Subject.doAsPrivileged()方法。

     

         由于现在普遍是多用户的系统,所以在实现代码级访问控制之外,我们还希望能够对用户的行为进行约束,因为对系统造成破坏的因素不仅仅是恶意代码,人自身的有意或无意的不当操作也会危及系统,譬如向上面说的你不在的时候别人可以在你机器上拷病毒,如果系统能在你不在的时候也能拒绝这个家伙的登录企图,那样麻烦岂不是少很多?于是在Java安全核心之外,提供了一个名为“Java认证与授权服务”(Java Authentication and Authorization Services,JAAS)东东,专门用来处理对用户的认证和授权,这也就是所谓的“以用户为中心的授权模型”,说白了就是在“以代码为中心的授权模型”上再加一层,首先用户要获得访问权限,然后用户去操纵代码,代码来实行真正的访问操作。下面我主要是讲讲JAAS是如何工作的。

  • java.security.Principal
         Principal代表用户的一种身份对象,一个用户的身份可能不只一个,他所在的组或所担任的角色也是一种身份,“张翠山”可以说“铁划银钩”,可以说“张三丰的徒弟”,可以说“张无忌他老爹”,我说“武当七侠”甚至“武当派”,当然也没错,这是一个组,呵呵。通过一次登录后,可能向Subject插入一个或多个Principal,这时候Subject才有实际意义,而不是一个空壳。
  • javax.security.auth.login.LoginContext
         LoginContext旨在提供一个开放的登录总接口,你只需要用从策略文件中取得的策略名,以及下面介绍的回调对象创建得到一个LoginContext,再调用一次login()方法即可完成登录,登录模块在这里是透明的。
  • javax.security.auth.spi.LoginModule
         登录模块实现了对用户的认证逻辑,它的作用是在登录配置文件中得到体现,在后面的例子里我们会看到怎么编写一个登录配置文件以及上面说过的策略文件。LoginModule接口包括五个主要的方法:

             initialize方法,初始化模块,保存当前Subject以及一些参数。

             login方法,判断一次登录过程中是否认证通过。

             commit方法,是否提交登录结果。咦,login不就行了吗?干吗要来个提交呢?这是因为JAAS采用的是类似于数据库事务处理的过程,将整体登录分为两阶段,尽管你login成功,但系统仍有权力根据你这次login的“地位”来决定究竟要不要接纳你的身份,只有通过commit,用户的Principal才会被真正添加到Subject当中,哼哼,真阴险!这里所说的login的“地位”是指策略文件中登录模块的“控制标记”选项,有点类似于优先级的概念,因为登录一个系统的过程可能会经过不止一个登录模块,譬如我们登录系统输入口令,但这个口令可能保存在一个数据库或LDAP目录中,访问这个数据源也需要经过认证,这就不止一个登录模块了吧?所以我们需要分清哪些认证过程是重要的,哪些又是次要的,系统对用户身份的接收与否是对这些策略综合权衡的结果。

             abort方法:哎呀,上面解释得是不是太多了?我们再看看abort,还记得数据库事务处理的回退过程(roll back)吗?abort就有点像roll back,表示系统并不接受你的身份,以前做过的统统作废,现场又恢复到和登录前完全一样。

             logout方法:注销过程,清除内部状态,并删除Subject中全部的Principal。 
  • javax.security.auth.callback.CallbackHandler
         回调对象是JAAS中用以将交互过程和认证逻辑分离的一种机制,这也是符合OO和松散耦合(loosely coupled是一个时髦词汇 ^_^)精神的。JAAS已经实现了一些常用的回调对象,包括取得用户名的NameCallback,取得口令的PasswordCallback,从终端获得输入文本的TextInputCallback,向终端发出文本消息的TextOutputCallback等等。我们所要做的仅仅是实现一个CallbackHandler接口,根据不同的交互信息类型,把从终端得到的信息填到相应的Callback中去就行了。后面的例子我是用了一个JoptionPane提示文本框来输入用户名和口令的。 
  • java.security.PrivilegedAction
          上面说了那么多登录相关的接口,该说说授权了,如果我们只谈写源代码,那么很简单,只要实现一个PrivilegedAction接口,覆盖一个run()方法,把你想要做的事情统统放到这个run中就可以了。但我说的只是写源代码部分,授权方面用得较多的还是在管理方面,譬如如何编写一个策略文件,下面我们就来看看JAAS登录和访问控制的一个完整流程。

    3.基本流程
          JAAS被称为是“可插拔的认证框架”(Pluggable Authentication Module,PAMs),其实PAM也不是SUN的专利,Linux上就有这方面的实现,但PAM确实是较早用在了Solaris系统上。我们看看JAAS在认证和授权方面是怎么体现PAM思想的:

        主要包括这么几个部分:
        用户的Principal(MyPrincipal.class)
        登录模块(MyLoginModule.class)
        回调对象(MyCallbackHandler.class)
        访问代码(MyAction.class)
        系统入口(JAASTest.class)
        资源(myfile.txt)
        策略配置文件(login.conf)
        登录配置文件(jaas.policy)
        启动脚本(JAASTest.bat)

          由于启动java的选项太长,所以写了一个shell,在控制台下运行JAASTest.bat,选项“-Djava.security.manager”指定启用安全管理器,执行的是JAASTest类的main线程,由于shell指定选项“-Djava.security.policy=jaas.policy”,该策略文件允许当前代码创建LoginContext,并授权进行其它一些操作,它首先初始化一个LoginContext,选项“-Djava.security.auth.login.config=login.conf”指定了登录配置文件,所以在当前目录下找到文件login.conf,该文件中指定的登录策略名称为“JAASTest”,所以在LoginCotext中第一个参数也是“JAASTest”,同时使用我们自定义的回调对象MyCallbackHandler。创建LoginContext成功,可以进行登录了,调用LoginContext的login方法,该方法找到login.conf中的登录模块MyLoginModule(当然可以有若干个登录模块,这里我只用了一个),执行该模块的登录过程,MyLoginModule首先初始化:

    Login module initializing ...

    并使用LoginContext所赋予它的回调对象MyCallbackHandler,该回调过程弹出两个图形对话框,要求输入用户名和口令,
  •                       

    我们使用指定的用户名“user”和口令“letmepass”,确定以后分别传给当前的NameCallback和PasswordCallback,然后回到MyLoginModule的login过程,该过程从回调对象处得到NameCallback和PasswordCallback,进行认证(这里仅仅是简单的用户名和口令的对比),

    MyLoginModule: Authentication pass!

    并决定是否commit,由于在login.conf中定义该登录模块是required,所以是一个必须通过才能整体认证成功的模块。

    MyLoginModule: Add a new principal to current subject.

    如果整体得到认证通过,那么Subject就可以授权允许MyAction中的代码了,如语句Subject.doAs(…)所示,该代码的动作是读取当前目录下的myfile.txt文件,并将其内容打印到控制台,注意到在策略文件jaas.policy中赋予MyPrincipal身份对myfile.txt的读取权限,所以我们成功看到控制台下出现

    Access successfully! Reading file:
    ==================================
    Why?
    Because they care!
    Because they want to know the truth!
    Because they want their country back!
    Because it still belongs to us as long as the people have the guts to fight for what they believe in!
     
    ==================================

         这是我喜欢的一部经典影片“JFK”中检察官Garrison激情的最后陈词中的一段,呵呵!
         以上过程我们可以用个图表来表示:


    4.简单的例子

         以上流程中使用到的Java源代码和配置文件如下:

    // MyPrincipal.java
    package com.jungleford.auth;
    import java.security.Principal;
    public class MyPrincipal implements Principal { // 一个Principal的例子
      private String name; // Principal的名字
      public MyPrincipal(String name)  {
        this.name = name;
      }
      public String getName()  { //取得Principal的名字
        return this.name;
      }
      public boolean equals(Object principal)  { // 判断两个Pincipal相同的依据
        if (principal instanceof MyPrincipal)
          return this.name.equals(((MyPrincipal)principal).getName());
        else
          return false;
      }
      public String toString()  { // Principal的表示
        return "MyPrincipal: " + this.name;
      }
      public int hashCode()  { // 确定本对象的散列值
        // 用于有基于散列容器的场合,判断在散列容器中是否是同一个对象。
        // 如果对hashCode感兴趣,请参见:
        // http://www-900.ibm.com/developerWorks/cn/java/j-jtp05273/
        return this.name.hashCode();
      }
    }
    下载源代码

    // MyLoginModule.java
    package com.jungleford.auth;
    import java.util.*;
    import java.io.IOException;
    import java.security.Principal;
    import javax.security.auth.*;
    import javax.security.auth.callback.*;
    import javax.security.auth.login.*;
    import javax.security.auth.spi.*;
    public class MyLoginModule implements LoginModule { // 一个登录模块的例子
      private Subject subject; // 登录主体的表征
      private CallbackHandler cbHandler; // 回调对象,提供终端下获取用户名、口令的界面
      private Map sharedState; // 用于缓存中间结果的共享区
      private Map options; // 用于保存某些登录模块所需要用到的一些配置选项
      private boolean succeeded = false// 一次login成功的标志
      private boolean cmtSucceeded = false// 整体登录成功的提交标志
      private String username; // 取得用户名
      private char[] password; // 取得口令
      private Principal principal; // 取得登录后的身份标志
      public void initialize(Subject subject,CallbackHandler cbHandler, Map sharedState,Map options)  { // 初始化过程
        System.out.println("Login module initializing ...");
        System.out.println();
        this.subject = subject;
        this.cbHandler = cbHandler;
        this.sharedState = sharedState;
        this.options = options;
      }
      public boolean login() throws LoginException  { // 一次登录过程
        if (cbHandler == null) // 尚未配置回调对象
          throw new LoginException("Error: No CallbackHandler available " +
                                      "to garner authentication information from the user");
        Callback[] cbs = new Callback[2]; // 仅使用用户名回调和口令回调
        cbs[0] = new NameCallback("Login: ");
        cbs[1] = new PasswordCallback("Password: ", false);
        try    {
          cbHandler.handle(cbs);
          username = ((NameCallback)cbs[0]).getName();
          char[] temp = ((PasswordCallback)cbs[1]).getPassword();
          if (temp == null)      { // 口令为空
            temp = new char[0];
          }
          password = new char[temp.length];
          System.arraycopy(temp, 0, password, 0, temp.length);
          ((PasswordCallback)cbs[1]).clearPassword(); // 清除内存中的口令痕迹
        }
        catch (IOException ioe)    {
          throw new LoginException(ioe.toString());
        }
        catch (UnsupportedCallbackException uce)    {
          throw new LoginException("Error: " + uce.getCallback().toString() +
                                      " not available to garner authentication information " +
                                      "from the user");
        }
        boolean usrCorrect = false// 用户名正确否?
        boolean pwdCorrect = false// 口令正确否?
        if (username.equals("user")) // 目前仅允许用户名为user的登录
          usrCorrect = true;
        if (usrCorrect &&
            password.length == 9 &&
            password[0] == 'l' &&
            password[1] == 'e' &&
            password[2] == 't' &&
            password[3] == 'm' &&
            password[4] == 'e' &&
            password[5] == 'p' &&
            password[6] == 'a' &&
            password[7] == 's' &&
            password[8] == 's')     {// user的口令指定为letmepass
          System.out.println("MyLoginModule: Authentication pass!");
          System.out.println();
          pwdCorrect = true;
          succeeded = true;
          return true// 一次登录成功
        }
        else    {
          System.out.println("MyLoginModule: Authentication failed!");
          System.out.println();
          succeeded = false;
          username = null;
          for (int i = 0; i < password.length; i++) // 清除内存中的口令痕迹
            password[i] = ' ';
          password = null;
          if (!usrCorrect)      {
            throw new FailedLoginException("Username incorrect!");
          }
          else      {
            throw new FailedLoginException("Password incorrect!");
          }
        }
      }
      public boolean commit() throws LoginException  { // 根据登录配置策略判断是否整体登录成功
        if (succeeded == false)    {
          return false;
        }
        else    {
          principal = new MyPrincipal(username);
          if (!subject.getPrincipals().contains(principal))
          subject.getPrincipals().add(principal); // 把新的身份添加到subject中
          System.out.println("MyLoginModule: Add a new principal to current subject.");
          System.out.println();
          username = null;
          for (int i = 0; i < password.length; i++) // 清除内存中的口令痕迹
            password[i] = ' ';
          password = null;
          cmtSucceeded = true;
          return true;
        }
      }
      public boolean abort() throws LoginException  { // 放弃登录,将状态复位至登录前
        if (succeeded == false)    {
          return false;
        }
        else if (succeeded == true && cmtSucceeded == false)    {
          succeeded = false;
          username = null;
          if (password != null)      {
            for (int i = 0; i < password.length; i++) // 清除内存中的口令痕迹
              password[i] = ' ';
            password = null;
          }
          principal = null;
        }
        else    {
          logout();
        }
        return true;
      }
      public boolean logout() throws LoginException  { // 注销,并将状态复位至登录前
        subject.getPrincipals().remove(principal);
        succeeded = false;
        succeeded = cmtSucceeded;
        username = null;
        if (password != null)    {
          for (int i = 0; i < password.length; i++) // 清除内存中的口令痕迹
            password[i] = ' ';
          password = null;
        }
        principal = null;
        return true;
      }
    }
    下载源代码

    // MyCallbackHandler.java
    package com.jungleford.auth;
    import java.io.IOException;
    import javax.security.auth.callback.*;
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    public class MyCallbackHandler implements CallbackHandler {
      public void handle(Callback[] cbs)
      throws IOException, UnsupportedCallbackException  {
        String username =
        JOptionPane.showInputDialog(null, "Available name: " + "user", "Enter your name",
           JOptionPane.QUESTION_MESSAGE);
        String password =
        JOptionPane.showInputDialog(null, "Available password: " + "letmepass", "Enter your password",
           JOptionPane.QUESTION_MESSAGE);
        for (int i = 0; i < cbs.length; i++)    {
          if (cbs[i] instanceof TextOutputCallback)      {
            TextOutputCallback toc = (TextOutputCallback)cbs[i];
            switch (toc.getMessageType())        {
              case TextOutputCallback.INFORMATION:
                System.out.println(toc.getMessage());
                break;
              case TextOutputCallback.ERROR:
                System.out.println("Error: " + toc.getMessage());
                break;
              case TextOutputCallback.WARNING:
                System.out.println("Warning: " + toc.getMessage());
                break;
              default:
                throw new IOException("Unsupported message type: " +
                                         toc.getMessageType());
            }
          }
          else if (cbs[i] instanceof NameCallback)      {
            // prompt the user for a username
            NameCallback nc = (NameCallback)cbs[i];
            //System.err.print(nc.getPrompt());
            //System.err.flush();
            nc.setName(username);
          }
          else if (cbs[i] instanceof PasswordCallback)      {
            // prompt the user for sensitive information
            PasswordCallback pc = (PasswordCallback)cbs[i];
            //System.err.print(pc.getPrompt());
            //System.err.flush();
            pc.setPassword(password.toCharArray());
          }
          else      {
            throw new UnsupportedCallbackException(cbs[i], "Unrecognized Callback");
          }
        }
      }
    }
    下载源代码

    //MyAction.java
    package com.jungleford.auth;
    import java.io.*;
    import java.security.*;
    public class MyAction implements PrivilegedAction { // 对资源的授权访问动作
      public Object run()  { // run方法是必须overriding的
        // 这里我们假设访问动作是读取当前目录下myfile.txt文件的内容
        File file = new File("myfile.txt");
        String content = "";
        try    {
          BufferedReader reader =
          new BufferedReader(
          new FileReader(file));
          String line = reader.readLine();
          while (line != null)      {
            content += line + "\n";
            line = reader.readLine();
          }
        }
        catch (Exception e)    {
          System.err.println("Error: Reading file failed!");
          System.err.println();
          e.printStackTrace();
        }
        return content;
      }
    }
    下载源代码

    //JAASTest.java
    package com.jungleford.auth;
    import javax.security.auth.Subject;
    import javax.security.auth.login.LoginContext;
    public class JAASTest { // 测试我们JAAS登录和授权的shell
      public static void main(String[] args) {
        LoginContext lc = null;
        try {// 创建context,使用自定义的回调对象,策略名为JAASTest
          // 简单起见,仅使

    分享到:
    评论

    相关推荐

      蓝牙手机控制的懒人专用智能房间控制器设计资料(转载、开源原理图、源码).zip

      在这个智能房间控制器中,蓝牙扮演了关键角色,使用户能够通过智能手机与控制器建立连接,发送控制指令,如开关灯光、调节温度等。 2. **手机应用程序开发**:调试版_蓝牙房间控制器.apk文件表明有一个安卓应用用于...

      转载MVC音乐商店实例_完整中文版

      - **角色权限**:根据用户角色控制对特定资源的访问权限。 #### 九、其他关键特性 - **母版页**:使用母版页统一布局样式,提高页面复用率。 - **布局**:定义页面的基本结构,便于维护和扩展。 - **AJAX 更新与...

      概要设计说明模板---转载---参考

      这部分简要概述了系统预期的性能指标,如响应时间、并发用户数等,以及采取的安全措施,如数据加密、访问控制等。 6. 维护与扩展性设计 考虑系统的可维护性和未来扩展性,设计应包含模块化、标准化的原则,以便于...

      ASP代码编写的群发邮件,测试版

      10. **mail_login.asp**:用户登录界面,用于验证用户身份并控制访问权限。 由于系统是测试版,说明部分功能可能未经过充分测试,可能存在漏洞、错误或不稳定因素。在实际使用前,需要对系统进行全面的功能测试和...

      ISO软件工程模板(6)概要设计说明书-转载

      - **数据结构与程序的关系**:映射数据结构与访问它们的程序之间的联系。 11. **系统出错处理设计**: - **出错信息**:列举可能出现的错误情况,以及对应的错误信息、含义和处理策略。 - **补救措施**:包括...

      vb编程100例(转载)

      VB编程100例(转载)是一份宝贵的资源,它包含了大量的Visual Basic(VB)编程实践案例,旨在帮助初学者和有一定基础的开发者巩固和提升VB编程技能。Visual Basic是微软公司开发的一种事件驱动编程语言,它以其直观的...

      SSM整合,参考尚硅谷视频,转载笔记,非原创,侵权删

      这一体系结构为开发者提供了强大的控制层、服务层和数据访问层的支持,使得企业级应用的开发更为高效和便捷。下面将详细介绍这三个组件以及它们的整合过程。 **1. Spring框架** Spring是Java企业级应用的核心框架,...

      转载软件测试试题

      - **控制传递测试**:验证控制流在各模块间的正确传递。 - **性能测试**:评估集成后的性能表现。 - **安全测试**:检查集成后的安全漏洞。 #### 集成测试与系统测试的关系 集成测试和系统测试都是软件测试的重要...

      JAVA OA平台源码(转载)SPRING BOOT....

      在OA系统中,可能需要对用户进行登录验证,控制不同角色的访问权限。Spring Boot可以很方便地集成Spring Security,实现安全控制。 8. **测试**:Spring Boot支持单元测试和集成测试,可以使用JUnit、Mockito等工具...

      nanoFramework ESP32 RMT发射器-电路方案

      如何在ESP32上直接从nanoFramework生成脉冲序列。 硬件组件: Espressif ESP32S× 1 ...使用这个包装器,可以编写一个WS2812 LED控制库,但它本身很有用,因此它与LED控制库分开。 电路城原创内容,未经同意,不得转载!

      Linux字符设备驱动(转载)

      Linux字符设备驱动是操作系统内核与硬件交互的重要组成部分,它允许应用程序通过标准的文件操作接口与特定的硬件设备进行通信。在Linux系统中,字符设备是按照字符流进行数据传输的设备,比如键盘和打印机。虽然通常...

      悠索科技高校教务管理系统(转载)

      2. **Entity Framework**:可能用于数据库操作,这是一个ORM(对象关系映射)工具,使得开发者可以使用C#对象来操作数据库,简化了数据访问层的代码编写。 3. **LINQ(Language Integrated Query)**:C#中的查询...

      jsp论坛—别的网站转载的

      开发者可以深入理解代码结构,学习和借鉴设计思路,甚至根据自己的需求添加新功能或修复已知问题。这种开源模式也促进了技术的传播和创新,使得JSP论坛在不断迭代中变得更为成熟和完善。 JSP(JavaServer Pages)是...

      数畅信息平台专用版 开发实例 版本:V 4.0

      这个版本的发布旨在提供更高效、稳定和功能丰富的开发环境,以帮助用户快速构建和定制自己的信息系统。下面将详细介绍该平台的核心特性和开发实例。 一、平台特性 1. **模块化设计**:数畅信息平台采用模块化结构...

      转载 - 26本 Ruby/Rails 相关英文图书简评

      1. **Ruby 基础**:学习 Ruby 首先要掌握其基础语法,如变量、常量、数据类型(包括字符串、数组、哈希等)、控制结构(条件语句、循环)、方法定义和调用、类与对象、模块、继承和多态性等。 2. **元编程**:Ruby ...

      汇编语言调试环境masm

      在IT领域,汇编语言是一种低级编程语言,它与计算机硬件紧密相关,允许程序员直接控制计算机的硬件资源。汇编语言的调试环境是学习和开发汇编程序的关键部分,而MASM(Microsoft Macro Assembler)是微软提供的一款...

      jsr168 portlet(struts2+spring2.5+hibernate3.3)(转载)

      - `spring.jar`:Spring框架的核心库,包含核心容器、数据访问/集成、Web、AOP和测试模块。 - `hibernate3.jar`:Hibernate3的主要库,包含实体管理和查询API。 - `xwork-2.1.2.jar`:Struts2的基础,提供了动作执行...

      【RPA之家转载AA视频教程】3.A2019 - Python script pacakge with single argument.rar

      1. 创建Python脚本:解释如何编写一个接受命令行参数的Python脚本,使用`sys.argv`来访问这些参数。 2. 脚本打包:演示如何将Python脚本打包成可执行文件或模块,以便于RPA工具调用。 3. 集成RPA工具:介绍如何在RPA...

      使用浏览器访问python写的服务器程序

      1. Python套接字编程:Python的socket模块允许你访问底层网络通信协议。在本例中,我们使用socket模块中的功能来创建一个TCP套接字,并将它绑定到特定的IP地址和端口上。这是一个TCP服务器的基础,它使用SOCKET套接...

    Global site tag (gtag.js) - Google Analytics