開發框架-Shiro-30分鐘學會如何使用Shiro
一、架構
要學習如何使用Shiro必須先從它的架構談起,作為一款安全框架Shiro的設計相當精妙。Shiro的應用不依賴任何容器,它也可以在JavaSE下使用。但是最常用的環境還是JavaEE。下面以使用者登入為例:
(1)使用使用者的登入資訊建立令牌
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token可以理解為使用者令牌,登入的過程被抽象為Shiro驗證令牌是否具有合法身份以及相關許可權。
(2)執行登陸動作
Shiro的核心部分是SecurityManager,它負責安全認證與授權。Shiro本身已經實現了所有的細節,使用者可以完全把它當做一個黑盒來使用。SecurityUtils物件,本質上就是一個工廠類似Spring中的ApplicationContext。Subject是初學者比較難於理解的物件,很多人以為它可以等同於User,其實不然。Subject中文翻譯:專案,而正確的理解也恰恰如此。它是你目前所設計的需要通過Shiro保護的專案的一個抽象概念。通過令牌(token)與專案(subject)的登陸(login)關係,Shiro保證了專案整體的安全。SecurityUtils.setSecurityManager(securityManager); // 注入SecurityManager Subject subject = SecurityUtils.getSubject(); // 獲取Subject單例物件 subject.login(token); // 登陸
(3)判斷使用者
Shiro本身無法知道所持有令牌的使用者是否合法,因為除了專案的設計人員恐怕誰都無法得知。因此Realm是整個框架中為數不多的必須由設計者自行實現的模組,當然Shiro提供了多種實現的途徑,本文只介紹最常見也最重要的一種實現方式——資料庫查詢。
(4)兩個重要的英文
我在學習Shiro的過程中遇到的第一個障礙就是這兩個物件的英文名稱:AuthorizationInfo,AuthenticationInfo。不用懷疑自己的眼睛,它們確實長的很像,不但長的像,就連意思都十分近似。
在解釋它們前首先必須要描述一下Shiro對於安全使用者的界定:和大多數作業系統一樣。使用者具有角色和許可權兩種最基本的屬性。例如,我的Windows登陸名稱是learnhow,它的角色是administrator,而administrator具有所有系統許可權。這樣learnhow自然就擁有了所有系統許可權。那麼其他人需要登入我的電腦怎麼辦,我可以開放一個guest角色,任何無法提供正確使用者名稱與密碼的未知使用者都可以通過guest來登入,而系統對於guest角色開放的許可權極其有限。
同理,Shiro對使用者的約束也採用了這樣的方式。AuthenticationInfo代表了使用者的角色資訊集合,AuthorizationInfo代表了角色的許可權資訊集合。如此一來,當設計人員對專案中的某一個url路徑設定了只允許某個角色或具有某種許可權才可以訪問的控制約束的時候,Shiro就可以通過以上兩個物件來判斷。說到這裡,大家可能還比較困惑。先不要著急,繼續往後看就自然會明白了。
二、實現Realm
如何實現Realm是本文的重頭戲,也是比較費事的部分。這裡大家會接觸到幾個新鮮的概念:快取機制、雜湊演算法、加密演算法。由於本文不會專門介紹這些概念,所以這裡僅僅拋磚引玉的談幾點,能幫助大家更好的理解Shiro即可。
(1)快取機制
Ehcache是很多Java專案中使用的快取框架,Hibernate就是其中之一。它的本質就是將原本只能儲存在記憶體中的資料通過演算法儲存到硬碟上,再根據需求依次取出。你可以把Ehcache理解為一個Map<String,Object>物件,通過put儲存物件,再通過get取回物件。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shirocache">
<diskStore path="java.io.tmpdir" />
<cache name="passwordRetryCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
以上是ehcache.xml檔案的基礎配置,timeToLiveSeconds為快取的最大生存時間,timeToIdleSeconds為快取的最大空閒時間,當eternal為false時ttl和tti才可以生效。更多配置的含義大家可以去網上查詢。(2)雜湊演算法與加密演算法
md5是本文會使用的雜湊演算法,加密演算法本文不會涉及。雜湊和加密本質上都是將一個Object變成一串無意義的字串,不同點是經過雜湊的物件無法復原,是一個單向的過程。例如,對密碼的加密通常就是使用雜湊演算法,因此使用者如果忘記密碼只能通過修改而無法獲取原始密碼。但是對於資訊的加密則是正規的加密演算法,經過加密的資訊是可以通過祕鑰解密和還原。
(3)使用者註冊
請注意,雖然我們一直在談論使用者登入的安全性問題,但是說到使用者登入首先就是使用者註冊。如何保證使用者註冊的資訊不丟失,不洩密也是專案設計的重點。
public class PasswordHelper {
private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
private String algorithmName = "md5";
private final int hashIterations = 2;
public void encryptPassword(User user) {
// User物件包含最基本的欄位Username和Password
user.setSalt(randomNumberGenerator.nextBytes().toHex());
// 將使用者的註冊密碼經過雜湊演算法替換成一個不可逆的新密碼儲存進資料,雜湊過程使用了鹽
String newPassword = new SimpleHash(algorithmName, user.getPassword(),
ByteSource.Util.bytes(user.getCredentialsSalt()), hashIterations).toHex();
user.setPassword(newPassword);
}
}
如果你不清楚什麼叫加鹽可以忽略雜湊的過程,只要明白儲存在資料庫中的密碼是根據戶註冊時填寫的密碼所產生的一個新字串就可以了。經過雜湊後的密碼替換使用者註冊時的密碼,然後將User儲存進資料庫。剩下的工作就丟給UserService來處理。那麼這樣就帶來了一個新問題,既然雜湊演算法是無法復原的,當用戶登入的時候使用當初註冊時的密碼,我們又應該如何判斷?答案就是需要對使用者密碼再次以相同的演算法雜湊運算一次,再同資料庫中儲存的字串比較。
(4)匹配
CredentialsMatcher是一個介面,功能就是用來匹配使用者登入使用的令牌和資料庫中儲存的使用者資訊是否匹配。當然它的功能不僅如此。本文要介紹的是這個介面的一個實現類:HashedCredentialsMatcher
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
// 宣告一個快取介面,這個介面是Shiro快取管理的一部分,它的具體實現可以通過外部容器注入
private Cache<String, AtomicInteger> passwordRetryCache;
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String) token.getPrincipal();
AtomicInteger retryCount = passwordRetryCache.get(username);
if (retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
// 自定義一個驗證過程:當用戶連續輸入密碼錯誤5次以上禁止使用者登入一段時間
if (retryCount.incrementAndGet() > 5) {
throw new ExcessiveAttemptsException();
}
boolean match = super.doCredentialsMatch(token, info);
if (match) {
passwordRetryCache.remove(username);
}
return match;
}
}
可以看到,這個實現裡設計人員僅僅是增加了一個不允許連續錯誤登入的判斷。真正匹配的過程還是交給它的直接父類去完成。連續登入錯誤的判斷依靠Ehcache快取來實現。顯然match返回true為匹配成功。(5)獲取使用者的角色和許可權資訊
說了這麼多才到我們的重點Realm,如果你已經理解了Shiro對於使用者匹配和註冊加密的全過程,真正理解Realm的實現反而比較簡單。我們還得回到上文提及的兩個非常類似的物件AuthorizationInfo和AuthenticationInfo。因為Realm就是提供這兩個物件的地方。
public class UserRealm extends AuthorizingRealm {
// 使用者對應的角色資訊與許可權資訊都儲存在資料庫中,通過UserService獲取資料
private UserService userService = new UserServiceImpl();
/**
* 提供使用者資訊返回許可權資訊
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 根據使用者名稱查詢當前使用者擁有的角色
Set<Role> roles = userService.findRoles(username);
Set<String> roleNames = new HashSet<String>();
for (Role role : roles) {
roleNames.add(role.getRole());
}
// 將角色名稱提供給info
authorizationInfo.setRoles(roleNames);
// 根據使用者名稱查詢當前使用者許可權
Set<Permission> permissions = userService.findPermissions(username);
Set<String> permissionNames = new HashSet<String>();
for (Permission permission : permissions) {
permissionNames.add(permission.getPermission());
}
// 將許可權名稱提供給info
authorizationInfo.setStringPermissions(permissionNames);
return authorizationInfo;
}
/**
* 提供賬戶資訊返回認證資訊
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
User user = userService.findByUsername(username);
if (user == null) {
// 使用者名稱不存在丟擲異常
throw new UnknownAccountException();
}
if (user.getLocked() == 0) {
// 使用者被管理員鎖定丟擲異常
throw new LockedAccountException();
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(),
user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), getName());
return authenticationInfo;
}
}
根據Shiro的設計思路,使用者與角色之前的關係為多對多,角色與許可權之間的關係也是多對多。在資料庫中需要因此建立5張表,分別是使用者表(儲存使用者名稱,密碼,鹽等)、角色表(角色名稱,相關描述等)、許可權表(許可權名稱,相關描述等)、使用者-角色對應中間表(以使用者ID和角色ID作為聯合主鍵)、角色-許可權對應中間表(以角色ID和許可權ID作為聯合主鍵)。具體dao與service的實現本文不提供。總之結論就是,Shiro需要根據使用者名稱和密碼首先判斷登入的使用者是否合法,然後再對合法使用者授權。而這個過程就是Realm的實現過程。(6)會話
使用者的一次登入即為一次會話,Shiro也可以代替Tomcat等容器管理會話。目的是當用戶停留在某個頁面長時間無動作的時候,再次對任何連結的訪問都會被重定向到登入頁面要求重新輸入使用者名稱和密碼而不需要程式設計師在Servlet中不停的判斷Session中是否包含User物件。啟用Shiro會話管理的另一個用途是可以針對不同的模組採取不同的會話處理。以淘寶為例,使用者註冊淘寶以後可以選擇記住使用者名稱和密碼。之後再次訪問就無需登陸。但是如果你要訪問支付寶或購物車等連結依然需要使用者確認身份。當然,Shiro也可以建立使用容器提供的Session最為實現。
三、與SpringMVC整合
有了註冊模組和Realm模組的支援,下面就是如何與SpringMVC整合開發。有過框架整合經驗的同學一定知道,所謂的整合基本都是一堆xml檔案的配置,Shiro也不例外。
(1)配置前端過濾器
先說一個題外話,Filter是過濾器,interceptor是攔截器。前者基於回撥函式實現,必須依靠容器支援。因為需要容器裝配好整條FilterChain並逐個呼叫。後者基於代理實現,屬於AOP的範疇。
如果希望在WEB環境中使用Shiro必須首先在web.xml檔案中配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>Shiro_Project</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 將Shiro的配置檔案交給Spring監聽器初始化 -->
<param-value>classpath:spring.xml,classpath:spring-shiro-web.xml</param-value>
</context-param>
<context-param>
<param-name>log4jConfigLoaction</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<!-- shiro配置 開始 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- shiro配置 結束 -->
</web-app>
熟悉Spring配置的同學可以重點看有綠字註釋的部分,這裡是使Shiro生效的關鍵。由於專案通過Spring管理,因此所有的配置原則上都是交給Spring。DelegatingFilterProxy的功能是通知Spring將所有的Filter交給ShiroFilter管理。接著在classpath路徑下配置spring-shiro-web.xml檔案
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 快取管理器 使用Ehcache實現 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
</bean>
<!-- 憑證匹配器 -->
<bean id="credentialsMatcher" class="utils.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager" />
<property name="hashAlgorithmName" value="md5" />
<property name="hashIterations" value="2" />
<property name="storedCredentialsHexEncoded" value="true" />
</bean>
<!-- Realm實現 -->
<bean id="userRealm" class="utils.UserRealm">
<property name="credentialsMatcher" ref="credentialsMatcher" />
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm" />
</bean>
<!-- Shiro的Web過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/" />
<property name="unauthorizedUrl" value="/" />
<property name="filterChainDefinitions">
<value>
/authc/admin = roles[admin]
/authc/** = authc
/** = anon
</value>
</property>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
</beans>
需要注意filterChainDefinitions過濾器中對於路徑的配置是有順序的,當找到匹配的條目之後容器不會再繼續尋找。因此帶有萬用字元的路徑要放在後面。三條配置的含義是: /authc/admin需要使用者有用admin許可權、/authc/**使用者必須登入才能訪問、/**其他所有路徑任何人都可以訪問。說了這麼多,大家一定關心在Spring中引入Shiro之後到底如何編寫登入程式碼呢。
@Controller
public class LoginController {
@Autowired
private UserService userService;
@RequestMapping("login")
public ModelAndView login(@RequestParam("username") String username, @RequestParam("password") String password) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (IncorrectCredentialsException ice) {
// 捕獲密碼錯誤異常
ModelAndView mv = new ModelAndView("error");
mv.addObject("message", "password error!");
return mv;
} catch (UnknownAccountException uae) {
// 捕獲未知使用者名稱異常
ModelAndView mv = new ModelAndView("error");
mv.addObject("message", "username error!");
return mv;
} catch (ExcessiveAttemptsException eae) {
// 捕獲錯誤登入過多的異常
ModelAndView mv = new ModelAndView("error");
mv.addObject("message", "times error");
return mv;
}
User user = userService.findByUsername(username);
subject.getSession().setAttribute("user", user);
return new ModelAndView("success");
}
}
登入完成以後,當前使用者資訊被儲存進Session。這個Session是通過Shiro管理的會話物件,要獲取依然必須通過Shiro。傳統的Session中不存在User物件。@Controller
@RequestMapping("authc")
public class AuthcController {
// /authc/** = authc 任何通過表單登入的使用者都可以訪問
@RequestMapping("anyuser")
public ModelAndView anyuser() {
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getSession().getAttribute("user");
System.out.println(user);
return new ModelAndView("inner");
}
// /authc/admin = user[admin] 只有具備admin角色的使用者才可以訪問,否則請求將被重定向至登入介面
@RequestMapping("admin")
public ModelAndView admin() {
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getSession().getAttribute("user");
System.out.println(user);
return new ModelAndView("inner");
}
}
四、總結
Shiro是一個功能很齊全的框架,使用起來也很容易,但是要想用好卻有相當難度。完整專案的原始碼就不在這裡提供了,需要交流的同學可以給我留言或直接查閱張開濤的部落格。如果大家感覺我寫的還可以,也希望能給我一些反饋意見。
相關推薦
開發框架-Shiro-30分鐘學會如何使用Shiro
一、架構 要學習如何使用Shiro必須先從它的架構談起,作為一款安全框架Shiro的設計相當精妙。Shiro的應用不依賴任何容器,它也可以在JavaSE下使用。但是最常用的環境還是JavaEE。下面以使用者登入為例: (1)使用使用者的登入資訊建立令牌 Usern
30分鐘學會如何使用Shiro
springmvc+mybatis dubbo+zookeeper restful redis分布式緩存 shiro kafka 一、架構要學習如何使用Shiro必須先從它的架構談起,作為一款安全框架Shiro的設計相當精妙。Shiro的應用不依賴任何容器,它也可以在JavaSE下使用。但
30分鐘學會如何使用Shiro(轉)
字段 col inf lan getc 含義 掌握 ide 包含 本文轉自http://www.cnblogs.com/learnhow/p/5694876.html 感謝作者 本篇內容大多總結自張開濤的《跟我學Shiro》原文地址:http://jinnianshilo
30分鐘學會如何使用Shiro(轉)
dex user false fin hiberna bean overflow getc mis 本篇內容大多總結自張開濤的《跟我學Shiro》原文地址:http://jinnianshilongnian.iteye.com/blog/2018936我並沒有全部看完,只是
****30分鐘學會如何使用Shiro
https://www.cnblogs.com/learnhow/p/5694876.html本篇內容大多總結自張開濤的《跟我學Shiro》原文地址:http://jinnianshilongnian.iteye.com/blog/2018936我並沒有全部看完,只是選擇了一
轉:30分鐘學會如何使用Shiro
授權 play 分享 配置文件 3.1 單向 get() 地址 全部 引自:http://www.cnblogs.com/learnhow/p/5694876.html 本篇內容大多總結自張開濤的《跟我學Shiro》原文地址:http://jinnianshilongni
30分鐘學會iOS 11開發環境xcode 9圖文教程
iOS 11關註微信公眾號【異步圖書】每周送書Xcode是一款功能全面的應用程序,通過此工具可以輕松輸入、編譯、調試並執行Objective-C程序。如果想在Mac上快速開發iOS應用程序,則必須學會使用這個強大的工具的方法。在本文容中,將詳細講解Xcode 9開發工具的基本知識,為讀者步入本書後面知識的學習
使用SpringBoot2.0搭建企業級應用開發框架(七)整合Shiro
準備 首先建立使用者許可權表 //使用者表 CREATE TABLE `sys_user` ( `id` varchar(32) NOT NULL COMMENT 'id', `username` varchar(64) DEFAULT NULL COMMENT '
30分鐘瞭解Shiro與Springboot的多Realm基礎配置
寫在前面的話: 我之前寫過兩篇與shiro安全框架有關的博文,居然能夠廣受歡迎實在令人意外。說明大家在網際網路時代大夥對於安全和登入都非常重視,無論是大型專案還是中小型業務,普遍都至少需要登入與認證的邏輯封裝。相較於SpringSecurity而言,Shrio更輕量無過多依賴和便於獨立部署的特點更收到開發者
微信小程式之30分鐘學會開發小程式(小程式總覽)
一.註冊小程式賬號,下載IDE 1.官網註冊https://mp.weixin.qq.com/,並下載IDE。 2.官方文件一向都是最好的學習資料。 注意: (1)註冊賬號之後會有一個appid,新建專案的時候需要填上,不然很多功能是用不了的,比如不能預覽,不能上傳程式碼等等。
小白30分鐘學會網頁采集基礎教程
網頁采集首先,以某個多頁(需要自動翻頁)表格數據的采集為例,先演示一次網頁采集的完整的過程:點擊從頭播放完整動圖演示這裏使用的是八爪魚,依次點擊表格某一行的每個字段,可以自動識別出其他所有數據行,並自動創建循環列表;點擊翻頁按鈕,選擇“循環點擊下一頁”動作,就能自動創建翻頁循環。網頁信息爪取相關的工具有很多,
30分鐘學會EventBus3.0詳解(一)(引入和初始化EventBus3.0) 30分鐘學會EventBus3.0詳解(二)(EventBus3.0的詳細使用) 30分鐘學會EventBus3.0詳解(一)(引入和初始化EventBus3.0) 30分鐘學會EventBus3.0詳解(二)(Ev
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
30分鐘學會ES6 (二)
ES6學習系列重點針對ES6中新增特性進行學習,分為兩個章節: 30分鐘學會ES6 (一) 本章節主要學習 let,const,template string,變數解構賦值,函式,Set,Map 30分鐘學會ES6 (二) 本章節主要學習 類 和 模組化 。 30
30分鐘學會ES6 (一)
ES6學習系列重點針對ES6中新增特性進行學習,分為兩個章節: 30分鐘學會ES6 (一) 本章節主要學習 let,const,template string,變數解構賦值,函式,Set,Map 30分鐘學會ES6 (二) 本章節主要學習 類 和 模組化 。 30
【grunt第一彈】30分鐘學會使用grunt打包前端程式碼
前言 以現在前端js激增的態勢,一個專案下來幾十個js檔案輕輕鬆鬆對於複雜一點的單頁應用來說,檔案上百簡直是家常便飯,那麼這個時候我們的js檔案應該怎麼處理呢?另外,對於css檔案,又該如何處理呢??這些都是我們實際工作中要遇到的問題,比如我們現在框架使用zepto、backbone、underscore我
【grunt整合版】30分鐘學會使用grunt打包前端程式碼
grunt 是一套前端自動化工具,一個基於nodeJs的命令列工具,一般用於:① 壓縮檔案② 合併檔案③ 簡單語法檢查 對於其他用法,我還不太清楚,我們這裡簡單介紹下grunt的壓縮、合併檔案,初學,有誤請包涵 準備階段 1、nodeJs環境 因為grunt是基於nodeJs的,所以首先各位需要安裝
30分鐘學會微信公眾號的資料抓取
網路爬蟲實戰之微信公眾號 簡介: 這篇文章主要教大家怎麼獲取一些電腦無法訪問的微信公眾號資料,乾貨滿滿,30分鐘包學會。 實戰 環境 作業系統: win10 python版本: 2.7.3 安裝 抓包工具有很多,
30分鐘學會用scikit-learn的基本回歸方法(線性、決策樹、SVM、KNN)和整合方法(隨機森林,Adaboost和GBRT)
注:本教程是本人嘗試使用scikit-learn的一些經驗,scikit-learn真的超級容易上手,簡單實用。30分鐘學會用呼叫基本的迴歸方法和整合方法應該是夠了。 本文主要參考了scikit-learn的官方網站 前言:本教程主要使用了numpy的最最基
微信小程式開發入門篇 30分鐘內教你寫出一個helloword小程式
本文件將帶你一步步建立完成一個微信小程式,並可以在手機上體驗該小程式的實際效果。 開發準備工作 獲取微信小程式的 AppID 登入 https://mp.weixin.qq.com ,就可以在網站的“設定”-“開發者設定”中,檢視到微信小程式的 AppID 了,注意
30分鐘學會vim之vimtutor(雙語版)
=============================================================================== = W e l c o m e t o t h e V I M T u t o r - Version 1.7 = 歡迎使用VIM教程 1.7版 =