欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中Shiro安全框架的權(quán)限管理

 更新時(shí)間:2023年08月18日 09:05:41   作者:ycfxhsw  
這篇文章主要介紹了Java中Shiro安全框架的權(quán)限管理,Apache?Shiro是Java的一個(gè)安全框架,Shiro可以非常容易的開發(fā)出足夠好的應(yīng)用,其不僅可以用在JavaSE環(huán)境,也可以用在JavaEE環(huán)境,需要的朋友可以參考下

前言

Apache Shiro是Java的一個(gè)安全框架。

目前,使用Apache Shiro的人越來(lái)越多,相比Spring Security而言相當(dāng)簡(jiǎn)單, 可能沒有Spring Security做的功能強(qiáng)大,但是在實(shí)際工作時(shí)可能并不需要那么復(fù)雜的東西, 所以使用小而簡(jiǎn)單的Shiro就足夠了。

對(duì)于它倆到底哪個(gè)好,這個(gè)不必糾結(jié),能更簡(jiǎn)單的解決項(xiàng)目問題就好了。

本文只介紹基本的Shiro使用,不會(huì)過多分析源碼等,重在使用。

Shiro架構(gòu)

Shiro可以非常容易的開發(fā)出足夠好的應(yīng)用,其不僅可以用在JavaSE環(huán)境,也可以用在JavaEE環(huán)境。

Shiro可以幫助我們完成:認(rèn)證、授權(quán)、加密、會(huì)話管理、與Web集成、緩存等。

Shiro的API非常簡(jiǎn)單,其基本功能點(diǎn)如下圖所示:

在這里插入圖片描述

  • Authentication:身份認(rèn)證/登錄,驗(yàn)證用戶是不是擁有相應(yīng)的身份;
  • Authorization:授權(quán),即權(quán)限驗(yàn)證,驗(yàn)證某個(gè)已認(rèn)證的用戶是否擁有某個(gè)權(quán)限;即判斷用戶是否能做事情,常見的如:驗(yàn)證某個(gè)用戶是否擁有某個(gè)角色。或者細(xì)粒度的驗(yàn)證某個(gè)用戶對(duì)某個(gè)資源是否具有某個(gè)權(quán)限;
  • Session Manager:會(huì)話管理,即用戶登錄后就是一次會(huì)話,在沒有退出之前,它的所有信息都在會(huì)話中;會(huì)話可以是普通JavaSE環(huán)境的,也可以是如Web環(huán)境的;
  • Cryptography:加密,保護(hù)數(shù)據(jù)的安全性,如密碼加密存儲(chǔ)到數(shù)據(jù)庫(kù),而不是明文存儲(chǔ);
  • Web Support:Web支持,可以非常容易的集成到Web環(huán)境;
  • Caching:緩存,比如用戶登錄后,其用戶信息、擁有的角色/權(quán)限不必每次去查,這樣可以提高效率;
  • Concurrency:shiro支持多線程應(yīng)用的并發(fā)驗(yàn)證,即如在一個(gè)線程中開啟另一個(gè)線程,能把權(quán)限自動(dòng)傳播過去;
  • Testing:提供測(cè)試支持;
  • Run As:允許一個(gè)用戶假裝為另一個(gè)用戶(如果他們?cè)试S)的身份進(jìn)行訪問;
  • Remember Me:記住我,這個(gè)是非常常見的功能,即一次登錄后,下次再來(lái)的話不用登錄了。

記住一點(diǎn),Shiro不會(huì)去維護(hù)用戶、維護(hù)權(quán)限,這些需要我們自己去設(shè)計(jì)/提供,然后通過相應(yīng)的接口注入給Shiro即可。

接下來(lái)我們分別從外部和內(nèi)部來(lái)看看Shiro的架構(gòu),對(duì)于一個(gè)好的框架,從外部來(lái)看應(yīng)該具有非常簡(jiǎn)單易于使用的API, 且API契約明確;從內(nèi)部來(lái)看的話,其應(yīng)該有一個(gè)可擴(kuò)展的架構(gòu),即非常容易插入用戶自定義實(shí)現(xiàn),因?yàn)槿魏慰蚣芏疾荒軡M足所有需求。

在這里插入圖片描述

可以看到:應(yīng)用代碼直接交互的對(duì)象是Subject,也就是說(shuō)Shiro的對(duì)外API核心就是 Subject。

  • Subject:主體,代表了當(dāng)前”用戶”,這個(gè)用戶不一定是一個(gè)具體的人,與當(dāng)前應(yīng)用交互的任何東西都是Subject,如網(wǎng)絡(luò)爬蟲,機(jī)器人等;即一個(gè)抽象概念;所有Subject都綁定到SecurityManager,與Subject的所有交互都會(huì)委托給SecurityManager;可以把Subject認(rèn)為是一個(gè)門面;SecurityManager才是實(shí)際的執(zhí)行者;
  • SecurityManager:安全管理器;即所有與安全有關(guān)的操作都會(huì)與SecurityManager交互;且它管理著所有Subject;可以看出它是Shiro的核心,它負(fù)責(zé)與后邊介紹的其他組件進(jìn)行交互,如果學(xué)習(xí)過SpringMVC,你可以把它看成DispatcherServlet前端控制器;
  • Realm:域,Shiro從從Realm獲取安全數(shù)據(jù)(如用戶、角色、權(quán)限),就是說(shuō)SecurityManager要驗(yàn)證用戶身份,那么它需要從Realm獲取相應(yīng)的用戶進(jìn)行比較以確定用戶身份是否合法;也需要從Realm得到用戶相應(yīng)的角色/權(quán)限進(jìn)行驗(yàn)證用戶是否能進(jìn)行操作;可以把Realm看成DataSource,即安全數(shù)據(jù)源。

也就是說(shuō)對(duì)于我們而言,最簡(jiǎn)單的一個(gè)Shiro應(yīng)用:

1、應(yīng)用代碼通過Subject 來(lái)進(jìn)行認(rèn)證和授權(quán),而 Subject 又委托給 SecurityManager;

2、我們需要給Shiro的 SecurityManager 注入 Realm,從而讓 SecurityManager 能得到合法的用戶及其權(quán)限進(jìn)行判斷。

從以上也可以看出,Shiro不提供維護(hù)用戶/權(quán)限,而是通過Realm讓開發(fā)人員自己注入。

接下來(lái)我們來(lái)從Shiro內(nèi)部來(lái)看下Shiro的架構(gòu),如下圖所示:

在這里插入圖片描述

  • Subject:主體,可以看到主體可以是任何可以與應(yīng)用交互的”用戶”;
  • SecurityManager:相當(dāng)于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心臟;所有具體的交互都通過SecurityManager進(jìn)行控制;它管理著所有Subject、且負(fù)責(zé)進(jìn)行認(rèn)證和授權(quán)、及會(huì)話、緩存的管理。
  • Authenticator:認(rèn)證器,負(fù)責(zé)主體認(rèn)證的,這是一個(gè)擴(kuò)展點(diǎn),如果用戶覺得Shiro默認(rèn)的不好,可以自定義實(shí)現(xiàn);其需要認(rèn)證策略(Authentication Strategy),即什么情況下算用戶認(rèn)證通過了;
  • Authorizer:授權(quán)器,或者訪問控制器,用來(lái)決定主體是否有權(quán)限進(jìn)行相應(yīng)的操作;即控制著用戶能訪問應(yīng)用中的哪些功能;
  • Realm:可以有1個(gè)或多個(gè)Realm,可以認(rèn)為是安全實(shí)體數(shù)據(jù)源,即用于獲取安全實(shí)體的;可以是JDBC實(shí)現(xiàn),也可以是LDAP實(shí)現(xiàn),或者內(nèi)存實(shí)現(xiàn)等等;由用戶提供;注意:Shiro不知道你的用戶/權(quán)限存儲(chǔ)在哪及以何種格式存儲(chǔ);所以我們一般在應(yīng)用中都需要實(shí)現(xiàn)自己的Realm;
  • SessionManager:如果寫過Servlet就應(yīng)該知道Session的概念,Session呢需要有人去管理它的生命周期,這個(gè)組件就是SessionManager;而Shiro并不僅僅可以用在Web環(huán)境,也可以用在如普通的JavaSE環(huán)境、EJB等環(huán)境;所有呢,Shiro就抽象了一個(gè)自己的Session來(lái)管理主體與應(yīng)用之間交互的數(shù)據(jù);這樣的話,比如我們?cè)赪eb環(huán)境用,剛開始是一臺(tái)Web服務(wù)器;接著又上了臺(tái)EJB服務(wù)器;這時(shí)想把兩臺(tái)服務(wù)器的會(huì)話數(shù)據(jù)放到一個(gè)地方,這個(gè)時(shí)候就可以實(shí)現(xiàn)自己的分布式會(huì)話(如把數(shù)據(jù)放到Memcached服務(wù)器);
  • SessionDAO:DAO大家都用過,數(shù)據(jù)訪問對(duì)象,用于會(huì)話的CRUD,比如我們想把Session保存到數(shù)據(jù)庫(kù),那么可以實(shí)現(xiàn)自己的SessionDAO,通過如JDBC寫到數(shù)據(jù)庫(kù);比如想把Session放到Memcached中,可以實(shí)現(xiàn)自己的Memcached SessionDAO,另外SessionDAO中可以使用Cache進(jìn)行緩存,以提高性能;
  • CacheManager:緩存控制器,來(lái)管理如用戶、角色、權(quán)限等的緩存的;因?yàn)檫@些數(shù)據(jù)基本上很少去改變,放到緩存中后可以提高訪問的性能
  • Cryptography:密碼模塊,Shiro提高了一些常見的加密組件用于如密碼加密/解密的。

參考 Shiro官網(wǎng)

SpringBoot 集成

實(shí)現(xiàn)一個(gè)最常見的驗(yàn)證碼登錄、記住我、權(quán)限自定義(or),緩存功能的SpringBoot應(yīng)用,模板使用Thymeleaf3

Maven依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- thymeleaf模板中shiro標(biāo)簽-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
<!-- shiro 權(quán)限控制 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-api</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!-- shiro ehcache (shiro緩存)-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.4.0</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-api</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!--驗(yàn)證碼框架-->
<dependency>
    <groupId>com.github.axet</groupId>
    <artifactId>kaptcha</artifactId>
    <version>0.0.9</version>
</dependency>

新建一個(gè)配置類ShiroConfig.java,內(nèi)容如下:

/**
 * Description  : Apache Shiro 核心通過 Filter 來(lái)實(shí)現(xiàn),就好像SpringMvc 通過DispachServlet 來(lái)主控制一樣。
 * 既然是使用 Filter 一般也就能猜到,是通過URL規(guī)則來(lái)進(jìn)行過濾和權(quán)限校驗(yàn),所以我們需要定義一系列關(guān)于URL的規(guī)則和訪問權(quán)限。
 */
@Configuration
@Order(1)
public class ShiroConfig {
    //配置kaptcha圖片驗(yàn)證碼框架提供的Servlet,,這是個(gè)坑,很多人忘記注冊(cè)(注意)
    @Bean
    public ServletRegistrationBean kaptchaServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new KaptchaServlet(), "/kaptcha.jpg");
        servlet.addInitParameter(Constants.KAPTCHA_SESSION_CONFIG_KEY, Constants.KAPTCHA_SESSION_KEY);//session key
        servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "50");//字體大小
        servlet.addInitParameter(Constants.KAPTCHA_BORDER, "no");
        servlet.addInitParameter(Constants.KAPTCHA_BORDER_COLOR, "105,179,90");
        servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "45");
        servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
        servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "宋體,楷體,微軟雅黑");
        servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
        servlet.addInitParameter(Constants.KAPTCHA_IMAGE_WIDTH, "125");
        servlet.addInitParameter(Constants.KAPTCHA_IMAGE_HEIGHT, "60");
        //可以設(shè)置很多屬性,具體看com.google.code.kaptcha.Constants
//		kaptcha.border  是否有邊框  默認(rèn)為true  我們可以自己設(shè)置yes,no
//		kaptcha.border.color   邊框顏色   默認(rèn)為Color.BLACK
//		kaptcha.border.thickness  邊框粗細(xì)度  默認(rèn)為1
//		kaptcha.producer.impl   驗(yàn)證碼生成器  默認(rèn)為DefaultKaptcha
//		kaptcha.textproducer.impl   驗(yàn)證碼文本生成器  默認(rèn)為DefaultTextCreator
//		kaptcha.textproducer.char.string   驗(yàn)證碼文本字符內(nèi)容范圍  默認(rèn)為abcde2345678gfynmnpwx
//		kaptcha.textproducer.char.length   驗(yàn)證碼文本字符長(zhǎng)度  默認(rèn)為5
//		kaptcha.textproducer.font.names    驗(yàn)證碼文本字體樣式  默認(rèn)為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
//		kaptcha.textproducer.font.size   驗(yàn)證碼文本字符大小  默認(rèn)為40
//		kaptcha.textproducer.font.color  驗(yàn)證碼文本字符顏色  默認(rèn)為Color.BLACK
//		kaptcha.textproducer.char.space  驗(yàn)證碼文本字符間距  默認(rèn)為2
//		kaptcha.noise.impl    驗(yàn)證碼噪點(diǎn)生成對(duì)象  默認(rèn)為DefaultNoise
//		kaptcha.noise.color   驗(yàn)證碼噪點(diǎn)顏色   默認(rèn)為Color.BLACK
//		kaptcha.obscurificator.impl   驗(yàn)證碼樣式引擎  默認(rèn)為WaterRipple
//		kaptcha.word.impl   驗(yàn)證碼文本字符渲染   默認(rèn)為DefaultWordRenderer
//		kaptcha.background.impl   驗(yàn)證碼背景生成器   默認(rèn)為DefaultBackground
//		kaptcha.background.clear.from   驗(yàn)證碼背景顏色漸進(jìn)   默認(rèn)為Color.LIGHT_GRAY
//		kaptcha.background.clear.to   驗(yàn)證碼背景顏色漸進(jìn)   默認(rèn)為Color.WHITE
//		kaptcha.image.width   驗(yàn)證碼圖片寬度  默認(rèn)為200
//		kaptcha.image.height  驗(yàn)證碼圖片高度  默認(rèn)為50
        return servlet;
    }
    //注入異常處理類
    @Bean
    public MyExceptionResolver myExceptionResolver() {
        return new MyExceptionResolver();
    }
    /**
     * ShiroFilterFactoryBean 處理攔截資源文件問題。
     * 注意:?jiǎn)为?dú)一個(gè)ShiroFilterFactoryBean配置是或報(bào)錯(cuò)的,以為在
     * 初始化ShiroFilterFactoryBean的時(shí)候需要注入:SecurityManager Filter Chain定義說(shuō)明
     * 1、一個(gè)URL可以配置多個(gè)Filter,使用逗號(hào)分隔
     * 2、當(dāng)設(shè)置多個(gè)過濾器時(shí),全部驗(yàn)證通過,才視為通過
     * 3、部分過濾器可指定參數(shù),如perms,roles
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必須設(shè)置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //驗(yàn)證碼過濾器
        Map<String, Filter> filtersMap = shiroFilterFactoryBean.getFilters();
        KaptchaFilter kaptchaFilter = new KaptchaFilter();
        filtersMap.put("kaptchaFilter", kaptchaFilter);
        //實(shí)現(xiàn)自己規(guī)則roles,這是為了實(shí)現(xiàn)or的效果
        //RoleFilter roleFilter = new RoleFilter();
        //filtersMap.put("roles", roleFilter);
        shiroFilterFactoryBean.setFilters(filtersMap);
        // 攔截器
        //rest:比如/admins/user/**=rest[user],根據(jù)請(qǐng)求的方法,相當(dāng)于/admins/user/**=perms[user:method] ,其中method為post,get,delete等。
        //port:比如/admins/user/**=port[8081],當(dāng)請(qǐng)求的url的端口不是8081是跳轉(zhuǎn)到schemal://serverName:8081?queryString,其中schmal是協(xié)議http或https等,serverName是你訪問的host,8081是url配置里port的端口,queryString是你訪問的url里的?后面的參數(shù)。
        //perms:比如/admins/user/**=perms[user:add:*],perms參數(shù)可以寫多個(gè),多個(gè)時(shí)必須加上引號(hào),并且參數(shù)之間用逗號(hào)分割,比如/admins/user/**=perms["user:add:*,user:modify:*"],當(dāng)有多個(gè)參數(shù)時(shí)必須每個(gè)參數(shù)都通過才通過,想當(dāng)于isPermitedAll()方法。
        //roles:比如/admins/user/**=roles[admin],參數(shù)可以寫多個(gè),多個(gè)時(shí)必須加上引號(hào),并且參數(shù)之間用逗號(hào)分割,當(dāng)有多個(gè)參數(shù)時(shí),比如/admins/user/**=roles["admin,guest"],每個(gè)參數(shù)通過才算通過,相當(dāng)于hasAllRoles()方法。//要實(shí)現(xiàn)or的效果看http://zgzty.blog.163.com/blog/static/83831226201302983358670/
        //anon:比如/admins/**=anon 沒有參數(shù),表示可以匿名使用。
        //authc:比如/admins/user/**=authc表示需要認(rèn)證才能使用,沒有參數(shù)
        //authcBasic:比如/admins/user/**=authcBasic沒有參數(shù)表示httpBasic認(rèn)證
        //ssl:比如/admins/user/**=ssl沒有參數(shù),表示安全的url請(qǐng)求,協(xié)議為https
        //user:比如/admins/user/**=user沒有參數(shù)表示必須存在用戶,當(dāng)?shù)侨氩僮鲿r(shí)不做檢查
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置退出過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實(shí)現(xiàn)了
        filterChainDefinitionMap.put("/logout", "logout");
        //配置記住我或認(rèn)證通過可以訪問的地址
        filterChainDefinitionMap.put("/index", "user");
        filterChainDefinitionMap.put("/", "user");
        filterChainDefinitionMap.put("/login", "kaptchaFilter");
        // <!-- 過濾鏈定義,從上向下順序執(zhí)行,一般將 /**放在最為下邊 -->:這是一個(gè)坑呢,一不小心代碼就不好使了;
        //這段是配合 actuator框架使用的,配置相應(yīng)的角色才能訪問
        // filterChainDefinitionMap.put("/health", "roles[aix]");//服務(wù)器健康狀況頁(yè)面
        // filterChainDefinitionMap.put("/info", "roles[aix]");//服務(wù)器信息頁(yè)面
        // filterChainDefinitionMap.put("/env", "roles[aix]");//應(yīng)用程序的環(huán)境變量
        // filterChainDefinitionMap.put("/metrics", "roles[aix]");
        // filterChainDefinitionMap.put("/configprops", "roles[aix]");
        //開放的靜態(tài)資源
        filterChainDefinitionMap.put("/favicon.ico", "anon");//網(wǎng)站圖標(biāo)
        filterChainDefinitionMap.put("/static/**", "anon");//配置static文件下資源能被訪問的,這是個(gè)例子
        filterChainDefinitionMap.put("/kaptcha.jpg", "anon");//圖片驗(yàn)證碼(kaptcha框架)
        filterChainDefinitionMap.put("/api/v1/**", "anon");//API接口
        // swagger接口文檔
        filterChainDefinitionMap.put("/v2/api-docs", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/doc.html", "anon");
        // 其他的
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁(yè)面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登錄成功后要跳轉(zhuǎn)的鏈接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授權(quán)界面,不生效(詳情原因看MyExceptionResolver)
        shiroFilterFactoryBean.setUnauthorizedUrl("/errorView/403_error.html");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 設(shè)置realm.
        securityManager.setRealm(myShiroRealm());
        //注入緩存管理器
        //這個(gè)如果執(zhí)行多次,也是同樣的一個(gè)對(duì)象;
        securityManager.setCacheManager(ehCacheManager());
        //注入記住我管理器;
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }
    /**
     * 身份認(rèn)證realm; (這個(gè)需要自己寫,賬號(hào)密碼校驗(yàn);權(quán)限等)
     */
    @Bean
    public MyShiroRealm myShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }
    /**
     * 憑證匹配器 (由于我們的密碼校驗(yàn)交給Shiro的SimpleAuthenticationInfo進(jìn)行處理了
     * 所以我們需要修改下doGetAuthenticationInfo中的代碼; @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法:這里使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列的次數(shù),比如散列兩次,相當(dāng)于md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        //表示是否存儲(chǔ)散列后的密碼為16進(jìn)制,需要和生成密碼時(shí)的一樣,默認(rèn)是base64;
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }
    /**
     * 開啟shiro aop注解支持. 使用代理方式; 所以需要開啟代碼支持;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * shiro緩存管理器;
     * 需要注入對(duì)應(yīng)的其它的實(shí)體類中:
     * 安全管理器:securityManager
     * 可見securityManager是整個(gè)shiro的核心;
     * @return
     */
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return cacheManager;
    }
    /**
     * cookie對(duì)象;
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        //System.out.println("ShiroConfiguration.rememberMeCookie()");
        //這個(gè)參數(shù)是cookie的名稱,對(duì)應(yīng)前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 記住我cookie生效時(shí)間30天 ,單位秒;-->
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }
    /**
     * cookie管理對(duì)象;
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        //System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager defaultWebSessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(18000000);
        // url中是否顯示session Id
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        // 刪除失效的session
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionValidationInterval(18000000);
        sessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler());
        //設(shè)置SessionIdCookie 導(dǎo)致認(rèn)證不成功,不從新設(shè)置新的cookie,從sessionManager獲取sessionIdCookie
        //sessionManager.setSessionIdCookie(simpleIdCookie());
        sessionManager.getSessionIdCookie().setName("session-z-id");
        sessionManager.getSessionIdCookie().setPath("/");
        sessionManager.getSessionIdCookie().setMaxAge(60 * 60 * 24 * 7);
        return sessionManager;
    }
    @Bean(name = "sessionValidationScheduler")
    public ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() {
        ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();
        scheduler.setInterval(900000);
        return scheduler;
    }
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

代碼中解釋都非常清楚。

MyShiroRealm.java的內(nèi)容如下:

/**
 * Description  : 身份校驗(yàn)核心類
 */
public class MyShiroRealm extends AuthorizingRealm {
    private static final Logger _logger = LoggerFactory.getLogger(MyShiroRealm.class);
    @Autowired
    ManagerInfoService managerInfoService;
    /**
     * 認(rèn)證信息.(身份驗(yàn)證)
     * Authentication 是用來(lái)驗(yàn)證用戶身份
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        _logger.info("MyShiroRealm.doGetAuthenticationInfo()");
        //獲取用戶的輸入的賬號(hào).
        String username = (String) token.getPrincipal();
        //_logger.info("用戶的賬號(hào):"+username);
        //通過username從數(shù)據(jù)庫(kù)中查找 ManagerInfo對(duì)象
        //實(shí)際項(xiàng)目中,這里可以根據(jù)實(shí)際情況做緩存,如果不做,Shiro自己也是有時(shí)間間隔機(jī)制,2分鐘內(nèi)不會(huì)重復(fù)執(zhí)行該方法
        ManagerInfo managerInfo = managerInfoService.findByUsername(username);
        if (managerInfo == null) {
            return null;
        }
        //交給AuthenticatingRealm使用CredentialsMatcher進(jìn)行密碼匹配,如果覺得人家的不好可以自定義實(shí)現(xiàn)
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                managerInfo, //用戶
                managerInfo.getPassword(), //密碼
                ByteSource.Util.bytes(managerInfo.getCredentialsSalt()),//salt=username+salt
                getName()  //realm name
        );
        //明文: 若存在,將此用戶存放到登錄認(rèn)證info中,無(wú)需自己做密碼對(duì)比,Shiro會(huì)為我們進(jìn)行密碼對(duì)比校驗(yàn)
//        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
//                managerInfo, //用戶名
//                managerInfo.getPassword(), //密碼
//                getName()  //realm name
//        );
        return authenticationInfo;
    }
    /**
     * 此方法調(diào)用hasRole,hasPermission的時(shí)候才會(huì)進(jìn)行回調(diào).
     * <p>
     * 權(quán)限信息.(授權(quán)):
     * 1、如果用戶正常退出,緩存自動(dòng)清空;
     * 2、如果用戶非正常退出,緩存自動(dòng)清空;
     * 3、如果我們修改了用戶的權(quán)限,而用戶不退出系統(tǒng),修改的權(quán)限無(wú)法立即生效。
     * (需要手動(dòng)編程進(jìn)行實(shí)現(xiàn);放在service進(jìn)行調(diào)用)
     * 在權(quán)限修改后調(diào)用realm中的方法,realm已經(jīng)由spring管理,所以從spring中獲取realm實(shí)例,調(diào)用clearCached方法;
     * :Authorization 是授權(quán)訪問控制,用于對(duì)用戶進(jìn)行的操作授權(quán),證明該用戶是否允許進(jìn)行當(dāng)前操作,如訪問某個(gè)鏈接,某個(gè)資源文件等。
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        /*
         * 當(dāng)沒有使用緩存的時(shí)候,不斷刷新頁(yè)面的話,這個(gè)代碼會(huì)不斷執(zhí)行,
         * 當(dāng)其實(shí)沒有必要每次都重新設(shè)置權(quán)限信息,所以我們需要放到緩存中進(jìn)行管理;
         * 當(dāng)放到緩存中時(shí),這樣的話,doGetAuthorizationInfo就只會(huì)執(zhí)行一次了,
         * 緩存過期之后會(huì)再次執(zhí)行。
         */
        _logger.info("權(quán)限配置-->MyShiroRealm.doGetAuthorizationInfo()");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        ManagerInfo managerInfo = (ManagerInfo) principals.getPrimaryPrincipal();
        //設(shè)置相應(yīng)角色的權(quán)限信息
        for (SysRole role : managerInfo.getRoles()) {
            //設(shè)置角色
            authorizationInfo.addRole(role.getRole());
            for (Permission p : role.getPermissions()) {
                //設(shè)置權(quán)限
                authorizationInfo.addStringPermission(p.getPermission());
            }
        }
        return authorizationInfo;
    }
    /**
     * 設(shè)置認(rèn)證加密方式
     */
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher();
        md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.HASH_ALGORITHM_NAME);
        md5CredentialsMatcher.setHashIterations(ShiroKit.HASH_ITERATIONS);
        super.setCredentialsMatcher(md5CredentialsMatcher);
    }
}

自定義異常處理類MyExceptionResolver.java :

public class MyExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        //如果是shiro無(wú)權(quán)操作,因?yàn)閟hiro 在操作auno等一部分不進(jìn)行轉(zhuǎn)發(fā)至無(wú)權(quán)限url
        if (ex instanceof UnauthorizedException) {
            return new ModelAndView("error/shiro_403");
        }
        return null;
    }
}

接口控制

配置好了Shiro后就可以通過注解方式來(lái)限制某些接口調(diào)用需要相應(yīng)的角色或權(quán)限了:

@RequestMapping(value = "/index")
@RequiresRoles("admin")
public String index(HttpServletRequest request, Model model) {
    _logger.info("進(jìn)入項(xiàng)目管理首頁(yè)...");
}

其他的注解請(qǐng)參考官網(wǎng)的 Shiro注解

Thymeleaf的shiro標(biāo)簽

可以在Thymeleaf模板中使用shiro的權(quán)限標(biāo)簽來(lái)控制某些菜單或按鈕是否顯示。

maven中添加依賴,這個(gè)前面已經(jīng)有了:

<!-- thymeleaf模板中shiro標(biāo)簽-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

在 ShiroConfig 中添加一個(gè)Bean配置:

@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect() {
    return new ShiroDialect();
}

在html頁(yè)面添加如下內(nèi)容:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

添加完后在html頁(yè)面調(diào)用如下:

<!-- 認(rèn)證通過或已記住的用戶。 -->    
<p shiro:user="">    
   Welcome back John! Not John? Click <a href="login.html">here</a> to login.    
</p>    
<p shiro:notAuthenticated="">  
    未身份驗(yàn)證(包括記住我)  
</p>   
<p shiro:guest=""><span style="white-space:pre;"> </span>Please <a href="login.html">login5555</a> </p>

第二種:

<shiro:guest>
    <a>登錄</a> <a>注冊(cè)</a>
</shiro:guest>
<shiro:user>
    歡迎<shiro:principal property="name"/>
</shiro:user>

權(quán)限數(shù)據(jù)庫(kù)設(shè)計(jì)

一般來(lái)講都會(huì)講用戶的角色和權(quán)限保存到數(shù)據(jù)庫(kù),這里設(shè)計(jì)一種最通用的模型, 使用RBAC(Role-Based Access Control,基于角色的訪問控制)模型設(shè)計(jì)用戶,角色和權(quán)限間的關(guān)系。

簡(jiǎn)單地說(shuō), 一個(gè)用戶擁有若干角色,每一個(gè)角色擁有若干權(quán)限。這樣,就構(gòu)造成”用戶-角色-權(quán)限”的授權(quán)模型。

在這種模型中,用戶與角色之間,角色與權(quán)限之間,一般者是多對(duì)多的關(guān)系。如下圖所示:

在這里插入圖片描述

我們通過MyBatis實(shí)現(xiàn)ManagerInfoService,

/**
 * 后臺(tái)用戶管理
 */
@Service
public class ManagerInfoService {
    @Resource
    private ManagerInfoDao managerInfoDao;
    public ManagerInfo findByUsername(String username) {
        return managerInfoDao.findByUsername(username);
    }
}

然后對(duì)應(yīng)的ManagerInfoDao.xml如下:

<resultMap id="ManagerInfoMap" type="managerInfo">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="name" column="name"/>
    <result property="password" column="password"/>
    <result property="salt" column="salt"/>
    <result property="state" column="state"/>
    <collection property="roles" ofType="sysRole">
        <id property="id" column="role_id"/>
        <result property="role" column="role_role"/>
        <collection property="permissions" ofType="permission">
            <id property="id" column="perm_id"/>
            <result property="permission" column="perm_permission"/>
        </collection>
    </collection>
</resultMap>
<select id="findByUsername" resultMap="ManagerInfoMap">
    SELECT DISTINCT
        A.id AS id,
        A.username AS username,
        A.name AS name,
        A.password AS password,
        A.salt AS salt,
        A.state AS state,
        C.id AS role_id,
        C.role AS role_role,
        E.id AS perm_id,
        E.permission AS perm_permission
    FROM t_manager A
        LEFT JOIN t_manager_role B ON A.id=B.manager_id
        LEFT JOIN t_role C ON B.role_id=C.id
        LEFT JOIN t_role_permission D ON C.id=D.role_id
        LEFT JOIN t_permission E ON D.permission_Id=E.id
    WHERE username=#{username}
    LIMIT 1
</select>

ManagerInfo.java如下:

public class ManagerInfo extends Manager implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 一個(gè)管理員具有多個(gè)角色
     */
    private List<SysRole> roles;// 一個(gè)用戶具有多個(gè)角色
}

到此這篇關(guān)于Java中Shiro安全框架的權(quán)限管理的文章就介紹到這了,更多相關(guān)Shiro框架的權(quán)限管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 中的FileReader和FileWriter源碼分析_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java 中的FileReader和FileWriter源碼分析_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    本文給大家分享一段示例程序,通過示例代碼可以看出FileReader是基于InputStreamReader實(shí)現(xiàn)的,FileWriter是基于OutputStreamWriter實(shí)現(xiàn)的,具體程序代碼大家通過本文了解下吧
    2017-05-05
  • SpringBoot3實(shí)戰(zhàn)教程之實(shí)現(xiàn)接口簽名驗(yàn)證功能

    SpringBoot3實(shí)戰(zhàn)教程之實(shí)現(xiàn)接口簽名驗(yàn)證功能

    接口簽名是一種重要的安全機(jī)制,用于確保 API 請(qǐng)求的真實(shí)性、數(shù)據(jù)的完整性以及防止重放攻擊,這篇文章主要介紹了SpringBoot3實(shí)戰(zhàn)教程之實(shí)現(xiàn)接口簽名驗(yàn)證功能,需要的朋友可以參考下
    2025-04-04
  • 3分鐘純 Java 注解搭個(gè)管理系統(tǒng)的示例代碼

    3分鐘純 Java 注解搭個(gè)管理系統(tǒng)的示例代碼

    這篇文章主要介紹了3分鐘純 Java 注解搭個(gè)管理系統(tǒng)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 深入理解Mybatis中的resultType和resultMap

    深入理解Mybatis中的resultType和resultMap

    這篇文章給大家介紹了mybatis中的resultType和resultMap的用法實(shí)例講解,MyBatis中在查詢進(jìn)行select映射的時(shí)候,返回類型可以用resultType,也可以用resultMap,至于兩種用法區(qū)別,通過本文一起學(xué)習(xí)吧
    2016-09-09
  • 淺談線性表的原理及簡(jiǎn)單實(shí)現(xiàn)方法

    淺談線性表的原理及簡(jiǎn)單實(shí)現(xiàn)方法

    下面小編就為大家?guī)?lái)一篇淺談線性表的原理及簡(jiǎn)單實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-06-06
  • 實(shí)現(xiàn) Java 本地緩存的方法解析

    實(shí)現(xiàn) Java 本地緩存的方法解析

    這篇文章主要介紹了實(shí)現(xiàn) Java 本地緩存的方法解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Java pom.xml parent引用報(bào)錯(cuò)問題解決方案

    Java pom.xml parent引用報(bào)錯(cuò)問題解決方案

    這篇文章主要介紹了Java pom.xml parent引用報(bào)錯(cuò)問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java 異常的知識(shí)整理

    Java 異常的知識(shí)整理

    這篇文章主要介紹了Java 異常的知識(shí)整理的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • 淺析Java設(shè)計(jì)模式編程中的單例模式和簡(jiǎn)單工廠模式

    淺析Java設(shè)計(jì)模式編程中的單例模式和簡(jiǎn)單工廠模式

    這篇文章主要介紹了淺析Java設(shè)計(jì)模式編程中的單例模式和簡(jiǎn)單工廠模式,使用設(shè)計(jì)模式編寫代碼有利于團(tuán)隊(duì)協(xié)作時(shí)程序的維護(hù),需要的朋友可以參考下
    2016-01-01
  • IDEA中Maven爆紅以及依賴下載失敗的最全解決方案

    IDEA中Maven爆紅以及依賴下載失敗的最全解決方案

    這篇文章主要介紹了IDEA中Maven爆紅,依賴下載失敗的最全解決方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08

最新評(píng)論