shiro是apache的一個開源框架,是一個權限管理的框架,實現 用戶認證、用戶受權。java
spring中有spring security (原名Acegi),是一個權限框架,它和spring依賴過於緊密,沒有shiro使用簡單。
shiro不依賴於spring,shiro不只能夠實現 web應用的權限管理,還能夠實現c/s系統,分佈式系統權限管理,shiro屬於輕量框架,愈來愈多企業項目開始使用shiro。web
Shiro架構:算法
cryptography:密碼管理,提供了一套加密/解密的組件,方便開發。好比提供經常使用的散列、加/解密等功能。spring
咱們在使用URL攔截的時候,要將全部的URL都配置起來,繁瑣、不易維護數據庫
而咱們的Shiro實現系統的權限管理,有效提升開發效率,從而下降開發成本。apache
咱們使用的是Maven的座標就好了緩存
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>1.2.3</version> </dependency>
固然了,咱們也能夠把Shiro相關的jar包所有導入進去安全
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.3</version> </dependency>
// 用戶登錄和退出 @Test public void testLoginAndLogout() { // 建立securityManager工廠,經過ini配置文件建立securityManager工廠 Factory<SecurityManager> factory = new IniSecurityManagerFactory( "classpath:shiro-first.ini"); // 建立SecurityManager SecurityManager securityManager = factory.getInstance(); // 將securityManager設置當前的運行環境中 SecurityUtils.setSecurityManager(securityManager); // 從SecurityUtils裏邊建立一個subject Subject subject = SecurityUtils.getSubject(); // 在認證提交前準備token(令牌) // 這裏的帳號和密碼 未來是由用戶輸入進去 UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "111111"); try { // 執行認證提交 subject.login(token); } catch (AuthenticationException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 是否定證經過 boolean isAuthenticated = subject.isAuthenticated(); System.out.println("是否定證經過:" + isAuthenticated); // 退出操做 subject.logout(); // 是否定證經過 isAuthenticated = subject.isAuthenticated(); System.out.println("是否定證經過:" + isAuthenticated); }
ModularRealmAuthenticator做用進行認證,須要調用realm查詢用戶信息(在數據庫中存在用戶信息)
ModularRealmAuthenticator進行密碼對比(認證過程)。
realm:須要根據token中的身份信息去查詢數據庫(入門程序使用ini配置文件),若是查到用戶返回認證信息,若是查詢不到返回null。markdown
從第一個認證程序咱們能夠看見,咱們所說的流程,是認證器去找realm去查詢咱們相對應的數據。而默認的realm是直接去與配置文件來比對的,通常地,咱們在開發中都是讓realm去數據庫中比對。
所以,咱們須要自定義realmsession
public class CustomRealm extends AuthorizingRealm { // 設置realm的名稱 @Override public void setName(String name) { super.setName("customRealm"); } // 用於認證 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { // token是用戶輸入的 // 第一步從token中取出身份信息 String userCode = (String) token.getPrincipal(); // 第二步:根據用戶輸入的userCode從數據庫查詢 // .... // 若是查詢不到返回null //數據庫中用戶帳號是zhangsansan /*if(!userCode.equals("zhangsansan")){// return null; }*/ // 模擬從數據庫查詢到密碼 String password = "111112"; // 若是查詢到返回認證信息AuthenticationInfo SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( userCode, password, this.getName()); return simpleAuthenticationInfo; } // 用於受權 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { // TODO Auto-generated method stub return null; } }
須要在shiro-realm.ini配置realm注入到securityManager中。
同上邊的入門程序,須要更改ini配置文件路徑:
同上邊的入門程序,須要更改ini配置文件路徑: Factory<SecurityManager> factory = new IniSecurityManagerFactory( "classpath:shiro-realm.ini");
咱們若是知道md5,咱們就會知道md5是不可逆的,可是若是設置了一些安全性比較低的密碼:111111…即時是不可逆的,但仍是能夠經過暴力算法來獲得md5對應的明文…
建議對md5進行散列時加salt(鹽),進行加密至關 於對原始密碼+鹽進行散列。\
正常使用時散列方法:
測試:
public class MD5Test { public static void main(String[] args) { //原始 密碼 String source = "111111"; //鹽 String salt = "qwerty"; //散列次數 int hashIterations = 2; //上邊散列1次:f3694f162729b7d0254c6e40260bf15c //上邊散列2次:36f2dfa24d0a9fa97276abbe13e596fc //構造方法中: //第一個參數:明文,原始密碼 //第二個參數:鹽,經過使用隨機數 //第三個參數:散列的次數,好比散列兩次,至關 於md5(md5('')) Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations); String password_md5 = md5Hash.toString(); System.out.println(password_md5); //第一個參數:散列算法 SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations); System.out.println(simpleHash.toString()); } }
自定義realm
public class CustomRealmMd5 extends AuthorizingRealm { // 設置realm的名稱 @Override public void setName(String name) { super.setName("customRealmMd5"); } // 用於認證 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { // token是用戶輸入的 // 第一步從token中取出身份信息 String userCode = (String) token.getPrincipal(); // 第二步:根據用戶輸入的userCode從數據庫查詢 // .... // 若是查詢不到返回null // 數據庫中用戶帳號是zhangsansan /* * if(!userCode.equals("zhangsansan")){// return null; } */ // 模擬從數據庫查詢到密碼,散列值 String password = "f3694f162729b7d0254c6e40260bf15c"; // 從數據庫獲取salt String salt = "qwerty"; //上邊散列值和鹽對應的明文:111111 // 若是查詢到返回認證信息AuthenticationInfo SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo( userCode, password, ByteSource.Util.bytes(salt), this.getName()); return simpleAuthenticationInfo; } // 用於受權 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { // TODO Auto-generated method stub return null; } }
配置文件:
測試:
// 自定義realm實現散列值匹配 @Test public void testCustomRealmMd5() { // 建立securityManager工廠,經過ini配置文件建立securityManager工廠 Factory<SecurityManager> factory = new IniSecurityManagerFactory( "classpath:shiro-realm-md5.ini"); // 建立SecurityManager SecurityManager securityManager = factory.getInstance(); // 將securityManager設置當前的運行環境中 SecurityUtils.setSecurityManager(securityManager); // 從SecurityUtils裏邊建立一個subject Subject subject = SecurityUtils.getSubject(); // 在認證提交前準備token(令牌) // 這裏的帳號和密碼 未來是由用戶輸入進去 UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "222222"); try { // 執行認證提交 subject.login(token); } catch (AuthenticationException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 是否定證經過 boolean isAuthenticated = subject.isAuthenticated(); System.out.println("是否定證經過:" + isAuthenticated); }