MySQL權限管理

目錄

設置用戶與權限

一個MySQL系統可能有許多用戶。為了安全起見,root用戶通常只用管理目的。對於每個需要使用該系統的用戶,應該為他們創建一個賬號和密碼。這些用戶名和密碼不必與MySQL之外的用戶名稱和密碼(例如,Linux或NT用戶名和密碼)相同。同樣的原則也適合於root用戶。對於系統用戶和MySQL用戶最好使用不同的密碼,這一點對root用戶尤其應該這樣。
為用戶設置密碼不是必須的,但是強烈建議為所有創建的用戶設定密碼。要建立一個Web數據庫,最好為每個網站應用程序建立一個用戶。你可能會問,“為什麼要這麼做呢?”—-答案在於權限。

用戶管理

我們的MySQL的用戶,都被記錄在了mysql數據庫的user表中,如下圖:

在MySQL數據庫中,一個完整的用戶賬號應該包含用戶名登錄主機,也就是說 用戶名@主機名才是一個完整的用戶賬號。

創建用戶基本語法:

CREATE USER 用戶名@主機名 IDENTIFIED BY '密碼';

刪除用戶基本語法:

DROP USER 用戶名@主機名;

修改用戶密碼:

- 修改自己的密碼
set password = password('新密碼');
- 修改別人的密碼(需要有該權限)
set password for 用戶名@主機名 = password('新密碼');

MySQL權限系統介紹

MySQL的最好特性之一是支持複雜的權限系統權限是對特定對象執行特定操作的權力,它與特定用戶相關。其概念非常類似於文件的權限。擋在MySQL中創建一個用戶時,就賦予了該用戶一定的權限,這些權限指定了用戶在本系統中可以做什麼和不可以做什麼。

最少權限原則

最少權限原則可以用來提高任何計算機系統的安全性。它是一個基本的、但又是非常重要的而且容易為我們忽略的原則。該原則包含如下內容:

一個用戶(或一個進程)應該擁有能夠執行分配給他的任務的最低級別的權限。

該原則同樣適用於MySQL,就像它應用於其他地方一樣。例如,要在網站上運行查詢,用戶並不需要root用戶所擁有的所有權限。因此,我們應該創建另一個用戶,這個用戶只有訪問我們剛剛建立的數據庫的必要權限。

創建用戶:GRANT命令

GRANT和REVOKE命令分別用來授予取消MySQL用戶的權限,這些權限分4個級別。它們分別是:

  • 全局
  • 數據庫

GRANT命令用來創建用戶並賦予他們權限。GRANT命令的常見形式是:

GRANT privileges [columns]
ON item
TO user_name [IDENTIFIED BY 'password']
[REQUIRE ssl_options]
[WITH [GRANT OPTION | limit_options]]

普通寫法可以簡略如下:

GRANT 權限列表 ON 數據庫.表 TO 用戶名@主機名 [IDENTIFIED BY '密碼'];

identified by可以省略,也可以寫出
(1)如果寫了,用戶存在,就是修改用戶的密碼
(2)如果寫了,該用戶不存在,就是創建用戶,同時指定密碼

方括號內的子句是可選的。在本語法中,出現了許多佔位符。第一個佔位符是 privileges,應該是由逗號分開的一組權限。MySQL已經有一組已定義的權限。它們在下一節詳細介紹。
佔位符 columns是可選的。可以用它對每一個列指定權限。也可以使用單列的名稱或者逗號分開的一組列的名稱。

佔位符 item是新權限的所應用於的數據庫或表。可以將項目指定為“.”,而將權限應用於所有數據庫。這叫做賦予全局權限。如果沒有使用在特定的數據庫,也可以通過只指定*完成賦予全局權限。更常見的是,以dbname.*的形式指定數據庫中所有的表,以dbname.tablename的形式指定單個表,或者通過指定tablename來指定特定的列。這些分別表示其他3個可以利用的權限:數據庫、表、列。如果在輸入命令的時候正在使用一個數據庫,tablename本身將被解釋成當前數據庫中的一個表。

user_name應該是用戶登錄MySQL所使用的用戶名。請注意,它不必與登錄系統時所使用的用戶名相同。MySQL中的user_name也可以包含一個主機名。可以用它來區分如itbsl(解釋成itbsl@localhost)和itbsl@somewhere.com。這是非常有用的一項能力,因為來自不同域的用戶經常可能使用同一個名字。這也提高了安全性能,因為可以指定用戶從什麼地方連接到本機,甚至可以指定它們在特定的地方可以訪問那些表和數據庫。

password應該是用戶登錄時使用的密碼。常見的密碼選擇規則在這裏都適用。我們後面將更詳細地講述安全問題,但是密碼應該不容易被猜出來。這意味着,密碼不應該是一個字段單詞或與用戶名相同。理想的密碼應該是大、小寫字母和非字母的組合。

REQUIRE子句允許指定用戶是否必須通過加密套接字連接,或者指定其它的SSL選項,關於SSL到MySQL連接的更多信息,請參閱MySQL手冊。

WITH GRANT OPTION選項,如果指定,表示允許指定的用戶向別人授予自己所擁有的權限。
我們也可以指定如下所示的WITH子句:
MAX_QUERIES_PER_HOUR n
或者
MAX_UPDATES_PER_HOUR n
或者
MAX_CONNECTIONS_PER_HOUR n
這些子句可以指定每個用戶每小時執行的查詢、更新和鏈接的數量。在共享的系統上限制單個用戶的負載時,這些子句是非常有用的。
權限存儲在名為mysql的數據庫中的5個系統中。這些表分別是mysql.user、mysql.db、mysql.host、mysql.tables_priv和mysql.columns_priv。作為GRANT命令的替代,可以直接修改這些表。

權限的類型和級別

MySQL中存在3個基本類型的權限:適用於賦予一般用戶的權限、適用於賦予管理員的權限和幾個特定的權限。任何用戶都可以被賦予這3類權限,但是根據最少權限原則,最好嚴格限定只將管理員類型的權限賦予管理員。

我們應該只賦予用戶訪問他們必須使用的數據庫和表的權限。而不應該將訪問mysql的權限賦予不是管理員的人。mysql數據庫是所有用戶名、密碼等信息存儲的地方。

常規用戶的權限直接與特定的SQL命令類型以及用戶是否被允許運行它們相關。下錶所示的是基本用戶權限。“應用於”列下面的對象給出了該類型權限可以授予的對象。

用戶的權限

權限 應用於 描述
SELECT 表、列 允許用戶從表中查詢行(記錄)
INSERT 表、列 允許用戶在表中插入新行
UPDATE 表、列 允許用戶修改現存表裡行中的值
DELETE 允許用戶刪除現存表的行
INDEX 允許用戶創建和拖動特定表索引
ALTER 允許用戶改變現存表的結構,例如,可添加列、重命名列或表、修改列的數據類型
CREATE 數據庫、表 允許用戶創建新數據庫或表。如果在GRANT中指定了一個特定的數據庫或表,它們只能夠創建該數據庫或表,即它們必須首先刪除(drop)它
DROP 數據庫、表 允許用戶拖動(刪除)數據庫或表

從系統的安全性方面考慮,適於常規用戶的權限大多數是相對無害的。ALTER權限通過重命名表可能會影響權限系統,但是大多數用戶需要它。安全性常常是可用性與保險性的折中。遇到ALTER的時候,應當做出自己的選擇,但是通常還是會將這個權限授予用戶。

下面的表給出了適用於管理員用戶使用的權限。
可以將這些權限授予非管理員用戶,這樣做的時候要非常小心。
FILE權限有些不同,它對普通用戶非常有用,因為它可以將數據從文件載入數據庫,從而可以節省許多時間,否則,每次將數據輸入數據庫都需要重新輸入,這很浪費時間。
然而,文件載入可以用來載入MySQL可識別的任何文件,包括屬於其他用戶的數據庫和潛在的密碼文件。授予該權限的時候需要小心,或者自己為用戶載入數據。

管理員權限

權限 描述
CREATE TEMPORARY TABLES 允許管理員在CREATE TABLE語句中使用TEMPORARY關鍵字
FILE 允許將數據從文件讀入表,或從表讀入文件
LOCK TABLES 允許使用LOCK TABLES語句
PROCESS 允許管理員查看屬於所有用戶的服務器進程
RELOAD 允許管理員重新載入授權表、清空授權、主機、日誌和表
REPLICATION CLIENT 允許管理員重新載入授權表、和從機(Slave)上使用SHOW STATUS
REPLICATION SLAVE 允許複製從服務器連接到主服務器
SHOW DATABASES 允許使用SHOW DATABASES語句查看所有數據庫列表。沒有這個權限,用戶只能看到他們能夠看到的數據庫
SHUTDOWN 允許管理員關閉MySQL服務器
SUPER 允許管理員關閉屬於任何用戶的的線程

特別的權限

權限 描述
ALL 授予上面兩個表列表的所有權限。也可以將ALL寫成ALL PRIVILEGES
USAGE 不授予權限。這創建一個用戶並允許他登錄,但是不允許進行任何操作。通常會在以後授予該用戶更多的權限

REVOKE命令

與GRANT相反的命令是REVOKE。它用來從一個用戶收回權限。在語法上與GRANT非常相似:

REVOKE privileges [(columns)]
ON item
FROM user_name

中文翻譯:

REVOKE 權限列表 ON 數據庫.對象 FROM 用戶名@主機名;

如果已經給出了WITH GRANT OPTION子句,可以按如下方式撤銷它(以及所有其他權限):

REVOKE ALL PRIVILEGES, GRANT
FROM user_name

權限立即生效

當我們修改用戶權限之後,如果想不重啟立即生效,需要執行以下flush privileges,這樣能快速刷新權限

FULSH PRIVILEGES;

使用GRANT和REVOKE的例子

要創建一個管理員,可以輸入如下所示的命令:

grant all on * to fred identified by 'mnb123' with grant option;

以上命令授予了用戶名為fred、密碼為mnb123的用戶使用所有數據庫的所有權限,並允許他向其他人授予這些權限。

如果不希望用戶在系統中存在,可以按如下方式撤銷:

revoke all privileges, grant from red;

現在,我們可以按如下方式創建一個沒有任何權限的常規用戶:

grant usage on books.* to sally identified by 'magic123';

在與sally交談后,我們對她需要進行的操作有了一定的了解,因此按如下方式可以授予她適當的權限:

grant select, insert, update, delete, index, alter, create, drop on books.* to sally;

請注意,要完成這些,並不需要指定sally密碼。

如果我們認為sally權限過高,可能會決定按如下方式減少一些權限:

revoke alter, create, drop on books.* from sally;

後來,當她不再需要使用數據庫時,可以按如下方式撤銷所有的權限:

revoke all on books.* from sally;

參考資料:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

dom4j的測試例子和源碼詳解(重點對比和DOM、SAX的區別)

目錄

簡介

dom4j用於創建和解析XML文件,不是純粹的DOMSAX,而是兩者的結合和改進,另外,dom4j支持Xpath來獲取節點。目前,由於其出色的性能和易用性,目前dom4j已經得到廣泛使用,例如SpringHibernate就是使用dom4j來解析xml配置。

注意,dom4j使用Xpath需要額外引入jaxen的包。

DOM、SAX、JAXP和DOM4J

其實,JDK已經帶有可以解析xml的api,如DOMSAXJAXP,但為什麼dom4j會更受歡迎呢?它們有什麼區別呢?在學習dom4j之前,需要先理解下DOMSAX等概念,因為dom4j就是在此基礎上改進而來。

xerces解釋器

先介紹下xerces解釋器,下面介紹的SAXDOMJAXP都只是接口,而xerces解釋器就是它們的具體實現,在com.sun.org.apache.xerces.internal包。xerces被稱為性能最好的解釋器,除了xerces外,還有其他的第三方解釋器,如crimson

SAX

JDK針對解析xml提供的接口,不是具體實現,在org.xml.sax包。SAX基於事件處理,解析過程中根據當前的XML元素類型,調用用戶自己實現的回調方法,如:startDocument();,startElement()。下面以例子說明,通過SAX解析xml並打印節點名:

    /*這裏解釋下四個的接口:
    EntityResolver:需要實現resolveEntity方法。當解析xml需要引入外部數據源時觸發,通過這個方法可以重定向到本地數據源或進行其他操作。
    DTDHandler:需要實現notationDecl和unparsedEntityDecl方法。當解析到"NOTATION", "ENTITY"或 "ENTITIES"時觸發。
    ContentHandler:最常用的一個接口,需要實現startDocument、endDocument、startElement、endElement等方法。當解析到指定元素類型時觸發。
    ErrorHandler:需要實現warning、error或fatalError方法。當解析出現異常時會觸發。
    */
    @Test
    public void test04() throws Exception {
        //DefaultHandler實現了EntityResolver, DTDHandler, ContentHandler, ErrorHandler四個接口     
        DefaultHandler handler = new DefaultHandler() {
            @Override
            //當解析到Element時,觸發打印該節點名
            public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                System.out.println(qName);
            }
        };
        //獲取解析器實例
        XMLReader xr = XMLReaderFactory.createXMLReader();
        //設置處理類
        xr.setContentHandler(handler);
        /*
         * xr.setErrorHandler(handler); 
         * xr.setDTDHandler(handler); 
         * xr.setEntityResolver(handler);
         */
        xr.parse(new InputSource("members.xml"));
    }

因為SAX是基於事件處理的,不需要等到整個xml文件都解析完才執行我們的操作,所以效率較高。但SAX存在一個較大缺點,就是不能隨機訪問節點,因為SAX不會主動地去保存處理過的元素(優點就是內存佔用小、效率高),如果想要保存讀取的元素,開發人員先構建出一個xml樹形結構,再手動往裡面放入元素,非常麻煩(其實dom4j就是通過SAX來構建xml樹)。

DOM

JDK針對解析xml提供的接口,不是具體實現,在org.w3c.dom包。DOM採用了解析方式是一次性加載整個XML文檔,在內存中形成一個樹形的數據結構,開發人員可以隨機地操作元素。見以下例子:

    @SuppressWarnings("restriction")
    @Test
    public void test05() throws Exception {
        //獲得DOMParser對象
        com.sun.org.apache.xerces.internal.parsers.DOMParser domParser = new com.sun.org.apache.xerces.internal.parsers.DOMParser();
        //解析文件
        domParser.parse(new InputSource("members.xml"));
        //獲得Document對象
        Document document=domParser.getDocument();
        // 遍歷節點
        printNodeList(document.getChildNodes());        
    }

通過DOM解析,我們可以獲取任意節點進行操作。但是,DOM有兩個缺點:

  1. 由於一次性加載整個XML文件到內存,當處理較大文件時,容易出現內存溢出。
  2. 節點的操作還是比較繁瑣。

以上兩點,dom4j都進行了相應優化。

JAXP

封裝了SAXDOM兩種接口,它並沒有為JAVA解析XML提供任何新功能,只是對外提供更解耦、簡便操作的API。如下:

DOM解析器

    @Test
    public void test02() throws Exception {
        // 獲得DocumentBuilder對象
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 解析xml文件,獲得Document對象
        Document document = builder.parse("members.xml");
        // 遍歷節點
        printNodeList(document.getChildNodes());
    }

獲取SAX解析器

    @Test
    public void test03() throws Exception {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser saxParser = factory.newSAXParser();
        saxParser.parse("members.xml", new DefaultHandler() {
            @Override
            public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                System.out.println(qName);
            }
        });
    }

其實,JAXP並沒有很大程度提高DOM和SAX的易用性,更多地體現在獲取解析器時實現解耦。完全沒有解決SAXDOM的缺點。

DOM4j

對比過dom4jJAXP就會發現,JAXP本質上還是將SAXDOM當成兩套API來看待,而dom4j就不是,它將SAXDOM結合在一起使用,取長補短,並對原有的api進行了改造,在使用簡便性、性能、面向接口編程等方面都要優於JDK自帶的SAXDOM

以下通過使用例子和源碼分析將作出說明。

項目環境

工程環境

JDK:1.8

maven:3.6.1

IDE:sts4

dom4j:2.1.1

創建項目

項目類型Maven Project,打包方式jar。

引入依賴

注意:dom4j使用XPath,必須引入jaxen的jar包。

<!-- junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!-- dom4j的jar包 -->
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.1</version>
</dependency>
<!-- dom4j使用XPath需要的jar包 -->
<dependency>
    <groupId>jaxen</groupId>
    <artifactId>jaxen</artifactId>
    <version>1.1.6</version>
</dependency>
<!-- 配置BeanUtils的包,這個我自定義工具類用的,如果只是簡單使用dom4j可以不引入 -->
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

使用例子–生成xml文件

本例子將分別使用dom4j和JDK的DOM接口生成xml文件(使用JDK的DOM接口時會使用JAXP的API)。

需求

構建xml樹,添加節點,並生成xml文件。格式如下:

<?xml version="1.0" encoding="UTF-8"?>
<members>
  <students>
    <student name="張三" location="河南" age="18"/>
    <student name="李四" location="新疆" age="26"/>
    <student name="王五" location="北京" age="20"/>
  </students>
  <teachers>
    <teacher name="zzs" location="河南" age="18"/>
    <teacher name="zzf" location="新疆" age="26"/>
    <teacher name="lt" location="北京" age="20"/>
  </teachers>
</members>

生成xml文件–使用w3c的DOM接口

主要步驟

  1. 通過JAXP的API獲得Document對象,這個對象可以看成xml的樹;

  2. 將對象轉化為節點,並添加在Document這棵樹上;

  3. 通過Transformer對象將樹輸出到文件中。

編寫測試類

路徑:test目錄下的cn.zzs.dom4j

注意:因為使用的是w3cDOM接口,所以節點對象導的是org.w3c.dom包,而不是org.dom4j包。

    @Test
    public void test02() throws Exception {
        // 創建工廠對象
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        // 創建DocumentBuilder對象
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        // 創建Document對象
        Document document = documentBuilder.newDocument();

        // 創建根節點
        Element root = document.createElement("members");
        document.appendChild(root);

        // 添加一級節點
        Element studentsElement = (Element)root.appendChild(document.createElement("students"));
        Element teachersElement = (Element)root.appendChild(document.createElement("teachers"));

        // 添加二級節點並設置屬性
        Element studentElement1 = (Element)studentsElement.appendChild(document.createElement("student"));
        studentElement1.setAttribute("name", "張三");
        studentElement1.setAttribute("age", "18");
        studentElement1.setAttribute("location", "河南");
        Element studentElement2 = (Element)studentsElement.appendChild(document.createElement("student"));
        studentElement2.setAttribute("name", "李四");
        studentElement2.setAttribute("age", "26");
        studentElement2.setAttribute("location", "新疆"); 
        Element studentElement3 = (Element)studentsElement.appendChild(document.createElement("student"));
        studentElement3.setAttribute("name", "王五");
        studentElement3.setAttribute("age", "20");
        studentElement3.setAttribute("location", "北京");     
        Element teacherElement1 = (Element)teachersElement.appendChild(document.createElement("teacher"));
        teacherElement1.setAttribute("name", "zzs");
        teacherElement1.setAttribute("age", "18");
        teacherElement1.setAttribute("location", "河南"); 
        Element teacherElement2 = (Element)teachersElement.appendChild(document.createElement("teacher"));
        teacherElement2.setAttribute("name", "zzf");
        teacherElement2.setAttribute("age", "26");
        teacherElement2.setAttribute("location", "新疆");     
        Element teacherElement3 = (Element)teachersElement.appendChild(document.createElement("teacher"));
        teacherElement3.setAttribute("name", "lt");
        teacherElement3.setAttribute("age", "20");
        teacherElement3.setAttribute("location", "北京"); 
            
        // 獲取文件對象
        File file = new File("members.xml");
        if(!file.exists()) {
            file.createNewFile();
        }
        // 獲取Transformer對象
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        // 設置編碼、美化格式
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        // 創建DOMSource對象
        DOMSource domSource = new DOMSource(document);
        // 將document寫出
        transformer.transform(domSource, new StreamResult(new PrintWriter(new FileOutputStream(file))));    
    }   

測試結果

此時,在項目路徑下會生成members.xml,文件內容如下,可以看到,使用w3cDOM接口輸出的內容沒有縮進格式。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<members>
<students>
<student age="18" location="河南" name="張三"/>
<student age="26" location="新疆" name="李四"/>
<student age="20" location="北京" name="王五"/>
</students>
<teachers>
<teacher age="18" location="河南" name="zzs"/>
<teacher age="26" location="新疆" name="zzf"/>
<teacher age="20" location="北京" name="lt"/>
</teachers>
</members>

生成xml文件–使用dom4j的DOM接口

主要步驟

  1. 通過DocumentHelper獲得Document對象,這個對象可以看成xml的樹;

  2. 將對象轉化為節點,並添加在Document這棵樹上;

  3. 通過XMLWriter對象將樹輸出到文件中。

編寫測試類

路徑:test目錄下的cn.zzs.dom4j。通過對比,可以看出,dom4j的API相比JDK的還是要方便很多。

注意:因為使用的是dom4jDOM接口,所以節點對象導的是org.dom4j包,而不是org.w3c.dom包(dom4j一個很大的特點就是改造了w3cDOM接口,極大地簡化了我們對節點的操作)。

    @Test
    public void test02() throws Exception {
        // 創建Document對象
        Document document = DocumentHelper.createDocument();

        // 添加根節點
        Element root = document.addElement("members");

        // 添加一級節點
        Element studentsElement = root.addElement("students");
        Element teachersElement = root.addElement("teachers");

        // 添加二級節點並設置屬性,dom4j改造了w3c的DOM接口,極大地簡化了我們對節點的操作
        studentsElement.addElement("student").addAttribute("name", "張三").addAttribute("age", "18").addAttribute("location", "河南");
        studentsElement.addElement("student").addAttribute("name", "李四").addAttribute("age", "26").addAttribute("location", "新疆");
        studentsElement.addElement("student").addAttribute("name", "王五").addAttribute("age", "20").addAttribute("location", "北京");
        teachersElement.addElement("teacher").addAttribute("name", "zzs").addAttribute("age", "18").addAttribute("location", "河南");
        teachersElement.addElement("teacher").addAttribute("name", "zzf").addAttribute("age", "26").addAttribute("location", "新疆");
        teachersElement.addElement("teacher").addAttribute("name", "lt").addAttribute("age", "20").addAttribute("location", "北京");

        // 獲取文件對象
        File file = new File("members.xml");
        if(!file.exists()) {
            file.createNewFile();
        }
        // 創建輸出格式,不設置的話不會有縮進效果
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");
        // 獲得XMLWriter
        XMLWriter writer = new XMLWriter(new FileWriter(file), format);
        // 打印Document
        writer.write(document);
        // 釋放資源
        writer.close();
    }

測試結果

此時,在項目路徑下會生成members.xml,文件內容如下,可以看出dom4j輸出文件會進行縮進處理,而JDK的不會:

<?xml version="1.0" encoding="UTF-8"?>

<members>
  <students>
    <student name="張三" age="18" location="河南"/>
    <student name="李四" age="26" location="新疆"/>
    <student name="王五" age="20" location="北京"/>
  </students>
  <teachers>
    <teacher name="zzs" age="18" location="河南"/>
    <teacher name="zzf" age="26" location="新疆"/>
    <teacher name="lt" age="20" location="北京"/>
  </teachers>
</members>

使用例子–解析xml文件

需求

  1. 解析xml:解析上面生成的xml文件,將學生和老師節點按以下格式遍歷打印出來(當然也可以再封裝成對象返回給調用者,這裏就不擴展了)。
student:name=張三,location=河南,age=18
student:name=李四,location=新疆,age=26
student:name=王五,location=北京,age=20
teacher:name=zzs,location=河南,age=18
teacher:name=zzf,location=新疆,age=26
teacher:name=lt,location=北京,age=20
  1. dom4j結合XPath查找指定節點

主要步驟

  1. 通過SAXReader對象讀取和解析xml文件,獲得Document對象,即xml樹;

  2. 調用Node的方法遍歷打印xml樹的節點;

  3. 使用XPath查詢指定節點。

測試遍歷節點

考慮篇幅,這裏僅給出一種節點遍歷方式,項目源碼中還給出了其他的幾種。

    /**
     *  測試解析xml
     */
    @Test
    public void test03() throws Exception {
        // 創建指定文件的File對象
        File file = new File("members.xml");
        // 創建SAXReader
        SAXReader saxReader = new SAXReader();
        // 將xml文件讀入成document
        Document document = saxReader.read(file);
        // 獲得根元素
        Element root = document.getRootElement();
        // 遞歸遍歷節點
        list1(root);
    }

    /**
     * 遞歸遍歷節點
     */
    private void list1(Element parent) {
        if(parent == null) {
            return;
        }
        // 遍歷當前節點屬性並輸出
        printAttr(parent);
        // 遞歸打印子節點
        Iterator<Element> iterator2 = parent.elementIterator();
        while(iterator2.hasNext()) {
            Element son = (Element)iterator2.next();
            list1(son);
        }
    }

測試結果如下:

-------第一種遍歷方式:Iterator+遞歸--------
student:name=張三,location=河南,age=18
student:name=李四,location=新疆,age=26
student:name=王五,location=北京,age=20
teacher:name=zzs,location=河南,age=18
teacher:name=zzf,location=新疆,age=26
teacher:name=lt,location=北京,age=20

測試XPath獲取指定節點

    @Test
    public void test04() throws Exception {
        // 創建指定文件的File對象
        File file = new File("members.xml");
        // 創建SAXReader
        SAXReader saxReader = new SAXReader();
        // 將xml文件讀入成document
        Document document = saxReader.read(file);
        // 使用xpath隨機獲取節點
        List<Node> list = document.selectNodes("//members//students/student");
        // List<Node> list = xmlParser.getDocument().selectSingleNode("students");
        // 遍歷節點
        Iterator<Node> iterator = list.iterator();
        while(iterator.hasNext()) {
            Element element = (Element)iterator.next();
            printAttr(element);
        }
    }

測試結果如下:

student:age=18,location=河南,name=張三
student:age=26,location=新疆,name=李四
student:age=20,location=北京,name=王五

XPath語法

利用XPath獲取指定節點,平時用的比較多,這裏列舉下基本語法。

表達式 結果
/members 選取根節點下的所有members子節點
//members 選取根節點下的所有members節點
//students/student[1] 選取students下第一個student子節點
//students/student[last()] 選取students下的最後一個student子節點
//students/student[position()<3] 選取students下前兩個student子節點
//student[@age] 選取所有具有age屬性的student節點
//student[@age=’18’] 選取所有age屬性為18的student節點
//students/* 選取students下的所有節點
//* 選取文檔中所有節點
//student[@*] 選取所有具有屬性的節點
//members/students\ //members/teachers

源碼分析

本文會先介紹dom4j如何將xml元素抽象成具體的對象,再去分析dom4j解析xml文件的過程(注意,閱讀以下內容前需要了解和使用過JDK自帶的DOMSAX)。

dom4j節點的類結構

先來看下一個完整xml的元素組成,可以看出,一個xml文件包含了DocumentElementCommentAttributeDocumentTypeText等等。

DOM的思想就是將xml元素解析為具體對象,並構建樹形數據結構。基於此,w3c提供了xml元素的接口規範,dom4j基本借用了這套規範(如下圖),只是改造了接口的方法,使得我們操作時更加簡便。

SAXReader.read(File file)

通過使用例子可知,我們解析xml文件的入口是SAXReader對象的read方法,入參可以是文件路徑、url、字節流、字符流等,這裏以傳入文件路徑為例。

注意:考慮篇幅和可讀性,以下代碼經過刪減,僅保留所需部分。

    public Document read(File file) throws DocumentException {
        //不管是URI,path,character stream還是byte stream,都會包裝成InputSource對象
        InputSource source = new InputSource(new FileInputStream(file));
        if (this.encoding != null) {
            source.setEncoding(this.encoding);
        }
        
        //下面這段代碼是為了設置systemId,當傳入URI且沒有指定字符流和字節流時,可以通過systemId去連接URL並解析
        //如果一開始傳入了字符流或字節流,這個systemId就是可選的
        String path = file.getAbsolutePath();
        if (path != null) {
            StringBuffer sb = new StringBuffer("file://");
            if (!path.startsWith(File.separator)) {
                sb.append("/");
            }
            path = path.replace('\\', '/');
            sb.append(path);
            source.setSystemId(sb.toString());
        }

        //這裏調用重載方法解析InputSource對象
        return read(source);
    }

SAXReader.read(InputSource in)

看到這個方法的代碼時,使用過JDK的SAX的朋友應該很熟悉,沒錯,dom4j也是採用事件處理的機制來解析xml。其實,只是這裏設置的SAXContentHandler已經實現好了相關的方法,這些方法共同完成一件事情:構建xml樹。明白這一點,應該就能理解dom4j是如何解決SAXDOM的缺點了。

注意:考慮篇幅和可讀性,以下代碼經過刪減,僅保留所需部分。

    public Document read(InputSource in) throws DocumentException {
        // 這裡會調用JAXP接口獲取XMLReader實現類對象
        XMLReader reader = getXMLReader();
        reader = installXMLFilter(reader);
        
        // 下面這些操作,是不是和使用JDK的SAX差不多,dom4j也是使用了事件處理機制。

        // EntityResolver:通過實現resolveEntity方法,當解析xml需要引入外部數據源時觸發,可以重定向到本地數據源或進行其他操作。
        EntityResolver thatEntityResolver = this.entityResolver;
        if (thatEntityResolver == null) {
            thatEntityResolver = createDefaultEntityResolver(in
                    .getSystemId());
            this.entityResolver = thatEntityResolver;
        }
        reader.setEntityResolver(thatEntityResolver);
        
        // 下面的SAXContentHandler繼承了DefaultHandler,即實現了EntityResolver, DTDHandler, ContentHandler, ErrorHandler等接口
        // 其中最重要的是ContentHandler接口,通過實現startDocument、endDocument、startElement、endElement等方法,當dom4j解析xml文件到指定元素類型時,可以觸發我們自定義的方法。
        // 當然,dom4j已經實現了ContentHandler的方法。具體實現的方法內容為:在解析xml時構建xml樹
        SAXContentHandler contentHandler = createContentHandler(reader);
        contentHandler.setEntityResolver(thatEntityResolver);
        contentHandler.setInputSource(in);
        boolean internal = isIncludeInternalDTDDeclarations();
        boolean external = isIncludeExternalDTDDeclarations();
        contentHandler.setIncludeInternalDTDDeclarations(internal);
        contentHandler.setIncludeExternalDTDDeclarations(external);
        contentHandler.setMergeAdjacentText(isMergeAdjacentText());
        contentHandler.setStripWhitespaceText(isStripWhitespaceText());
        contentHandler.setIgnoreComments(isIgnoreComments());
        reader.setContentHandler(contentHandler);

        configureReader(reader, contentHandler);
        
        // 使用事件處理機制解析xml,處理過程會構建xml樹
        reader.parse(in);
        // 返回構建好的xml樹
        return contentHandler.getDocument();
    }

SAXContentHandler

通過上面的分析,可知SAXContentHandlerdom4j構建xml樹的關鍵。這裏看下它的幾個重要方法和屬性。

startDocument()

    // xml樹
    private Document document;

    // 節點棧,棧頂存放當前解析節點(節點解析結束)、或當前解析節點的父節點(節點解析開始)
    private ElementStack elementStack;

    // 節點處理器,可以看成節點開始解析或結束解析的標誌
    private ElementHandler elementHandler;
    
    // 當前解析節點(節點解析結束)、或當前解析節點的父節點(節點解析開始)
    private Element currentElement;
    public void startDocument() throws SAXException {
        document = null;
        currentElement = null;
        
        // 清空節點棧
        elementStack.clear();
        // 初始化節點處理器
        if ((elementHandler != null)
                && (elementHandler instanceof DispatchHandler)) {
            elementStack.setDispatchHandler((DispatchHandler) elementHandler);
        }

        namespaceStack.clear();
        declaredNamespaceIndex = 0;

        if (mergeAdjacentText && (textBuffer == null)) {
            textBuffer = new StringBuffer();
        }

        textInTextBuffer = false;
    }

startElement(String,String,String,Attributes)

    public void startElement(String namespaceURI, String localName,
            String qualifiedName, Attributes attributes) throws SAXException {
        if (mergeAdjacentText && textInTextBuffer) {
            completeCurrentTextNode();
        }

        QName qName = namespaceStack.getQName(namespaceURI, localName,
                qualifiedName);
        // 獲取當前解析節點的父節點
        Branch branch = currentElement;

        if (branch == null) {
            branch = getDocument();
        }
        // 創建當前解析節點
        Element element = branch.addElement(qName);
        addDeclaredNamespaces(element);

        // 添加節點屬性
        addAttributes(element, attributes);
        
        //將當前節點壓入節點棧
        elementStack.pushElement(element);
        currentElement = element;
        entity = null; // fixes bug527062

        //標記節點解析開始
        if (elementHandler != null) {
            elementHandler.onStart(elementStack);
        }
    }

endElement(String, String, String)

    public void endElement(String namespaceURI, String localName, String qName)
            throws SAXException {
        if (mergeAdjacentText && textInTextBuffer) {
            completeCurrentTextNode();
        }
        // 標記節點解析結束
        if ((elementHandler != null) && (currentElement != null)) {
            elementHandler.onEnd(elementStack);
        }
        // 當前解析節點從節點棧中彈出
        elementStack.popElement();
        // 指定為棧頂節點
        currentElement = elementStack.peekElement();
    }

endDocument()

    public void endDocument() throws SAXException {
        namespaceStack.clear();
        // 清空節點棧
        elementStack.clear();
        currentElement = null;
        textBuffer = null;
    }

以上,dom4j的源碼分析基本已經分析完,其他具體細節後續再做補充。

參考以下資料:

本文為原創文章,轉載請附上原文出處鏈接:https://github.com/ZhangZiSheng001/dom4j-demo

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

PHP安全之道學習筆記2:編碼安全指南

編碼安全指南

編程本身就應該是一門藝術,而安全編程更是一種在刀尖上舞蹈的藝術,不僅要小心腳下的鋒利寒刃,更要小心來自網絡黑客或攻擊者的狂轟亂炸。
– by code artist

  • 1.hash比較的缺陷
    經過試驗發現,當Hash值以”0e”開頭且後面都為数字,當和数字進行比較的時候總會被判斷和0相等

例如:

var_dump(‘0e1327544’ == 0); // bool(true)

當密碼被md5計算后,可能會以”0e”開頭,下面這個例子可以繞過密碼驗證。
經過我的驗證PHP 7.1.x后沒有這種問題。

<?php
    $password_from_db = "0e23434";
    $password = "2323"; // 隨意的一個密碼。來自$_POST,即表單提交
    if ($password_from_db == md5($password)) {
        echo "login success!";
    } else {
        echo "login fails";
    }

更安全的hash比較:
可以使用內置函數hash_equals()來比較hash值。(PHP版本必須是5.6及其以上)

 if (hash_equals($password_from_db, md5($password)) {
     .....// other logic
 }
  • 2.bool比較的缺陷

json_decode和unserialize函數可能將部分結構解析成bool值,造成一些比較上的缺陷。

先舉例json_decode的案例:

<?php
$str = '{"user":true, "pass": true}';

$data = json_decode($str, true);

if ($data['user'] == 'root' && $data['pass'] == 'pass') {
    echo "login success\r\n";
} else {
    echo "login fails\r\n";
}

執行結果為:login success
這樣利用bool比較的漏洞就繞過了登錄或者授權驗證。

unserialize過程相逆,結果類似,也會出現安全問題。

正確的做法還是使用”===”來進行比較,這不光是php,包括一些其他腳本語言或者靜態語言,都請嚴謹地使用全等於符號進行比較。

  • 3.數值比較

PHP雖然是弱類型語言,但是數據類型也有數值範圍。對於整型而言,最大值為PHP_INT_MAX(即9223372036854775807)
攻擊者可以利用最大值越界,繞過一些驗證,如登錄、賬號充值等等。

舉例:

$a = 9223372036854775807;
$b = 9223372036854775827;
var_dump($a === $b); // bool(true)
var_dump($a % 100); // int(0)

由此,可見全等號(===)也不是萬能的,具體場景下要更小心。經驗證,PHP7.1.x后不會出現該問題,5.x的可能出現。

在實際業務邏輯裏面一定要注意判斷最大值問題,避免越界帶來的問題。

當使用超長浮點數變量的時候,PHP也會出錯。

<?php
$uid = 0.999999999999999999;
if ($uid == "1") {
    echo "search uid is 1 for data\r\n"; // 這裏PHP將$uid約等於1了,進入該判斷條件里的邏輯
}

同理,2.999999999999也會被當成3,這就是超越浮點數精度造成的偏差。

解決辦法有很多,最簡單的就是用is_int()函數進行判斷,如果不是整型,則報錯或做錯誤處理。

  • 4.switch缺陷

當用case判斷数字的時候,switch會把參數轉換成int類型進行計算,代碼如下:

<?php
$num = "1FreePHP";
switch ($num) {
    case 0: echo "nothing";
        break;
    case 1: echo "1 hacker here!";
        break;
    case 2: echo "2 hackers here";
        break;
    default:
        echo "confused";
}

最後輸出:1 hacker here!

所以,請使用is_numeric()函數進行判斷,保證數據類型如預期的一致。

  • 5.數組缺陷。
    in_array()和array_search()函數在沒有使用嚴格模式的情況下會用鬆散比較,可能造成一些錯誤。
    例如:
<?php
$arr = [0, 2, 3, "4"];
var_dump(in_array('freephp', $arr)); // true
var_dump(array_search('freephp', $arr)); // 0: 下標
var_dump(in_array('2freephp', $arr)); // true
var_dump(array_search('3freephp', $arr)); // 2: 下標

總的來說,PHP工程師對於這種弱類型語言的使用上要更加小心,雖然平時寫起業務來“短平快”,但安全編程也不要忘記,能用上hint的高版本PHP就進行標註清楚入參、出參,讓PHP代碼更加健壯。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

Spring Security之多次登錄失敗后賬戶鎖定功能的實現

在上一次寫的文章中,為大家說到了如何動態的從數據庫加載用戶、角色、權限信息,從而實現登錄驗證及授權。在實際的開發過程中,我們通常會有這樣的一個需求:當用戶多次登錄失敗的時候,我們應該將賬戶鎖定,等待一定的時間之後才能再次進行登錄操作。

一、基礎知識回顧

要實現多次登錄失敗賬戶鎖定的功能,我們需要先回顧一下基礎知識:

  • Spring Security 不需要我們自己實現登錄驗證邏輯,而是將用戶、角色、權限信息以實現UserDetails和UserDetailsService接口的方式告知Spring Security。具體的登錄驗證邏輯Spring Security 會幫助我們實現。
  • UserDetails接口中有一個方法叫做isAccountNonLocked()用於判斷賬號是否被鎖定,也就是說我們應該通過該方法對應的set方法setAccountNonLocked(false)告知Spring Security該登錄賬戶被鎖定。
  • 那麼應該在哪裡判斷賬號登錄失敗的次數並執行鎖定機制呢?當然是我們之前文章給大家介紹的《自定義登錄成功及失敗結果處理》的AuthenticationFailureHandler。

建議您先閱讀本文,如果您對本文的實現過程感到迷惑,建議您再翻看本號之前的相關內容。

二、實現多次登錄失敗鎖定的原理

一般來說實現這個需求,我們需要針對每一個用戶記錄登錄失敗的次數nLock和鎖定賬戶的到期時間releaseTime。具體你是把這2個信息存儲在mysql、還是文件中、還是redis中等等,完全取決於你對你所處的應用架構適用性的判斷。具體的實現邏輯無非就是:

  • 登陸失敗之後,從存儲中將nLock取出來加1。
  • 如果nLock大於登陸失敗閾值(比如3次),則將nLock=0,然後設置releaseTime為當前時間加上鎖定周期。通過setAccountNonLocked(false)告知Spring Security該登錄賬戶被鎖定。
  • 如果nLock小於等於1,則將nLock再次存起來。
  • 在一個合適的時機,將鎖定狀態重置為setAccountNonLocked(true)。

這是一種非常典型的實現方式,筆者向大家介紹一款非常有用的開源軟件叫做:ratelimitj。這個軟件的功能主要是為API訪問進行限流,也就是說可以通過制定規則限制API接口的訪問頻率。那恰好登錄驗證接口也是API的一種啊,我們正好也需要限制它在一定的時間內的訪問次數。

三、具體實現

首先需要將ratelimitj通過maven坐標引入到我們的應用裏面來。我們使用的是內存存儲的版本,還有redis存儲的版本,大家可以根據自己的應用情況選用。

        <dependency>
            <groupId>es.moki.ratelimitj</groupId>
            <artifactId>ratelimitj-inmemory</artifactId>
            <version>0.4.1</version>
        </dependency>

之後通過繼承SimpleUrlAuthenticationFailureHandler ,實現onAuthenticationFailure方法。該實現是針對登錄失敗的結果的處理,在我們之前的文章中已經講過。

@Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Autowired
    UserDetailsManager userDetailsManager;

    //規則定義:1小時之內5次機會,就觸發限流行為
    Set<RequestLimitRule> rules = 
            Collections.singleton(RequestLimitRule.of(1 * 60, TimeUnit.MINUTES,5)); 
    RequestRateLimiter limiter = new InMemorySlidingWindowRequestRateLimiter(rules);


    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response, 
                                        AuthenticationException exception) 
                                        throws IOException, ServletException {

         String userId = //從request或request.getSession中獲取登錄用戶名
         //計數器加1,並判斷該用戶是否已經到了觸發了鎖定規則
         boolean reachLimit = limiter.overLimitWhenIncremented(userId);

        if(reachLimit){ //如果觸發了鎖定規則,通過UserDetails告知Spring Security鎖定賬戶
               user.setAccountNonLocked(false);
               userDetailsManager.updateUser(user);
               SysUser user = (SysUser) userDetailsManager.loadUserByUsername(userId);
        }
        

        
        //此處省略通過response做json或html響應
    }
}
  • 核心實現注意看代碼中的註釋
  • 代碼中的SysUser為UserDetails的實現類,如果不知道如何實現請參考本號之前的文章
  • userDetailsManager被用於管理UserDetails信息,通過改變UserDetails改變Spring Security驗證行為。

四、重置鎖定狀態的時機

user.setAccountNonLocked(true);

重置鎖定狀態很簡單,就是上面的代碼。但是更重要的是如何選擇重置鎖定狀態的時機。筆者能想到幾種方案如下

  • 下一次登陸的時候,自定義過濾器,加在Spring Boot過濾器鏈最前端做鎖定狀態重置的判斷。
  • 當登錄賬戶被鎖定之後,之後用戶的每一次登錄都會拋出LockedException。我們完全可以通過Spring Boot的全局異常捕獲機制,在其中捕獲LockedException,並做鎖定狀態的判斷及重置行為。
  • 寫一個Spring 的定時器輪詢,當然這是最差的方案。

    期待您的關注

  • 向您推薦博主的系列文檔:
  • 本文轉載註明出處(必須帶連接,不能只轉文字):。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※高價收購3C產品,價格不怕你比較

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

3c收購,鏡頭 收購有可能以全新價回收嗎?

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

Head First設計模式——適配器和外觀模式,Head First設計模式——裝飾者模式

前言:為什麼要一次講解這兩個模式,說點騷話:因為比較簡單(*^_^*),其實是他們兩個有相似和有時候我們容易搞混概念。

講到這兩個設計模式與另外一個“裝飾者模式”也有相似,他們三個按照結構模式分類都屬於“結構性模式”,所有我們接下來就來看什麼是適配器模式和外觀模式。

另外裝飾模式可以看我的另一篇博文→

一、適配器模式

適配器對應到我們現實生活中的例子,最典型的就是插頭接口適配器,比如我們買的有些港版手機充電頭是圓形三角插頭,而大陸的三角電源插板插不進去港版的插頭。

這時候我們就會在某寶上買個轉接頭轉換一下,而這個轉接頭就是適配器,用它來適配港版手機充電頭讓他能夠插入到我們的電源插板裏面。

在設計模式中這個適配器是什麼,用程序如何表現,先讓我舉個栗子:我們有一隻鴨子,一隻雞,我們如何通過適配器轉換鴨和雞。

鴨子有很多種,我們定義一個鴨子的接口,然後以綠頭鴨為例。關於這個綠頭鴨在策略模式也有用到,可以看看我另一篇綠頭鴨如何攪動策略模式→

    public  interface Duck
    {
        //叫
        public void Quack();
        //飛
        public void Fly();
    }

    public class GreenDuck : Duck
    {
        public void Fly()
        {
            Console.WriteLine("綠頭鴨,飛");
        }

        public void Quack()
        {
            Console.WriteLine("綠頭鴨,呱呱叫");
        }
    }

  同樣我們定義一個雞的接口,和一隻母雞的類

    public  interface Chicken
    {
        //叫
        public void Gobble();
        //飛
        public void Fly();
    }

    public class Hen : Chicken
    {
       
        public void Gobble()
        {
            Console.WriteLine("母雞,咯咯叫");
        }

        public void Fly()
        {
            Console.WriteLine("母雞,飛");
        }

    }

  鴨子和母雞的叫聲不一樣,現在我們讓母雞來冒充鴨子,利用適配器模式如何做。 直接看代碼吧

    /// <summary>
    /// 母雞適配器
    /// 適配母雞讓它變成鴨子
    /// </summary>
    public class HenAdapter : Duck
    {
        Chicken chicken;
        public HenAdapter(Chicken chicken)
        {
            this.chicken = chicken;
        }
        public void Quack()
        {
            //調用母雞咯咯叫
            chicken.Gobble();
        }

        public void Fly()
        {
            //調用母雞飛
            chicken.Fly();
        }

    }

  測試母雞適配器

如上我們使用母雞適配器將母雞適配成了鴨子,鴨子也可以用適配器將鴨子適配成母雞,適配器模式定義:

適配器模式:將一個類的接口,裝換成客戶期望的另一個接口。適配器讓原本接口不兼容的類可以合作無間。

與適配器看起來相似的裝飾者模式是包裝對象的行為或責任,裝飾者被包裝后可能會繼續被包裝,他們不裝換接口,而適配器則一定會進行接口的轉換。

適配的工作是將一個接口轉換成另外一個接口,雖然大多數適配器採取的例子都是讓一個適配器包裝一個被適配者,但是有時候我們需要讓一個適配器包裝多個被適配者。

而這實際又涉及到另外一個模式,就是外觀模式,我們常常將適配器模式和外觀模式混為一談,那接着就來講解外觀模式。

二、外觀模式

外觀模式以家庭影院為例,家庭影院有許多組件構成,比如:显示屏、DVD、音響、燈光等等。

當我們要看電影的時候要打開显示屏,打開DVD,打開音響,關閉燈光等一系列動作,將這些動作寫成類方法的調用

            Screen screen = new Screen();
            DVD dvd = new DVD();
            SoundEngineer sound = new SoundEngineer();
            Light light = new Light();

            screen.Down();
            dvd.PlayDVD();
            sound.TurnOn();
            light.TurnOff();

可以看到每次我們要使用就要調用一篇這些方法,如果要關閉呢,我們也需要調用一篇。而我們正需要的就是一個外觀:通過實現一個提供更合理的接口的外觀類。

還是看代碼吧

 public class HomeThreaterFacade
    {
        Screen screen;
        DVD dvd;
        SoundEngineer sound;
        Light light;

        public HomeThreaterFacade(Screen screen, DVD dvd, SoundEngineer sound, Light light)
        {
            this.screen = screen;
            this.dvd = dvd;
            this.sound = sound;
            this.light = light;
        }

        public void WatchMovie()
        {
            Console.WriteLine("開始播放電影......");
            screen.Down();
            dvd.PlayDVD();
            sound.TurnOn();
            light.TurnOff();
        }
    }

由於其他類比較簡單就是一個打印輸出,我就不列出來了,還有關閉方法同理也很簡單就實現了。

還是測試一下效果:

外觀模式定義

外觀模式:提供了一個統一的接口,用來訪問子系統中的一群接口。外觀定義了一個高層接口,讓子系統更容易使用。

外觀模式遵循了一個設計原則

最少知識原則:之和你的密友談話。

這個原則希望我們在設計中,不要讓太多的類耦合在一起,免得修改系統中一部分,會影響其他部分。而外觀模式讓用戶不用關心全部子系統組件,讓客戶變得簡單有彈性。我們可以在不影響客戶的情況下升級外觀模式里的組件,而客戶只有一個朋友,也就是外觀模式。

三、適配器模式與外觀模式區別

從上面例子我們也許會覺得適配器和外觀模式之間的差異在於:適配器包裝一個類,而外觀可以代表許多類

但是實際它們的本質和作用並不是在於包裝多少類,適配器模式將一個或多個接口變成客戶期望的一個接口,我們一般適配一個類,但是特殊需求也可以適配多個類來提供一個接口。類是地,一個外觀也可以只爭對一個複雜接口的類提供簡化接口。兩中模式的差異在於他們的意圖。適配器模式意圖是將接口裝換成不同接口,外觀的意圖是簡化接口。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

中國動力電池技術突破,2025 年電動車成本效益比料勝燃油車

 

21 世紀經濟報導,中國電動汽車百人會理事長陳清泰表示,過去一年,中國電動汽車產業正在向高品質發輾轉型,發展形勢良好;而電動汽車再往前發展要跨越一個臨界點,就是電動車的成本效益比達到和超過燃油車,他預期這個臨界點會可能會在 2025 年前後出現。   中國 2017 年新能源汽車銷量目標 70 萬輛,去年 1 到 11 月累計銷售 60.9 萬輛、年增 51.4%。專家預測,中國去年新能源汽車總銷量可能超過 80 萬輛。中國汽車工業協會秘書長助理許海東日前表示,中國去年新能源汽車 70 萬輛銷量目標應可達成,2018 年新能源車銷量增速仍可保持 40% 至 50%,預期銷量將超過 100 萬輛。   陳清泰指出,電動汽車再往前發展要跨越一個臨界點,就是電動車的成本效益比達到和超過燃油車,如果跨過了這個門檻,電動車就可依託市場力量自主發展了,目前仍需靠政策、靠政府補貼。他亦預期,當財政補貼完全取消後,雙積分政策作為替代政策,新能源汽車積分比例將在 2020 年 12% 的基礎上繼續上調。   對於上述臨界點會出現在什麼時候?陳清泰的判斷是,大約在 2025 年前後。對此,他建議中國車企要在以下幾個方面做好準備,首先是在財政補貼退坡之後,做好可持續發展的保障工作,另外電動車自身要透過輕量化、節能化提高成本效益比;其次,產品技術要雙線作戰,其中一條戰線是完善汽車的行駛功能,另一條戰線則是將車聯網和共享化應用到新能源汽車上;第三,自動駕駛是爭奪未來的一個重點;第四,在有限的時間要加緊做品牌建設。   中國電動汽車百人會執行副理事長歐陽明高表示,2017 年中國動力電池技術已取得實質性進展,動力電池系統能量密度已達到 150 瓦時/公斤甚至以上,鋰離子動力電池單體比能量有望於 2020 年前實現 300 瓦時/公斤目標。   他進一步指出,2025 年,具備一般成本效益比的純電動轎車合理的里程是 300 到 400 公里;但 2030 年,最大的技術突破將體現在電解質上,固態電池會規模產業化,電池單體比能量可望觸及 500 瓦時/公斤;2030 年常規的成本效益比車型,續航里程應該可達到 500 公里以上。   (本文內容由授權使用。首圖來源:)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

台積電與格芯達成訴訟和解 現在及未來十年專利交互授權

  作者:蘇亞

  【TechWeb】10 月 29 日,台積電宣布與格芯(GlobalFoundries)達成專利訴訟和解,雙方同意撤回所有法律訴訟,並同意對現有及未來十年的半導體技術專利,達成全球專利交互授權協議。

  根據台積電公告,他們將駁回它們之間以及涉及其任何客戶的所有訴訟,兩家公司已經同意相互之間廣泛的專利壽命交叉許可,這些交叉許可適用於彼此在全球範圍內現有的半導體專利以及在未來十年內將要申請的專利,該決議保證了台積電和 GF 的運營自由,並確保各自的客戶將繼續獲得每個代工廠的完整技術和服務。

  “我們很高興很快達成這一承認我們各自知識產權實力的解決方案。今天的公告使我們兩家公司都能專註於創新並更好地為全球客戶提供服務。” GF 首席執行官 Thomas Caulfield 說。“ GF 與台積電之間的這項協議確保了 GF 的增長能力,並且是當今全球經濟核心的整個半導體行業的勝利。”

  半導體行業一直競爭激烈,驅使參与者追求創新,豐富了世界各地數百萬人的生活。台積電已投入數百億美元用於創新,以達到今天的領先地位。”台積電總顧問 Sylvia Fang 說。“這項決議是一項积極的進展,將使我們始終專註於滿足客戶對將不斷帶來創新的技術的需求,這將使整個半導體行業蓬勃發展和繁榮。”

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

高價3c回收,收購空拍機,收購鏡頭,收購 MACBOOK-更多收購平台討論專區

3c收購,鏡頭 收購有可能以全新價回收嗎?

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?

雲孤島“混而不合” 戴爾推出一致性混合雲平台

  過去的幾十年,IT 基礎架構經歷了從大型機到 PC 機,從數據中心到移動互聯網、IoT 的變化,看起來我們總是在重複集中、分佈的循環演進過程,但戴爾公司認為,每一次的改變都不是簡單的重複,而是在原有基礎上的提升和發展。

  雲計算時代,企業 IT 戰略重點在不斷地轉移,企業看待雲計算的角度也不再以資源視角,而是更多從應用或者工作負載視角。

  在構建企業的核心數據架構以及釋放數據潛能的過程當中,IT 和 OT 的戰略重點在快速融合,創新理念到業務落地,數據管理到業務安全,全球展望到全球服務,所有這一切都在不斷推動每一家企業去實施新 IT 戰略——從資源平台的打造到創新平台的演進。

  為何需要戴爾科技雲平台?

  戴爾科技集團企業技術戰略架構師總監許良謀表示,隨着雲計算行業發展,一个中大型企業內部存在3-5 朵雲是非常正常的,假如企業客戶把關鍵應用放到公有雲,一定會考慮同時放在兩朵公有雲,再加上生產線私有雲,支持創新業務的測試雲、開發雲等,企業內部有3-5 朵相同或者不同的雲是正常現象。

  以戴爾集團本身為例,戴爾內部有超過 1200 個應用,涵蓋從售前、售後、供應鏈等各個環節,一朵雲解決想要所有的問題是不可能的,所以企業尋求多雲戰略是實際需求推動。

  但多雲同樣帶來困擾,戴爾科技集團全球資深副總裁、大中華區企業解決方案總經理曹志平解釋道,每家企業的應用和業務流程在不同的時間階段,需要不同的部署方式;每一個雲平台的技術特點不同,決定了它對某些工作負載支持效果好一點,對某些工作支持差一點;此外,多雲形態下的雲計算資源計費,很多用戶發現產生了跟預期偏差較大的情況,尤其是對於網絡資源消耗帶來的費用增長,企業用戶對這方面的估計不足。

  企業難以應付部署在各種雲平台上的工作負載,數據孤島變成了新的雲孤島,數據割裂導致企業在做整體業務規劃、整體戰略實施時徒增困難。

  雷鋒網了解到,今年年初,戴爾科技集團決定整合集團內部所有與雲計算相關的技術和資源,打造一套能夠充分利用各種雲平台技術的落地方案,尤其是 VMware 和戴爾易安信,兩者解決方案深度融合,推出了一個一致性的雲策略——戴爾科技雲平台。

  多雲至簡

  “戴爾科技集團的雲是一朵徹底的混合雲,基於 VMware Cloud Foundation 軟件定義數據中心,開發者無需注意應用所在位置。可以在最合適的位置來運行工作負載”,在一年一度的戴爾科技峰會主論壇演講中,戴爾公司創始人邁克爾·戴爾也着力推廣一致性混合雲。

  多雲至簡是戴爾混合雲解決方案的關鍵詞,看起來“多”和“簡”好像是兩個錯位的反義詞,多雲是企業擁抱雲架構演進的必然過程,至簡表示企業希望混合雲架構帶來價值,卻並不希望帶來額外的複雜。

  從技術視角看,戴爾混合雲解決方案是兩種能力的融合,其一是虛擬化技術,使企業 IT 能夠大規模整合資源,並將資源池化;其二是軟件定義數據中心技術,是把整個數據中心軟件化和敏捷化的關鍵技術。

  “對於一個企業來說,整個平台從資源平台轉型到創新平台,真正的用戶是創新者,創新者根本不用在乎底層平台是什麼技術實現,只要有創新的想法就可以迅速獲取創新所需要的平台”,VMware 大中華區技術部高級總監李剛說道。

  從戴爾混合雲解決方案視角看,不管是基於戴爾科技雲平台結合 VCF 和 VxRail 的方案,還是服務器+存儲三層架構的方案打造 VCF 的平台,戴爾的工作在於推動 VCF 與所有公有雲平台廠商做聯合,即在其他雲廠商的公有雲內部開闢 VMware 雲,這樣不論是數據、技術還是管理都能保持一致性。

  對於已經使用戴爾 EMC 或者 VMware 的企業用戶,保持技術的延續性,使用戴爾混合雲方案是自然的過程,而對其他非戴爾客戶,出於多雲的需求,嘗鮮使用戴爾混合雲方案就有可能轉化為戴爾企業級解決方案的用戶。

  而對戴爾,VMware 雲融於公有雲工作量倍增,戴爾要和不同公有雲平台兼容適配,還涉及到不同私有雲平台,客戶端至簡的代價必然是背後的供應商額外工作量,以戴爾的體量做一致性混合雲平台,也是建立技術和商業壁壘的策略。

  戴爾混合雲的差異化

  同推混合雲概念,戴爾的混合雲和其他雲廠商所強調的混合雲是否有不同?

  雷鋒網了解到,市場出現的所謂混合雲主要分為兩個方向:一種是公有雲廠商做私有雲方案,一家廠商的公私方案整合為混合雲方案;另一種是私有雲廠商開發的產品融合部分公有雲廠商產品,加以整合併推出的混合雲。

  許良謀用中西餐做形象比喻,一些混合雲方案只提供一個菜系,比如山東菜,一些混合雲方案中西餐混着吃,這些混合雲方案都相對封閉,戴爾則希望打造一個“唐人街”——不管是在國內還是國外,家裡還是外出,直接在“唐人街”就可以吃到喜歡的中餐。

  戴爾混合雲是業界支持公有雲平台數量最多的產品,VMware 目前也是全球私有雲環境市場佔有率最高的私有雲方案,這意味着企業客戶可以直接利用戴爾混合雲方案做利舊,保護歷史投資。

  技術支持能力和平台支持數量,以及整體市場佔有率,再加上技術優化細節,成為戴爾的差異化優勢。

  “真正讓用戶能夠擺脫雲孤島,所有的數據、所有的應用和工作負載,無論它的形式上是某個私有雲或者某個公有雲上,實質是在同一個管理平台,這就是戴爾科技集團雲平台的優勢、價值和意義”,戴爾科技集團全球資深副總裁、大中華區企業解決方案總經理曹志平總結道。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

高價3c回收,收購空拍機,收購鏡頭,收購 MACBOOK-更多收購平台討論專區

3c收購,鏡頭 收購有可能以全新價回收嗎?

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?

格力“招親”落定人選 高瓴勝出

  作者:木子松

  【TechWeb】10 月 28 日晚間消息,格力電器公告,格力集團函告公司,經評審委員會對參与本次公開徵集的兩家意向受讓方進行綜合評審,確定珠海明駿投資合夥企業(有限合夥)為最終受讓方。

  公告內容显示,珠海明駿投資成立於 2017 年 5 月,背後實際操盤者是高瓴資本,旗下不同的產品分別出資組成的註冊於橫琴新區的新基金。

  以下是公告全文:

  關於控股股東擬協議轉讓公司部分股份公開徵集受讓方的結果公告

  本公司及董事會全體成員保證信息披露的內容真實、準確、完整,沒有虛假記載、誤導性陳述或重大遺漏。

  珠海格力電器股份有限公司(以下簡稱“格力電器”或“公司”)分別於 2019 年 4 月 1 日、2019 年 4 月 9 日、2019 年 8 月 13 日、2019 年 9 月 3 日披露了《重大事項停牌公告》(公告編號:2019-015)、《關於控股股東擬通過公開徵集受讓方的方式協議轉讓公司部分股權暨復牌的提示性公告》(公告編號: 2019-016)、《關於控股股東擬協議轉讓公司部分股份公開徵集受讓方的公告》(公告編號:2019-052)(以下簡稱“《公開徵集受讓方公告》”)、《關於控股股東擬協議轉讓公司部分股份公開徵集受讓方的進展公告》(公告編號: 2019-057)。

  2019 年 10 月 28 日,格力集團函告公司,經評審委員會對參与本次公開徵集的兩家意向受讓方進行綜合評審,確定珠海明駿投資合夥企業(有限合夥)(以下簡稱“珠海明駿”)為最終受讓方。

  一、最終受讓方的基本情況 

  二、最終受讓方維護管理層穩定的措施及合作方案

  根據《公開徵集受讓方公告》披露的公開徵集方案,意向受讓方應有利於提升上市公司質量,維護公司持續健康發展,且本次受讓申請材料中的受讓意向書要求意向受讓方提出維護管理層穩定的具體措施及未來與管理層合作的具體方案。

  基於上述要求,參与本次公開徵集的兩家意向受讓方珠海明駿投資合夥企業(有限合夥),以及格物厚德股權投資(珠海)合夥企業(有限合夥)與 GENESIS FINANCIAL INVESTMENT COMPANY LIMITED 組成的聯合體均在向格力集團提交的受讓申請材料中提出了維護管理層穩定的具體措施及合作方案。

  鑒於本次公開徵集的最終受讓方確定為珠海明駿,且珠海明駿已通過受讓意向書書面邀請的形式向格力電器管理層提出合作邀請,若格力電器管理層最終接受珠海明駿的邀請,並依據受讓意向書提出的邀請方案展開合作,雙方需在珠海明駿與格力集團簽署本次公開徵集的《股份轉讓協議》前對具體合作方案予以明確並對外披露。 前述合作方案為最終受讓方的單方面邀請,格力電器管理層是否接受最終受讓方的邀請以及雙方最終能否就合作方案達成一致尚存在不確定性,敬請廣大投資者注意投資風險。

  三、公開徵集後續安排

  根據《公開徵集受讓方公告》披露的公開徵集方案,意向受讓方應自被確定為最終受讓方之日(即本公告發布之日)起 10 個工作日內與格力集團簽訂《股份轉讓協議》,所簽署的《股份轉讓協議》仍須經國有資產監督管理機構及其他有權政府部門批准後方能生效,是否能夠獲得國有資產監督管理機構及其他有權政府部門的批准以及股份轉讓是否能夠最終完成尚存在不確定性,敬請廣大投資者注意投資風險。

  公司將與格力集團、格力電器管理層保持密切聯繫並根據相關事項進展情況,嚴格按照相關法律、法規的規定及時履行信息披露義務。公司指定的信息披露媒體為《證券日報》、《證券時報》、《上海證券報》、《中國證券報》和巨潮資訊網(www.cninfo.com.cn),敬請廣大投資者謹慎決策,注意投資風險。

  特此公告。

  珠海格力電器股份有限公司

  董事會

  二〇一九年十月二十九日

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

高價3c回收,收購空拍機,收購鏡頭,收購 MACBOOK-更多收購平台討論專區

3c收購,鏡頭 收購有可能以全新價回收嗎?

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?

NASA發布太陽“怪異”照 看似最佳的萬聖節南瓜燈

  據外媒 BGR 報道,隨着萬聖節即將到來,美國宇航局(NASA)在其 Facebook 頁面上分享了一張特別怪異的太陽圖像,看起來像人們熟悉的萬聖節南瓜燈。該圖像是由目前正在繞地球運行的 NASA 太陽動力學天文台(Solar Dynamics Observatory)捕獲的。

  該天文台能夠捕捉到我們最近的恆星的一些令人難以置信的照片,但是這張特殊的圖像在距萬聖節只有幾天時間時發布具有特殊的意義。

  通過其強大的傳感器套件,該天文台可以以我們眼睛無法看到的方式觀察太陽。該圖像使用紫外線來揭示恆星表面上的活動區域,在這種情況下,它看起來就像是一張“咧嘴大笑的臉”。

  NASA 解釋道:

該圖像中的活動區域顯得更亮,因為這些區域會發出更多的光和能量。它們是在太陽大氣層電暈中盤旋的強烈而複雜的磁場的標誌。該圖像將兩組分別位於 171 和 193Ångströms 的極端紫外線波長混合在一起,通常以金色和黃色着色,以產生特別類似於萬聖節的外觀。

  這張照片最初是在 2014 年分享的,但它是 NASA 的最愛之一,並且自那以後的幾年裡,它多次出現。同時,太陽動力學天文台仍在努力工作,將太陽的新影像傳回地球,科學家將繼續研究和監測恆星。

  該航天器於 2010 年 2 月發射,任務期限長達十年。其已在太空中已經捕獲了超過 3.5 億張圖像,並將在可預見的未來繼續這樣做。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

高價3c回收,收購空拍機,收購鏡頭,收購 MACBOOK-更多收購平台討論專區

3c收購,鏡頭 收購有可能以全新價回收嗎?

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?