Shiro教程之四 雜湊演算法和憑證配置
阿新 • • 發佈:2020-08-27
Shiro認證密碼一般都需要加密(大部分是md5或hash雜湊加密)
如果改為密文驗證,需要 1.改動一下自定義的Realm類和 2.測試時new出來的 Realm類加入認證的憑證
先來下加密的例子
//五,測試加密 (要引入Shiro包,對應的有此類) @Test public void testMd5Hash(){ String source = "123456";//原文 String salt = "abc";//混淆(可選引數) int num = 2;//雜湊加密次數(可選引數) Md5Hash hash1= new Md5Hash(source,salt,num); System.out.println("123456混淆abc雜湊2次的結果:"+hash1); }
1.改動一下自定義的Realm類
package com.cc8w.shiro; import com.cc8w.entity.UserActivePojo; import com.cc8w.entity.UserPojo; import com.cc8w.service.PermssionService; import com.cc8w.service.RoleService; import com.cc8w.service.UserService;import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; /** * Shiro預設使用自帶的IniRealm,IniRealm從ini配置檔案中讀取使用者的資訊,大部分情況下需要從系統的資料庫中讀取使用者資訊,所以需要自定義realm。 * 最基礎的是Realm介面,CachingRealm負責快取處理,AuthenticationRealm負責認證,AuthorizingRealm負責授權,通常自定義的realm繼承AuthorizingRealm */ @Component public class ShiroRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermssionService permssionService; @Override public String getName() { return this.getClass().getSimpleName(); } /* * 登入資訊和使用者驗證資訊驗證(non-Javadoc) * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1.從authenticationToken中獲取身份資訊,其實就是使用者的登入名 String username = authenticationToken.getPrincipal().toString(); String password = authenticationToken.getCredentials().toString(); //2.根據使用者名稱查詢使用者是否存在 UserPojo user=userService.queryUserByUserName(username); System.out.println(user); //返回null說明使用者不存在 if(null!=user) { //2.1根據使用者名稱去查詢使用者擁有哪些角色 List<String> roles= roleService.queryRolesByUserName(user.getUserName()); System.out.println(roles); //2.2根據使用者名稱查詢使用者擁有哪些許可權 List<String> permissions=permssionService.queryPermissionsByUserName(user.getUserName()); UserActivePojo activeUser=new UserActivePojo(user, roles, permissions); //3.返回認證資訊(第一個不支援雜湊加密,第二個可以雜湊加密) /** * 引數1 使用者身份 * 引數2 使用者在資料庫裡面存放的密碼 * 引數3 當前類名 */ //SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName()); /** * 引數1 傳到doGetAuthorizationInfo裡面的getPrimaryPrincipal()物件或subject.getPrincipal()-我們封裝的實體activeUser * 引數2 hashedCredentials加密後的密碼(資料庫查詢的密碼)-user.getPassword() * 引數3 混淆(鹽) * 引數4 當前類名 */ SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName) return info; } return null; } /* * 授權查詢回撥函式, 進行鑑權但快取中無使用者的授權資訊時呼叫,負責在應用程式中決定使用者的訪問控制的方法(non-Javadoc) * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1.獲得使用者身份資訊(PrincipalCollection有認證回撥傳來的第一個引數[activeUser]) UserActivePojo activeUser = (UserActivePojo) principalCollection.getPrimaryPrincipal(); System.out.println("doGetAuthorizationInfo"); //2.根據身份資訊獲取許可權資料 SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); //3.根據使用者查詢使用者的角色 (其實認證方法一併查詢出來了,儲存在UserActivePojo) List<String> roles = activeUser.getRoles(); if(null!=roles&&roles.size()>0) { info.addRoles(roles);//新增角色 } //4.根據使用者查詢使用者的許可權 List<String> permissions=activeUser.getPermissions(); if(null!=permissions&&permissions.size()>0) { info.addStringPermissions(permissions);//新增許可權 } /** * 總結:本來授權->查角色和許可權都在本方法寫(但是前端每查詢一次許可權,就會回撥本方法一次, * 所以直接查資料庫,對資料庫有壓力),所以最後, * 1.把查角色和許可權方法寫在了認證,然後封裝成activeUser傳遞過來,這樣Controller每次查許可權,就不用查資料庫了,直接在activeUser獲取即可. * 2.快取應該也可以解決 */ return info; } }
2.測試時new出來的 Realm類加入認證的憑證
<和上幾篇文章一樣,最重要的改動是 new Realm(),之後加入了雜湊密碼相關的設定>
package com.cc8w.test; import com.cc8w.shiro.ShiroRealm; import org.apache.log4j.Logger; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.UnauthenticatedException; import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.apache.shiro.mgt.SecurityManager; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.Arrays; /** * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) public class TestShiro { private static Logger logger = Logger.getLogger(TestShiro.class); @Autowired private ShiroRealm shiroRealm; public static void main(String[] args) { TestShiro ts = new TestShiro(); ts.testAuth(); } //三,登入測試(自定義Realm) @Test public void testRealmLogin(){ //1.建立一個安全管理器的工廠 Factory<SecurityManager> factory = new IniSecurityManagerFactory(); //2.在工廠中獲取安全管理器 DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance(); //---新增認證憑證配置start --如果Spring環境下(這些都是在xml配置的) //2.1 建立自定義Realm注入到安全管理器 ShiroRealm shiroRealm = new ShiroRealm();//(SpringM在bean控制,並可以配置雜湊加密相關) //2.1.1設定密碼學相關加密方式 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); //2.1.2設定加密方式 credentialsMatcher.setHashAlgorithmName("md5"); //2.1.3設定雜湊次數 credentialsMatcher.setHashIterations(1); //2.1.4將密碼學憑證注入到自定義的Realm類中 shiroRealm.setCredentialsMatcher(credentialsMatcher); //---新增認證憑證配置end --如果Spring環境下(這些都是在xml配置的) securityManager.setRealm(shiroRealm); //3.將securityManager繫結到執行環境 SecurityUtils.setSecurityManager(securityManager); //4.獲取Subject物件(將要登入的使用者) Subject subject = SecurityUtils.getSubject(); //5.獲取要登入使用者的token,客戶端傳遞過來的使用者名稱和密碼 String username = "zhangsan",password="123456"; UsernamePasswordToken token = new UsernamePasswordToken(username,password); try{ //6.登陸(認證) subject.login(token); logger.info("登入了"); }catch (IncorrectCredentialsException e ){ logger.info("密碼不正確"); logger.info(e); }catch (UnknownAccountException e) { System.out.println("沒有這個帳號"); }catch (AuthenticationException e) { e.printStackTrace(); } //如果登入成功了,可以獲取subject中各種狀態了 Boolean isAuth = subject.isAuthenticated(); System.out.println("認證狀態:" + isAuth); // 7.授權 分為:基於角色授權 基於資源的授權 //7.1 基於角色授權 boolean permited = subject.hasRole("role1"); System.out.println("這是授權單個:"+permited); boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2","role3")); System.out.println("這個授權多個"+hasAllRoles); // 使用check方法進行授權,如果授權不通過會丟擲異常 // subject.checkRole("role13"); try { subject.checkRole("roles1"); }catch (UnauthenticatedException e){ logger.info("沒有這個角色"); //e.printStackTrace(); }catch (UnauthorizedException e){ logger.info("沒有這個許可權"); //e.printStackTrace(); } //7.2 基於資源的授權 //isPermitted傳入許可權識別符號 boolean isPermitted = subject.isPermitted("user:query"); System.out.println("單個許可權判斷:"+isPermitted); boolean isPermittedAll = subject.isPermittedAll("user:query","user:adb","user:add"); System.out.println("多個許可權判斷"+isPermittedAll); // 使用check方法進行授權,如果授權不通過會丟擲異常 try { subject.checkPermission("user:adb"); }catch (UnauthenticatedException e){ logger.info("沒有這個角色"); //e.printStackTrace(); }catch (UnauthorizedException e){ logger.info("沒有這個許可權"); //e.printStackTrace(); } } //四,授權驗證(自定義Realm) public void testRealmAuth(){ //其實授權也需要登陸(上面方法第7條之後:就是授權的驗證) } }