SpringBoot集成Shiro

Shiro主要用來進行權限管理。簡單的介紹以下:html

1、概念java

Shiro是一個安全框架,能夠進行角色、權限管理。git

Shiro主要功能以下:
Authentication(認證):用戶身份識別,一般被稱爲用戶「登陸」
Authorization(受權):訪問控制。好比某個用戶是否具備某個操做的使用權限。
Session Management(會話管理):特定於用戶的會話管理,甚至在非web 或 EJB 應用程序。
Cryptography(加密):在對數據源使用加密算法加密的同時,保證易於使用。
2、主要的類github

1.Subject:當前用戶,Subject能夠是一我的,也能夠是第三方服務
2.SecurityManager:管理全部Subject,能夠配合內部安全組件。web

3.principals:身份,即主體的標識屬性,能夠是任何東西,如用戶名、郵箱等,惟一便可。一個主體能夠有多個principals,但只有一個Primary principals,通常是用戶名/密碼/手機號。
4.credentials:證實/憑證,即只有主體知道的安全值,如密碼/數字證書等。
最多見的principals和credentials組合就是用戶名/密碼了。算法

5.Realms:用於進行權限信息的驗證,須要本身實現。
6.Realm 本質上是一個特定的安全 DAO:它封裝與數據源鏈接的細節,獲得Shiro 所需的相關的數據。
在配置 Shiro 的時候,你必須指定至少一個Realm 來實現認證(authentication)和/或受權(authorization)。
咱們須要實現Realms的Authentication 和 Authorization。其中 Authentication 是用來驗證用戶身份,Authorization 是受權訪問控制,用於對用戶進行的操做受權,證實該用戶是否容許進行當前操做,如訪問某個連接,某個資源文件等。spring

7.SimpleHash,能夠經過特定算法(好比md5)配合鹽值salt,對密碼進行屢次加密。數據庫

 3、Shiro配置apache

1.Spring集成Shiro通常經過xml配置,SpringBoot集成Shiro通常經過java代碼配合@Configuration和@Bean配置。緩存

2.Shiro的核心經過過濾器Filter實現。Shiro中的Filter是經過URL規則來進行過濾和權限校驗,因此咱們須要定義一系列關於URL的規則和訪問權限。

3.SpringBoot集成Shiro,咱們須要寫的主要是兩個類,ShiroConfiguration類,還有繼承了AuthorizingRealm的Realm類

ShiroConfiguration類,用來配置Shiro,注入各類Bean。

包括過濾器(shiroFilter)、安全事務管理器(SecurityManager)、密碼憑證(CredentialsMatcher)、aop註解支持(authorizationAttributeSourceAdvisor)等等

Realm類,包括登錄認證(doGetAuthenticationInfo)、受權認證(doGetAuthorizationInfo)

4、具體示例以下:

ShiroConfiguration.java以下:

package com.example.demo.config;


import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by lenovo on  三月
 */
@Configuration
public class ShiroConfiguration {

    @Bean
     public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
         ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
         //設置安全管理器
         shiroFilterFactoryBean.setSecurityManager(securityManager);
         //默認跳轉到登錄頁面
         shiroFilterFactoryBean.setLoginUrl("/login");
         //登錄成功後的頁面
         shiroFilterFactoryBean.setSuccessUrl("/index");
         shiroFilterFactoryBean.setUnauthorizedUrl("/403");

         //自定義過濾器
        Map<String,Filter> filterMap=new LinkedHashMap<>();
        shiroFilterFactoryBean.setFilters(filterMap);
        //權限控制map
        Map<String,String> filterChainDefinitionMap=new LinkedHashMap<>();
        // 配置不會被攔截的連接 順序判斷
        filterChainDefinitionMap.put("/static/**", "anon");
        //配置退出 過濾器,其中的具體的退出代碼Shiro已經替咱們實現了
        filterChainDefinitionMap.put("/logout", "logout");
//        //<!-- 過濾鏈定義,從上向下順序執行,通常將/**放在最爲下邊 -->:這是一個坑呢,一不當心代碼就很差使了;
//        //<!-- authc:全部url都必須認證經過才能夠訪問; anon:全部url都均可以匿名訪問-->
//        filterChainDefinitionMap.put("/**", "anon");

         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
         return shiroFilterFactoryBean;
     }

    /**
     * 核心的安全事務管理器
     * @return
     */
     @Bean
    public SecurityManager securityManager(){
         DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager ();
         //設置realm
         securityManager.setRealm( myShiroRealm(  )  );
         securityManager.setRememberMeManager(rememberMeManager());
         return securityManager;
     }


    /**
     * 身份認證Realm,此處的注入不能夠缺乏。不然會在UserRealm中注入對象會報空指針.
     * @return
     */
    @Bean
    public UserRealm myShiroRealm(  ){
        UserRealm myShiroRealm = new UserRealm();
        myShiroRealm.setCredentialsMatcher(  hashedCredentialsMatcher() );
        return myShiroRealm;
    }



    /**
     * 哈希密碼比較器。在myShiroRealm中做用參數使用
     * 登錄時會比較用戶輸入的密碼,跟數據庫密碼配合鹽值salt解密後是否一致。
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這裏使用md5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次數,好比散列兩次,至關於 md5( md5(""));
        return hashedCredentialsMatcher;
    }

//    //注入緩存
//    @Bean
//    public EhCacheManager ehCacheManager(){
//        System.out.println("ShiroConfiguration.getEhCacheManager()執行");
//        EhCacheManager cacheManager=new EhCacheManager();
//        cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
//        return cacheManager;
//    }

    /**
     *  開啓shiro aop註解支持.
     *  使用代理方式;因此須要開啓代碼支持;不然@RequiresRoles等註解沒法生效
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * Shiro生命週期處理器
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 自動建立代理
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
}

Realm類以下:

package com.example.demo.config;

import com.example.demo.pojo.SysPermission;
import com.example.demo.pojo.SysUserRole;
import com.example.demo.pojo.User;
import com.example.demo.service.UserSerevice;
import com.example.demo.utils.State;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.*;
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.apache.shiro.util.ByteSource;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.apache.coyote.http11.Constants.a;

/**
 * Created by lenovo on  三月
 */
public class UserRealm extends AuthorizingRealm {
    @Resource(name = "userServiceImp")
    private UserSerevice userService;

    private Logger logger=Logger.getLogger(UserRealm.class);

    /**
     * 提供用戶信息,返回權限信息
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("---------------------------->受權認證:");
        SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
        String userName=(String) principals.getPrimaryPrincipal();
        String userId=userService.findUserIdByName(userName);
        Set<SysUserRole> roleIdSet=userService.findRoleIdByUid( Integer.parseInt(userId) );
        Set<String> roleSet=new HashSet<>();
        Set<Integer>  pemissionIdSet=new HashSet<>();
        Set<String>  pemissionSet=new HashSet<>();
        for(SysUserRole roleInfo : roleIdSet) {
              int roleId=roleInfo.getRoleId();
               roleSet.add( userService.findRoleByRoleId( roleId  ) );
               //將擁有角色的全部權限放進Set裏面,也就是求Set集合的並集
               //因爲我這邊的數據表設計得不太好,因此提取set集合比較麻煩
              pemissionIdSet.addAll( userService.findPermissionIdByRoleId(  roleId ));
        }
        for(int permissionId : pemissionIdSet) {
            String permission= userService.findPermissionById( permissionId ).getPermission() ;
            pemissionSet.add(  permission );
        }
         // 將角色名稱組成的Set提供給受權info
        authorizationInfo.setRoles( roleSet );
        // 將權限名稱組成的Set提供給info
        authorizationInfo.setStringPermissions(pemissionSet);

        return authorizationInfo;
    }

    /**
     * 提供賬戶信息,返回認證信息
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("---------------------------->登錄驗證:");
        String userName=(String)authenticationToken.getPrincipal();
        User user=userService.findUserByName(userName);
        if(user==null) {
            //用戶不存在就拋出異常
            throw new UnknownAccountException();
        }
        if( State.LOCKED.equals( user.getState() )  ) {
           //用戶被鎖定就拋異常
           throw new  LockedAccountException();
        }
        //密碼能夠經過SimpleHash加密,而後保存進數據庫。
        //此處是獲取數據庫內的帳號、密碼、鹽值,保存到登錄信息info中
        SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user.getUsername(),
                user.getPassword(),
                ByteSource.Util.bytes(user.getSalt())   ,
                getName());                   //realm name

        return authenticationInfo;
    }
}

 

更具體的代碼,參見GitHub:

https://github.com/firefoxer1992/SpringBootProject

參考博客:

http://www.ityouknow.com/springboot/2017/06/26/springboot-shiro.html

鹽值及密碼的加密,能夠參考博客:

https://blog.csdn.net/qq_31080089/article/details/53715910

相關文章
相關標籤/搜索