Shiro整合springboot(登錄認證和加密)

參考博客:https://blog.csdn.net/bicheng4769/article/details/86668209?ops_request_misc=&request_id=&biz_id=102&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0

今天花了時間學了下shiro整合springboot,學得很淺,這裏記一下筆記。

maven配置和spring的配置就不寫在這了。

直奔主題。

在spingboot裏面整合shiro主要有兩個類:

一是:自定義的Realm類,咱的授權邏輯和認證邏輯代碼都寫在這裏面。

二是:Shiro的配置類,這裏面做的主要是下面三點。

Shiro的核心API

Subject: 用戶主體(把操作交給SecurityManager)

SecurityManager:安全管理器(關聯Realm)

Realm:Shiro連接數據的橋樑

 

這裏給上這兩個類的基本代碼:

Realm類:

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

/**
 * 自定義Realm
 * @author lenovo
 *
 */
public class UserRealm extends AuthorizingRealm{

	/**
	 * 執行授權邏輯
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		System.out.println("執行授權邏輯");
		return null;
	}

	/**
	 * 執行認證邏輯
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("執行認證邏輯");
		return null;
	}

}

doGetAuthorizationInfo: 權限認證,即登錄過後,每個身份不一定,對應的所能看的頁面也不一樣。
doGetAuthenticationInfo:身份認證。即登錄通過賬號和密碼驗證登陸人的身份信息。

比如身份認證裏面,就可以寫好對賬號密碼的判斷,通過參數裏面的Token拿出來,判斷賬號是否存在,比如下面所示。

然後我們在寫業務代碼的時候調用Subject.login()就可以判斷賬號是非存在了。

(下面的salt是用來解碼的,因爲咱在存密碼的時候進行了加密操作,後邊會提到。)

@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String userName = token.getPrincipal().toString();
        User user=userService.getByName(userName);
        String passwordInDB=user.getPassword();
        String salt = user.getSalt();
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordInDB, ByteSource.Util.bytes(salt),
                getName());
        return authenticationInfo;
    }

shiro配置類:

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Shiro的配置類
 * @author lenovo
 *
 */
@Configuration
public class ShiroConfig {

	/**
	 * 創建ShiroFilterFactoryBean
	 */
    @Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		//設置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 創建DefaultWebSecurityManager
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//關聯realm
		securityManager.setRealm(userRealm);
		return securityManager;
	}
	
	/**
	 * 創建Realm
	 */
	@Bean(name="userRealm")
	public UserRealm getRealm(){
		return new UserRealm();
	}
}

這裏面是最基本的三個方法,必要的,

Shiro內置過濾器實現頁面攔截,如下:

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Shiro的配置類
 * @author lenovo
 *
 */
@Configuration
public class ShiroConfig {

	/**
	 * 創建ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		//設置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//添加Shiro內置過濾器
		/**
		 * Shiro內置過濾器,可以實現權限相關的攔截器
		 *    常用的過濾器:
		 *       anon: 無需認證(登錄)可以訪問
		 *       authc: 必須認證纔可以訪問
		 *       user: 如果使用rememberMe的功能可以直接訪問
		 *       perms: 該資源必須得到資源權限纔可以訪問
		 *       role: 該資源必須得到角色權限纔可以訪問
		 */
		Map<String,String> filterMap = new LinkedHashMap<String,String>();
		/*filterMap.put("/add", "authc");
		filterMap.put("/update", "authc");*/
		
		filterMap.put("/testThymeleaf", "anon");
		
		filterMap.put("/*", "authc");
		
		//修改調整的登錄頁面
		shiroFilterFactoryBean.setLoginUrl("/toLogin");
		
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 創建DefaultWebSecurityManager
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//關聯realm
		securityManager.setRealm(userRealm);
		return securityManager;
	}
	
	/**
	 * 創建Realm
	 */
	@Bean(name="userRealm")
	public UserRealm getRealm(){
		return new UserRealm();
	}
}

下面代碼是Realm裏面的授權邏輯部分,處理上面進行了授權攔截的訪問,首先在數據庫設置一個字段Perms,表示該user的權限,然後給info加上這個授權字符串,

比如一個user: name=java   Perms=user:add,那麼在訪問時被config裏面的代碼攔截了,但是攔截的訪問都會運行到Realm裏面的,在這裏面又給他進行了授權,那就能正常訪問了。(我自己寫的項目並沒有涉及權限,所以這一塊理解不深)

然後還有密碼採用加密,在config裏面加上下面代碼:

@Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher=new HashedCredentialsMatcher();
// 散列算法:這裏使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
// 散列的次數,比如散列兩次,相當於 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

然後在獲取Realm部分需要改變:

@Bean(name="userRealm")
	public UserRealm getRealm(){
		UserRealm myShiroRealm=new UserRealm();
        UserRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return UserRealm;
	}
}

流程是這樣的,用戶註冊的時候,程序將明文通過加密方式加密,存到數據庫的是密文,登錄時將密文取出來,再通過shiro將用戶輸入的密碼進行加密對比,一樣則成功,不一樣則失敗。
我們可以看到這裏的加密採用的是MD5,而且是加密兩次(MD5(MD5))。

shiro提供了SimpleHash類幫助我們快速加密

列子:

註冊:

@PostMapping("/foreregister")
    public Object register(@RequestBody User user) {
        String name =  user.getName();
        String password = user.getPassword();
        name = HtmlUtils.htmlEscape(name);
        user.setName(name);
        boolean exist = userService.isExist(name);

        if(exist){
            String message ="用戶名已經被使用,不能使用";
            return Result.fail(message);
        }
        String salt =new SecureRandomNumberGenerator().nextBytes().toString();
        int times= 2;
        String algorithmName="md5";
        String encodedPassword=new SimpleHash(algorithmName,password,salt,times).toString();
        user.setSalt(salt);
        user.setPassword(encodedPassword);

        userService.add(user);

        return Result.success();
    }

這是構造salt和加密密碼存入數據庫,然後我們在登錄認證的時候進行解碼比較:

@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String userName = token.getPrincipal().toString();
        User user=userService.getByName(userName);
        String passwordInDB=user.getPassword();
        String salt = user.getSalt();
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordInDB, ByteSource.Util.bytes(salt),
                getName());
        return authenticationInfo;
    }

這就實現了加密操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章