1. 程式人生 > >spring-boot + shiro + mybatis 的 demo

spring-boot + shiro + mybatis 的 demo

這裡總結下用springboot實現shiro的幾個要點,如果要下載完整的專案,請到********(下載後先在本地建立test_shiro資料庫,然後執行resources的sql包下的5個sql檔案),啟動專案即可看到網頁並且測試

說一下要點:

資料庫一般至少有五張表(本專案是用mybaits)

   1- user:使用者賬號密碼

   2-role:角色ID,一個賬戶可以有很多角色

   3. permission許可權ID,一個角色可以有很多許可權

   4-user_role關係對照表:記錄每個userID有的角色

   5-role_permission關係對照表,記錄每個role有的permission

shiro授權的時候,就是先配置那些url需要授權,那些是某個角色可以訪問的,那些是擁有某個許可權可以訪問的,另外還有不需要登入不需要許可權就可以訪問的路徑(包含靜態資源)。然後遇到需要許可權的url,就迴圈登入的userId,查出他的所有角色和所有許可權,驗證是否可以訪問這個url,如果不能就會說沒有角色或許可權,例如以下

1.  ShiroConfig類的配置,替代之前xml

其中很多配置可以用註解實現,不一定要在config裡設定

@Configuration
public class ShiroConfig {
/*無需再另外設定filter*/

    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.err.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必須設定 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //新增shiro內建過濾器
        /* 具體參考shiro的enum DefaultFilter
         * anon:表示可以匿名使用。
           authc:表示需要認證(登入)才能使用,沒有引數
           roles:引數可以寫多個,多個時必須加上引號,並且引數之間用逗號分割,當有多個引數時,例如admins/user/**=roles["admin,guest"],每個引數通過才算通過,相當於hasAllRoles()方法。
           perms:引數可以寫多個,多個時必須加上引號,並且引數之間用逗號分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],當有多個引數時必須每個引數都通過才通過,想當於isPermitedAll()方法。
           rest:根據請求的方法,相當於/admins/user/**=perms[user:method] ,其中method為post,get,delete等。
           port:當請求的url的埠不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置裡port的埠,queryString是你訪問的url裡的?後面的引數。
           authcBasic:沒有引數表示httpBasic認證
           ssl:表示安全的url請求,協議為https
           user:當登入操作時不做檢查
         */

        Map<String, String> fMap = new HashMap<String, String>();
        /*user:必須登入才能訪問的頁面 效力同@RequiresUser*/
//        fMap.put("/users", "user");

        /*authc:表示需要認證(登入)才能使用,這裡不能包含anon的部分,否則anno的設定無效,導致靜態資源無法訪問*/
//        fMap.put("/admin", "authc");
//        fMap.put("/user", "authc");

        /*anon:表示可以匿名使用*/
        /*靜態資源*/
        fMap.put("/assets/**", "anon");
        fMap.put("/css/**", "anon");
        fMap.put("/images/**", "anon");
        fMap.put("/img/**", "anon");
        fMap.put("/js/**", "anon");

        /*登入介面*/
        fMap.put("/getGifCode", "anon");//驗證碼
        fMap.put("/ajaxLogin", "anon");//ajax登入介面
        fMap.put("/main", "anon");//for test
        fMap.put("/logout", "anon");
        fMap.put("/login", "anon");
//        fMap.put("/*", "anon");
//        fMap.put("/**/*", "anon");

        /*設定可以登入的人員 可用註解配置*/
//        fMap.put("/admin", "perms[admin:*]");
//        fMap.put("/add", "perms[*:add]");
//        fMap.put("/user", "perms[user:*]");

        /*設定可以登入的角色,可用註解配置,效力同@RequiresRoles("admin")*/
//        fMap.put("/user", "roles[user,admin]");
//        fMap.put("/admin", "roles[admin]");


//        Shiro攔截器工廠類注入
        shiroFilterFactoryBean.setFilterChainDefinitionMap(fMap);

        //被攔截返回登入頁面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登入成功後要跳轉的連結
        shiroFilterFactoryBean.setSuccessUrl("/main");
        //未授權介面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");

        return shiroFilterFactoryBean;
    }


    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //設定realm.,必須是@Bean
        securityManager.setRealm(myShiroRealm());
        //注入快取管理器;
        securityManager.setCacheManager(ehCacheManager());//這個如果執行多次,也是同樣的一個物件;
        securityManager.setRememberMeManager(rememberMeManager());

        /*把securityManager注入SecurityUtils*/
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    /**
     * 身份認證realm;
     * (這個需要自己寫,賬號密碼校驗;許可權等)
     *
     * @return
     */
    @Bean
    public UserAuthenticationRealm myShiroRealm() {
        UserAuthenticationRealm myShiroRealm = new UserAuthenticationRealm();
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());;
        return myShiroRealm;
    }

    /**
     * shiro快取管理器;
     * 需要注入對應的其它的實體類中:
     * 1、安全管理器:securityManager
     * 可見securityManager是整個shiro的核心;
     *
     * @return
     */
    @Bean
    public EhCacheManager ehCacheManager() {
        System.out.println("ShiroConfiguration.getEhCacheManager()");
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
        return cacheManager;
    }


    /**
     * cookie管理物件;
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }

    /**
     * cookie物件;
     *
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        System.out.println("ShiroConfiguration.rememberMeCookie()");
        //這個引數是cookie的名稱,對應前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 記住我cookie生效時間30天 ,單位秒;-->
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }




    /**
     * 憑證匹配器
     * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了
     * 所以我們需要修改下doGetAuthenticationInfo中的程式碼;
     * )
     *
     * @return
     */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher(){
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

		hashedCredentialsMatcher.setHashAlgorithmName(Md5Hash.ALGORITHM_NAME);//雜湊演算法:這裡使用MD5演算法;
		hashedCredentialsMatcher.setHashIterations(1);//雜湊的次數,比如雜湊兩次,相當於 md5(md5(""));

//        hashedCredentialsMatcher.setHashAlgorithmName("SHA-256");
//        hashedCredentialsMatcher.setHashIterations(1024);

//        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false); // 這一行決定hex還是base64,true=Hex,false=base64
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); // 這一行決定hex還是base64,true=Hex,false=base64
        hashedCredentialsMatcher.setHashSalted(false);

		return hashedCredentialsMatcher;
	}

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
//	    <!-- 配置是否啟動過慮器的init/destory方法 -->
//        <!-- 保證實現了Shiro內部lifecycle函式的bean執行 -->
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 開啟shiro aop註解支援.
     * 使用代理方式;所以需要開啟程式碼支援;
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}

ehcache-shiro.xml檔案裡的配置

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />
    <!-- 登入記錄快取鎖定10分鐘 -->
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
</ehcache>

2. relm的加密,資料庫密碼必須加密,加密方式在Config裡配置要注意到是,實現AuthorizingRealm類的登入認證裡要和自己加密方式搭配,以下是用Hex,不加鹽的情況

SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userInfo.getUserId(),
      userInfo.getPassword(), userInfo.getUserName());

3. 註解和ShiroFilterFactoryBean的config都可以配置許可權,個人覺得註解好用一點

4.  實現AuthorizingRealm的類裡有兩個方法doGetAuthenticationInfo是登入驗證,在SecurityUtils.getSubject().login(token);的時候呼叫,另外一個doGetAuthorizationInfo是有驗證要求的時候才會執行,例如加了@RequiresAuthentication註解的Controller  

如果要下載完整的專案,請到********(下載後先在本地建立test_shiro資料庫,然後執行resources的sql包下的5個sql檔案),啟動專案即可看到網頁並且測試