Apache Shiro Reference Documentation

  1. Overview
  2. Core
  3. Spring-based Applications

1.Overview

  1. pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.shiro.tutorials</groupId>
    <artifactId>shiro-tutorial</artifactId>
    <version>1.0.0</version>
    <name>First Apache Shiro Application</name>
    <packaging>jar</packaging> <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties> <build>
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.0.2</version>
    <configuration>
    <source>1.8</source>
    <target>1.8</target>
    <encoding>${project.build.sourceEncoding}</encoding>
    </configuration>
    </plugin> <!-- This plugin is only to test run our little application. It is not
    needed in most Shiro-enabled applications: -->
    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.1</version>
    <executions>
    <execution>
    <goals>
    <goal>java</goal>
    </goals>
    </execution>
    </executions>
    <configuration>
    <classpathScope>test</classpathScope>
    <mainClass>Tutorial</mainClass>
    </configuration>
    </plugin>
    </plugins>
    </build> <dependencies>
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.3.2</version>
    </dependency>
    <!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding
    in this example app. -->
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.6.1</version>
    <scope>test</scope>
    </dependency>
    </dependencies> </project>
  2. src\main\resources\shiro.ini
    # =============================================================================
    # Tutorial INI configuration
    #
    # Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
    # ============================================================================= # -----------------------------------------------------------------------------
    # Users and their (optional) assigned roles
    # username = password, role1, role2, ..., roleN
    # -----------------------------------------------------------------------------
    [users]
    root = secret, admin
    agror = agror, goodguy, schwartz
    guest = guest, guest
    presidentskroob = 12345, president
    darkhelmet = ludicrousspeed, darklord, schwartz
    lonestarr = vespa, goodguy, schwartz # -----------------------------------------------------------------------------
    # Roles with assigned permissions
    # roleName = perm1, perm2, ..., permN
    # -----------------------------------------------------------------------------
    [roles]
    admin = *
    schwartz = lightsaber:*
    goodguy = winnebago:drive:eagle5
  3. src\main\java\Tutorial.java
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory; public class Tutorial { private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class); public static void main(String[] args) {
    log.info("My First Apache Shiro Application"); //1.
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //2.
    SecurityManager securityManager = factory.getInstance(); //3.
    SecurityUtils.setSecurityManager(securityManager); //4.
    Subject currentUser = SecurityUtils.getSubject();
    //Session session = currentUser.getSession();
    //session.setAttribute( "someKey", "aValue" );
    //session.setAttribute( "argor", "argor" ); //5.
    // Login
    if ( !currentUser.isAuthenticated() ) {
    //collect user principals and credentials in a gui specific manner
    //such as username/password html form, X509 certificate, OpenID, etc.
    //We'll use the username/password example here since it is the most common.
    //UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
    UsernamePasswordToken token = new UsernamePasswordToken("agror", "agror"); //this is all you have to do to support 'remember me' (no config - built in!):
    token.setRememberMe(true); try {
    currentUser.login( token );
    //if no exception, that's it, we're done! //print their identifying principal (in this case, a username):
    log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." ); //6.
    TestAuthorization( currentUser );
    } catch ( UnknownAccountException uae ) {
    //username wasn't in the system, show them an error message?
    } catch ( IncorrectCredentialsException ice ) {
    //password didn't match, try again?
    } catch ( LockedAccountException lae ) {
    //account for that username is locked - can't login. Show them a message?
    } catch ( AuthenticationException ae ) {
    //unexpected condition - error?
    } finally {
    log.info( "User login status: [- " + currentUser.isAuthenticated() + " -]." ); // Logout
    currentUser.logout();
    //removes all identifying information and invalidates their session too.
    }
    } System.exit(0);
    } private static void TestAuthorization ( Subject currentUser ) {
    //6.
    // We can also test to see if they have specific role or not:
    if ( currentUser.hasRole( "schwartz" ) ) {
    log.info( currentUser.getPrincipal() + " May the Schwartz be with you!" );
    } else {
    log.info( currentUser.getPrincipal() + " , mere mortal, has't schwartz." );
    } if ( currentUser.hasRole( "goodguy" ) ) {
    log.info( currentUser.getPrincipal() + " May the goodguy be with you!" );
    } else {
    log.info( currentUser.getPrincipal() + " , mere mortal, has't goodguy." );
    } if ( currentUser.hasRole( "admin" ) ) {
    log.info( currentUser.getPrincipal() + " May the admin be with you!" );
    } else {
    log.info( currentUser.getPrincipal() + " , mere mortal, has't admin." );
    } // We can also see if they have a permission to act on a certain type of entity:
    if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
    log.info("You may use a lightsaber ring.");
    } else {
    log.info("Sorry, lightsaber rings are for schwartz masters only.");
    }
    // Also, we can perform an extremely powerful instance-level permission
    // check - the ability to see if the user has the ability to access a
    // specific instance of a type:
    if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
    log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " +
    "Here are the keys - have fun!");
    } else {
    log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
    }
    }
    }
  4. run
    E:\Users\Administrator\Desktop\shiro\shiro-tutorial>mvn compile exec:java
    [INFO] Scanning for projects...
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] Building First Apache Shiro Application 1.0.0
    [INFO] ------------------------------------------------------------------------
    [INFO]
    [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ shiro-tutorial ---
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Copying 1 resource
    [INFO]
    [INFO] --- maven-compiler-plugin:2.0.2:compile (default-compile) @ shiro-tutorial ---
    [INFO] Compiling 1 source file to E:\Users\Administrator\Desktop\shiro\shiro-tutorial\target\classes
    [INFO]
    [INFO] >>> exec-maven-plugin:1.1:java (default-cli) > validate @ shiro-tutorial >>>
    [INFO]
    [INFO] <<< exec-maven-plugin:1.1:java (default-cli) < validate @ shiro-tutorial <<<
    [INFO]
    [INFO] --- exec-maven-plugin:1.1:java (default-cli) @ shiro-tutorial ---
    0 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application
    47 [Tutorial.main()] INFO Tutorial - User login status: [- false -]. E:\Users\Administrator\Desktop\shiro\shiro-tutorial>mvn compile exec:java
    [INFO] Scanning for projects...
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] Building First Apache Shiro Application 1.0.0
    [INFO] ------------------------------------------------------------------------
    [INFO]
    [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ shiro-tutorial ---
    [INFO] Using 'UTF-8' encoding to copy filtered resources.
    [INFO] Copying 1 resource
    [INFO]
    [INFO] --- maven-compiler-plugin:2.0.2:compile (default-compile) @ shiro-tutorial ---
    [INFO] Compiling 1 source file to E:\Users\Administrator\Desktop\shiro\shiro-tutorial\target\classes
    [INFO]
    [INFO] >>> exec-maven-plugin:1.1:java (default-cli) > validate @ shiro-tutorial >>>
    [INFO]
    [INFO] <<< exec-maven-plugin:1.1:java (default-cli) < validate @ shiro-tutorial <<<
    [INFO]
    [INFO] --- exec-maven-plugin:1.1:java (default-cli) @ shiro-tutorial ---
    0 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application
    31 [Tutorial.main()] INFO org.apache.shiro.session.mgt.AbstractValidatingSessionManager - Enabling session validation scheduler...
    109 [Tutorial.main()] INFO Tutorial - User [agror] logged in successfully.
    109 [Tutorial.main()] INFO Tutorial - agror May the Schwartz be with you!
    109 [Tutorial.main()] INFO Tutorial - agror May the goodguy be with you!
    109 [Tutorial.main()] INFO Tutorial - agror , mere mortal, has't admin.
    109 [Tutorial.main()] INFO Tutorial - You may use a lightsaber ring.
    109 [Tutorial.main()] INFO Tutorial - You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. Here are the keys - have fun!
    109 [Tutorial.main()] INFO Tutorial - User login status: [- true -].

    第一次执行时,使用一个错误的密码;第二次执行使用正确的密码;

2.Core

  1. Authentication
    1. Authenticating Subjects
      Step 1: Collect the Subject’s principals and credentials

      //Example using most common scenario of username/password pair:
      UsernamePasswordToken token = new UsernamePasswordToken(username, password); //"Remember Me" built-in:
      token.setRememberMe(true);

      Step 2: Submit the principals and credentials

      Subject currentUser = SecurityUtils.getSubject();
      currentUser.login(token);

      Step 3: Handling Success or Failure

      try {
      currentUser.login(token);
      } catch ( UnknownAccountException uae ) { ...
      } catch ( IncorrectCredentialsException ice ) { ...
      } catch ( LockedAccountException lae ) { ...
      } catch ( ExcessiveAttemptsException eae ) { ...
      } ... catch your own ...
      } catch ( AuthenticationException ae ) {
      //unexpected error?
      }

      <Login Failure Tip> The security best practice is to only show a generic failure message , for example, "Incorrect username or password".

    2. Logging Out
      currentUser.logout(); //removes all identifying information and invalidates their session too.
  2. Authorization
    1. Understanding Permissions in Apache Shiro
      Wildcard Permissions & Checking Permissions

      Simple Usage
      subject.isPermitted("sshdStart")
      subject.isPermitted("sshdStop")
      subject.isPermitted("sshdRestart") Multiple Parts
      subject.isPermitted("sshd:start")
      subject.isPermitted("sshd:stop")
      subject.isPermitted("sshd:restart") Multiple Values
      subject.isPermitted("sshd:start,stop,restart")
      subject.isPermitted("sshd:*") Instance-Level Access Control
      subject.isPermitted("sshd:start:root")
      subject.isPermitted("sshd:*:root")
      subject.isPermitted("sshd:*:*") Missing Parts
      subject.isPermitted("sshd:start:")//is equivalent to below
      subject.isPermitted("sshd:start:*")
      subject.isPermitted("sshd")//is equivalent to below
      subject.isPermitted("sshd:*:*")
    2. Elements of Authorization
      ♠Permission statements reflect behavior (actions associated with resource types) only.
      ♠A Role is a named entity that typically represents a set of behaviors or responsibilities.
      ♠A user essentially is the ‘who’ of an application. As we’ve covered previously however, the Subject is really Shiro’s ‘User’ concept.
    3. Authorizing Subjects
      • ♠Programmatically
        Role-Based Authorization

        Subject currentUser = SecurityUtils.getSubject();
        
        if (currentUser.hasRole("administrator")) {
        //show the admin button
        } else {
        //don't show the button? Grey it out?
        }

        Permission-Based Authorization

        Permission printPermission = new PrinterPermission("laserjet4400n", "print");
        
        Subject currentUser = SecurityUtils.getSubject();
        
        if (currentUser.isPermitted(printPermission)) {
        //show the Print button
        } else {
        //don't show the button? Grey it out?
        }
        Subject currentUser = SecurityUtils.getSubject();
        
        if (currentUser.isPermitted("printer:print:laserjet4400n")) {
        //show the Print button
        } else {
        //don't show the button? Grey it out?
        }
        Subject currentUser = SecurityUtils.getSubject();
        
        Permission p = new WildcardPermission("printer:print:laserjet4400n");
        
        if (currentUser.isPermitted(p) {
        //show the Print button
        } else {
        //don't show the button? Grey it out?
        }
      • ♠JDK annotations( <need> to enable AOP support in your application)
        The RequiresAuthentication annotation
        @RequiresAuthentication
        public void updateAccount(Account userAccount) {
        //this method will only be invoked by a
        //Subject that is guaranteed authenticated
        ...
        }
        //This is mostly equivalent to the following Subject-based logic: public void updateAccount(Account userAccount) {
        if (!SecurityUtils.getSubject().isAuthenticated()) {
        throw new AuthorizationException(...);
        } //Subject is guaranteed authenticated here
        ...
        }

        The RequiresGuest annotation

        @RequiresGuest
        public void signUp(User newUser) {
        //this method will only be invoked by a
        //Subject that is unknown/anonymous
        ...
        }
        //This is mostly equivalent to the following Subject-based logic: public void signUp(User newUser) {
        Subject currentUser = SecurityUtils.getSubject();
        PrincipalCollection principals = currentUser.getPrincipals();
        if (principals != null && !principals.isEmpty()) {
        //known identity - not a guest:
        throw new AuthorizationException(...);
        } //Subject is guaranteed to be a 'guest' here
        ...
        }

        The RequiresPermissions annotation

        @RequiresPermissions("account:create")
        public void createAccount(Account account) {
        //this method will only be invoked by a Subject
        //that is permitted to create an account
        ...
        }
        //This is mostly equivalent to the following Subject-based logic: public void createAccount(Account account) {
        Subject currentUser = SecurityUtils.getSubject();
        if (!subject.isPermitted("account:create")) {
        throw new AuthorizationException(...);
        } //Subject is guaranteed to be permitted here
        ...
        }

        The RequiresRoles permission

        @RequiresRoles("administrator")
        public void deleteUser(User user) {
        //this method will only be invoked by an administrator
        ...
        }
        //This is mostly equivalent to the following Subject-based logic: public void deleteUser(User user) {
        Subject currentUser = SecurityUtils.getSubject();
        if (!subject.hasRole("administrator")) {
        throw new AuthorizationException(...);
        } //Subject is guaranteed to be an 'administrator' here
        ...
        }

        The RequiresUser annotation

        @RequiresUser
        public void updateAccount(Account account) {
        //this method will only be invoked by a 'user'
        //i.e. a Subject with a known identity
        ...
        }
        This is mostly equivalent to the following Subject-based logic: public void updateAccount(Account account) {
        Subject currentUser = SecurityUtils.getSubject();
        PrincipalCollection principals = currentUser.getPrincipals();
        if (principals == null || principals.isEmpty()) {
        //no identity - they're anonymous, not allowed:
        throw new AuthorizationException(...);
        } //Subject is guaranteed to have a known identity here
        ...
        }

3.Integrating into Spring-based Applications

Shiro applications need an application singleton SecurityManager instance.

  1. web.xml
    Here is how to configure Shiro in a Spring-based web application:

    <!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml -->
    <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
    <param-name>targetFilterLifecycle</param-name>
    <param-value>true</param-value>
    </init-param>
    </filter> ... <!-- Make sure any request you want accessible to Shiro is filtered. /* catches all -->
    <!-- requests. Usually this filter mapping is defined first (before all others) to -->
    <!-- ensure that Shiro works in subsequent filters in the filter chain: -->
    <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
  2. applicationContext.xml
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <!-- override these for application-specific URLs if you like:
    <property name="loginUrl" value="/login.jsp"/>
    <property name="successUrl" value="/home.jsp"/>
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/> -->
    <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean -->
    <!-- defined will be automatically acquired and available via its beanName in chain -->
    <!-- definitions, but you can perform instance overrides or name aliases here if you like: -->
    <!-- <property name="filters">
    <util:map>
    <entry key="anAlias" value-ref="someFilter"/>
    </util:map>
    </property> -->
    <property name="filterChainDefinitions">
    <value>
    # some example chain definitions:
    /admin/** = authc, roles[admin]
    /docs/** = authc, perms[document:read]
    /** = authc
    # more URL-to-FilterChain definitions here
    </value>
    </property>
    </bean> <!-- Define any javax.servlet.Filter beans you want anywhere in this application context. -->
    <!-- They will automatically be acquired by the 'shiroFilter' bean above and made available -->
    <!-- to the 'filterChainDefinitions' property. Or you can manually/explicitly add them -->
    <!-- to the shiroFilter's 'filters' Map if desired. See its JavaDoc for more details. -->
    <bean id="someFilter" class="..."/>
    <bean id="anotherFilter" class="..."> ... </bean>
    ... <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <!-- Single realm app. If you have multiple realms, use the 'realms' property instead. -->
    <property name="realm" ref="myRealm"/>
    <!-- By default the servlet container sessions will be used. Uncomment this line
    to use shiro's native sessions (see the JavaDoc for more): -->
    <!-- <property name="sessionMode" value="native"/> -->
    </bean>
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- Define the Shiro Realm implementation you want to use to connect to your back-end -->
    <!-- security datasource: -->
    <bean id="myRealm" class="...">
    ...
    </bean>
  3. Enabling Shiro Annotations
    Here is how to enable these annotations. Just add these two bean definitions to applicationContext.xml:
    <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after -->
    <!-- the lifecycleBeanProcessor has run: -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"/>
    </bean>

.

official shiro(Reference Manual)的更多相关文章

  1. Java中的原始类型(Primitive Types)与引用类型(Reference Values)

    Java虚拟机可以处理的类型有两种,一种是原始类型(Primitive Types),一种是引用类型(Reference Types). 与之对应,也存在有原始值(Primitive Values)和 ...

  2. C#中的值类型(value type)与引用类型(reference type)的区别

    ylbtech- .NET-Basic:C#中的值类型与引用类型的区别 C#中的值类型(value type)与引用类型(reference type)的区别 1.A,相关概念返回顶部     C#中 ...

  3. (20)Cocos2d-x中的引用计数(Reference Count)和自动释放池(AutoReleasePool)

    引用计数 引用计数是c/c++项目中一种古老的内存管理方式.当我8年前在研究一款名叫TCPMP的开源项目的时候,引用计数就已经有了. iOS SDK把这项计数封装到了NSAutoreleasePool ...

  4. 【转】 C++易混知识点4: 自己编写一个智能指针(Reference Counting)学习auto_ptr和reference counting

    这篇文章建大的介绍了如何编写一个智能指针. 介绍: 什么是智能指针?答案想必大家都知道,智能指针的目的就是更好的管理好内存和动态分配的资源,智能指针是一个智能的指针,顾名思义,他可以帮助我们管理内存. ...

  5. SpringMVC整合Shiro(配解释)

    第一步:配置web.xml ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!-- 配置Shiro过滤器,先让Shiro过滤系统接收到的请求 --> ...

  6. C++11 特性:成员函数引用限定 (Reference qualifier)

    学了这么多年C++今天拜读scott meyes的more effective cpp第一次看到这种写法... 引用限定可以让成员函数只能被左值对象调用或者只能被右值对象调用: #include &l ...

  7. 【Netty官方文档翻译】引用计数对象(reference counted objects)

    知乎有关于引用计数和垃圾回收GC两种方式的详细讲解 https://www.zhihu.com/question/21539353 原文出处:http://netty.io/wiki/referenc ...

  8. iOS项目开发中的知识点与问题收集整理②(Part 二)

    1.点击UIButton 无法产生触摸事件    如果在UIImageView中添加了一个按钮,你会发现在默认情况下这个按钮是无法被点击的,需要设置UIImageView的userInteractio ...

  9. iOS 面试大全从简单到复杂(简单篇)

    1.UIWindow和UIView和 CALayer 的联系和区别? 答:UIView是视图的基类,UIViewController是视图控制器的基类,UIResponder是表示一个可以在屏幕上响应 ...

随机推荐

  1. Asp.Net Grieview Eval 绑定数据 调用JS事件

    <asp:TemplateField ItemStyle-HorizontalAlign="Center"> <ItemTemplate> <asp: ...

  2. java小程序(课堂作业04)

    请编写一个程序,使用上述算法加密或解密用户输入的英文字串要求设计思想.程序流程图.源代码.结果截图. 1,设计思想: 先输入索要加密的字符串由于此程序比较基础所以只考虑大写字母,然后用toCharAr ...

  3. Python依赖打包发布详细

    http://www.cnblogs.com/mywolrd/p/4756005.html 将Python脚本打包成可执行文件   Python是一个脚本语言,被解释器解释执行.它的发布方式: .py ...

  4. MyEclipse+Tomcat配置

    一.Tomcat 1 Tomcat概述 Tomcat服务器由Apache提供,开源免费.由于Sun和其他公司参与到了Tomcat的开发中,所以最新的JSP/Servlet规范总是能在Tomcat中体现 ...

  5. 查询某个SPID,session_id对应的执行sql.

    select er.session_id, CAST(csql.text AS varchar(255)) AS CallingSQL from master.sys.dm_exec_requests ...

  6. esp8266尝鲜

    请将当前用户添加到dialout组,否则会提示打开/dev/ttyUSB0权限不足 sudo usermod -a -G dialout `whoami` dmeg查看驱动安装信息 dmesg | g ...

  7. 黄聪:TortoiseGit(乌龟git)保存用户名密码的方法

    1.在项目文件夹右键--tortoiseGit--设置 2.编辑全局.git/config 3.加上这行代码 里面会有你先前配好的name 和email,只需在下面加一行 [credential] h ...

  8. xftp找不到匹配的outgoing encryption 算法 怎么解决

    alert("找不到匹配的outgoing encryption 算法"); 原因,是ssh登录本地终端缓存了相关的安全确认信息: 远端的ssh服务升级后,其对应的加密算法均作了升 ...

  9. python调用有道翻译api实现翻译

    通过调用有道翻译的api,实现中译英.其他语言译中文 代码: # coding=utf-8 import urllib import urllib2 import json import time i ...

  10. 2.C++语言特性

    一.普遍编程语言的特征 任何常用的编程语言都具备一组公共的语法特征,不同的语言仅在特征的细节上有所区别.所以,要想掌握一门语言,需要理解其语法特征的实现细节是第一步.  最基本的特征包括:       ...