1. 程式人生 > 實用技巧 >Shiro教程之四 雜湊演算法和憑證配置

Shiro教程之四 雜湊演算法和憑證配置

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條之後:就是授權的驗證)

    }



}