2018-01-07

shiro

1.shiro的簡介:

簡介

Apache Shiro 是 Java 的一個安全框架。目前,使用 Apache Shiro 的人越來越多,因為它相當(dāng)簡單,對比 Spring Security,可能沒有 Spring Security 做的功能強(qiáng)大,但是在實際工作時可能并不需要那么復(fù)雜的東西,所以使用小而簡單的 Shiro 就足夠了。對于它倆到底哪個好,這個不必糾結(jié),能更簡單的解決項目問題就好了。

本教程只介紹基本的 Shiro 使用,不會過多分析源碼等,重在使用。

Shiro 可以非常容易的開發(fā)出足夠好的應(yīng)用,其不僅可以用在 JavaSE 環(huán)境,也可以用在 JavaEE 環(huán)境。Shiro 可以幫助我們完成:認(rèn)證、授權(quán)、加密、會話管理、與 Web 集成、緩存等。這不就是我們想要的嘛,而且 Shiro 的 API 也是非常簡單;其基本功能點如下圖所示:

image

Authentication:身份認(rèn)證 / 登錄,驗證用戶是不是擁有相應(yīng)的身份;

Authorization:授權(quán),即權(quán)限驗證,驗證某個已認(rèn)證的用戶是否擁有某個權(quán)限;即判斷用戶是否能做事情,常見的如:驗證某個用戶是否擁有某個角色。或者細(xì)粒度的驗證某個用戶對某個資源是否具有某個權(quán)限;

Session Manager:會話管理,即用戶登錄后就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通 JavaSE 環(huán)境的,也可以是如 Web 環(huán)境的;

Cryptography:加密,保護(hù)數(shù)據(jù)的安全性,如密碼加密存儲到數(shù)據(jù)庫,而不是明文存儲;

Web Support:Web 支持,可以非常容易的集成到 Web 環(huán)境;

Caching:緩存,比如用戶登錄后,其用戶信息、擁有的角色 / 權(quán)限不必每次去查,這樣可以提高效率;

Concurrency:shiro 支持多線程應(yīng)用的并發(fā)驗證,即如在一個線程中開啟另一個線程,能把權(quán)限自動傳播過去;

Testing:提供測試支持;

Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進(jìn)行訪問;

Remember Me:記住我,這個是非常常見的功能,即一次登錄后,下次再來的話不用登錄了。

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

接下來我們分別從外部和內(nèi)部來看看 Shiro 的架構(gòu),對于一個好的框架,從外部來看應(yīng)該具有非常簡單易于使用的 API,且 API 契約明確;從內(nèi)部來看的話,其應(yīng)該有一個可擴(kuò)展的架構(gòu),即非常容易插入用戶自定義實現(xiàn),因為任何框架都不能滿足所有需求。

首先,我們從外部來看 Shiro 吧,即從應(yīng)用程序角度的來觀察如何使用 Shiro 完成工作。如下圖:

image

可以看到:應(yīng)用代碼直接交互的對象是 Subject,也就是說 Shiro 的對外 API 核心就是 Subject;其每個 API 的含義:

Subject:主體,代表了當(dāng)前 “用戶”,這個用戶不一定是一個具體的人,與當(dāng)前應(yīng)用交互的任何東西都是 Subject,如網(wǎng)絡(luò)爬蟲,機(jī)器人等;即一個抽象概念;所有 Subject 都綁定到 SecurityManager,與 Subject 的所有交互都會委托給 SecurityManager;可以把 Subject 認(rèn)為是一個門面;SecurityManager 才是實際的執(zhí)行者;

SecurityManager:安全管理器;即所有與安全有關(guān)的操作都會與 SecurityManager 交互;且它管理著所有 Subject;可以看出它是 Shiro 的核心,它負(fù)責(zé)與后邊介紹的其他組件進(jìn)行交互,如果學(xué)習(xí)過 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

Realm:域,Shiro 從從 Realm 獲取安全數(shù)據(jù)(如用戶、角色、權(quán)限),就是說 SecurityManager 要驗證用戶身份,那么它需要從 Realm 獲取相應(yīng)的用戶進(jìn)行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應(yīng)的角色 / 權(quán)限進(jìn)行驗證用戶是否能進(jìn)行操作;可以把 Realm 看成 DataSource,即安全數(shù)據(jù)源。

也就是說對于我們而言,最簡單的一個 Shiro 應(yīng)用:

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

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

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

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

image

Subject:主體,可以看到主體可以是任何可以與應(yīng)用交互的 “用戶”;

SecurityManager:相當(dāng)于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心臟;所有具體的交互都通過 SecurityManager 進(jìn)行控制;它管理著所有 Subject、且負(fù)責(zé)進(jìn)行認(rèn)證和授權(quán)、及會話、緩存的管理。

Authenticator:認(rèn)證器,負(fù)責(zé)主體認(rèn)證的,這是一個擴(kuò)展點,如果用戶覺得 Shiro 默認(rèn)的不好,可以自定義實現(xiàn);其需要認(rèn)證策略(Authentication Strategy),即什么情況下算用戶認(rèn)證通過了;

Authrizer:授權(quán)器,或者訪問控制器,用來決定主體是否有權(quán)限進(jìn)行相應(yīng)的操作;即控制著用戶能訪問應(yīng)用中的哪些功能;

Realm:可以有 1 個或多個 Realm,可以認(rèn)為是安全實體數(shù)據(jù)源,即用于獲取安全實體的;可以是 JDBC 實現(xiàn),也可以是 LDAP 實現(xiàn),或者內(nèi)存實現(xiàn)等等;由用戶提供;注意:Shiro 不知道你的用戶 / 權(quán)限存儲在哪及以何種格式存儲;所以我們一般在應(yīng)用中都需要實現(xiàn)自己的 Realm;

SessionManager:如果寫過 Servlet 就應(yīng)該知道 Session 的概念,Session 呢需要有人去管理它的生命周期,這個組件就是 SessionManager;而 Shiro 并不僅僅可以用在 Web 環(huán)境,也可以用在如普通的 JavaSE 環(huán)境、EJB 等環(huán)境;所有呢,Shiro 就抽象了一個自己的 Session 來管理主體與應(yīng)用之間交互的數(shù)據(jù);這樣的話,比如我們在 Web 環(huán)境用,剛開始是一臺 Web 服務(wù)器;接著又上了臺 EJB 服務(wù)器;這時想把兩臺服務(wù)器的會話數(shù)據(jù)放到一個地方,這個時候就可以實現(xiàn)自己的分布式會話(如把數(shù)據(jù)放到 Memcached 服務(wù)器);

SessionDAO:DAO 大家都用過,數(shù)據(jù)訪問對象,用于會話的 CRUD,比如我們想把 Session 保存到數(shù)據(jù)庫,那么可以實現(xiàn)自己的 SessionDAO,通過如 JDBC 寫到數(shù)據(jù)庫;比如想把 Session 放到 Memcached 中,可以實現(xiàn)自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 進(jìn)行緩存,以提高性能;

CacheManager:緩存控制器,來管理如用戶、角色、權(quán)限等的緩存的;因為這些數(shù)據(jù)基本上很少去改變,放到緩存中后可以提高訪問的性能

Cryptography:密碼模塊,Shiro 提高了一些常見的加密組件用于如密碼加密 / 解密的。

2. Shiro實例詳細(xì)說明

本實例環(huán)境:eclipse + jdk8

本實例采用的主要技術(shù):spring + springmvc + shiro

假設(shè)已經(jīng)配置好了spring和springmvc的情況下,還需要引入shiro以及shiro集成到spring的包,

web.xml的配置:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<!-- Shiro Filter is defined in the spring application context: -->
<!-- 
1. 配置  Shiro 的 shiroFilter.  
2. DelegatingFilterProxy 實際上是 Filter 的一個代理對象. 默認(rèn)情況下, Spring 會到 IOC 容器中查找和 
<filter-name> 對應(yīng)的 filter bean. 也可以通過 targetBeanName 的初始化參數(shù)來配置 filter bean 的 id. 
-->
<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

springmvc.xml的配置:

<context:component-scan base-package="com.wuruan.shiro"></context:component-scan>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>

<mvc:annotation-driven></mvc:annotation-driven>
<mvc:default-servlet-handler/>

application-shiro.xml的配置:

<!--  
1. 配置 SecurityManager!
-->     
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="cacheManager" ref="cacheManager"/>
    <property name="authenticator" ref="authenticator"></property>
    
    <property name="realms">
        <list>
            <ref bean="jdbcRealm"/>
            <ref bean="secondRealm"/>
        </list>
    </property>
    
    <property name="rememberMeManager.cookie.maxAge" value="10"></property>
</bean>

<!-- Let's use some enterprise caching support for better performance.  You can replace this with any enterprise
     caching framework implementation that you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc -->
<!--  
2. 配置 CacheManager. 
2.1 需要加入 ehcache 的 jar 包及配置文件. 
-->     
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one.  If not, a new one
         will be creaed with a default config:
         <property name="cacheManager" ref="ehCacheManager"/> -->
    <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want
         a specific Ehcache configuration to be used, specify that here.  If you don't, a default
         will be used.: -->
    <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> 
</bean>

<bean id="authenticator" 
    class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
    <property name="authenticationStrategy">
        <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
    </property>
</bean>

<!-- Used by the SecurityManager to access security data (users, roles, etc).
     Many other realm implementations can be used too (PropertiesRealm,
     LdapRealm, etc. -->
<!-- 
    3. 配置 Realm 
    3.1 直接配置實現(xiàn)了 org.apache.shiro.realm.Realm 接口的 bean
-->     
<bean id="jdbcRealm" class="com.wuruan.shiro.realms.ShiroRealm">
    <property name="credentialsMatcher">
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="MD5"></property>
            <property name="hashIterations" value="1024"></property>
        </bean>
    </property>
</bean>

<bean id="secondRealm" class="com.wuruan.shiro.realms.SecondRealm">
    <property name="credentialsMatcher">
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="SHA1"></property>
            <property name="hashIterations" value="1024"></property>
        </bean>
    </property>
</bean>

<!--  
4. 配置 LifecycleBeanPostProcessor. 可以自定的來調(diào)用配置在 Spring IOC 容器中 shiro bean 的生命周期方法. 
-->       
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
     the lifecycleBeanProcessor has run: -->
<!--  
5. 啟用 IOC 容器中使用 shiro 的注解. 但必須在配置了 LifecycleBeanPostProcessor 之后才可以使用. 
-->     
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
      depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"/>
</bean>

<!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml -
     web.xml uses the DelegatingFilterProxy to access this bean.  This allows us
     to wire things with more control as well utilize nice Spring things such as
     PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: -->
<!--  
6. 配置 ShiroFilter. 
6.1 id 必須和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.
                  若不一致, 則會拋出: NoSuchBeanDefinitionException. 因為 Shiro 會來 IOC 容器中查找和 <filter-name> 名字對應(yīng)的 filter bean.
-->     
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <property name="loginUrl" value="/login.jsp"/>
    <property name="successUrl" value="/list.jsp"/>
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
    
    <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
    
    <!--  
        配置哪些頁面需要受保護(hù). 
        以及訪問這些頁面需要的權(quán)限. 
        1). anon 可以被匿名訪問
        2). authc 必須認(rèn)證(即登錄)后才可能訪問的頁面. 
        3). logout 登出.
        4). roles 角色過濾器
    -->

    <property name="filterChainDefinitions">
        <value>
            /login.jsp = anon
            /shiro/login = anon
            /shiro/logout = logout
            
            /user.jsp = roles[user]
            /admin.jsp = roles[admin]
            
            # everything else requires authentication:
            /** = authc
        </value>
    </property>
   
</bean>

<!-- 配置一個 bean, 該 bean 實際上是一個 Map. 通過實例工廠方法的方式 -->
<bean id="filterChainDefinitionMap" 
    factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean>

<bean id="filterChainDefinitionMapBuilder"
    class="com.wuruan.shiro.factory.FilterChainDefinitionMapBuilder"></bean>

<bean id="shiroService"
    class="com.wuruan.shiro.services.ShiroService"></bean>

</beans>
ehcache.xml的配置:

<diskStore path="java.io.tmpdir"/>

<cache name="authorizationCache"
       eternal="false"
       timeToIdleSeconds="3600"
       timeToLiveSeconds="0"
       overflowToDisk="false"
       statistics="true">
</cache>

<cache name="authenticationCache"
       eternal="false"
       timeToIdleSeconds="3600"
       timeToLiveSeconds="0"
       overflowToDisk="false"
       statistics="true">
</cache>

<cache name="shiro-activeSessionCache"
       eternal="false"
       timeToIdleSeconds="3600"
       timeToLiveSeconds="0"
       overflowToDisk="false"
       statistics="true">
</cache>


<defaultCache
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    overflowToDisk="true"
    />
<cache name="sampleCache1"
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="300"
    timeToLiveSeconds="600"
    overflowToDisk="true"
    />

<cache name="sampleCache2"
    maxElementsInMemory="1000"
    eternal="true"
    timeToIdleSeconds="0"
    timeToLiveSeconds="0"
    overflowToDisk="false"
    /> -->

</ehcache>
自定的的Realm對象:

public class ShiroRealm extends AuthorizingRealm {

@Override
protected AuthenticationInfo doGetAuthenticationInfo(
        AuthenticationToken token) throws AuthenticationException {
    System.out.println("[FirstRealm] doGetAuthenticationInfo");
    
    //1. 把 AuthenticationToken 轉(zhuǎn)換為 UsernamePasswordToken 
    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    
    //2. 從 UsernamePasswordToken 中來獲取 username
    String username = upToken.getUsername();
    
    //3. 調(diào)用數(shù)據(jù)庫的方法, 從數(shù)據(jù)庫中查詢 username 對應(yīng)的用戶記錄
    System.out.println("從數(shù)據(jù)庫中獲取 username: " + username + " 所對應(yīng)的用戶信息.");
    
    //4. 若用戶不存在, 則可以拋出 UnknownAccountException 異常
    if("unknown".equals(username)){
        throw new UnknownAccountException("用戶不存在!");
    }
    
    //5. 根據(jù)用戶信息的情況, 決定是否需要拋出其他的 AuthenticationException 異常. 
    if("monster".equals(username)){
        throw new LockedAccountException("用戶被鎖定");
    }
    
    //6. 根據(jù)用戶的情況, 來構(gòu)建 AuthenticationInfo 對象并返回. 通常使用的實現(xiàn)類為: SimpleAuthenticationInfo
    //以下信息是從數(shù)據(jù)庫中獲取的.
    //1). principal: 認(rèn)證的實體信息. 可以是 username, 也可以是數(shù)據(jù)表對應(yīng)的用戶的實體類對象. 
    Object principal = username;
    //2). credentials: 密碼. 
    Object credentials = null; //"fc1709d0a95a6be30bc5926fdb7f22f4";
    if("admin".equals(username)){
        credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
    }else if("user".equals(username)){
        credentials = "098d2c478e9c11555ce2823231e02ec1";
    }
    
    //3). realmName: 當(dāng)前 realm 對象的 name. 調(diào)用父類的 getName() 方法即可
    String realmName = getName();
    //4). 鹽值. 
    ByteSource credentialsSalt = ByteSource.Util.bytes(username);
    
    SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);
    info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
    return info;
}

public static void main(String[] args) {
    String hashAlgorithmName = "MD5";
    Object credentials = "123456";
    Object salt = ByteSource.Util.bytes("user");;
    int hashIterations = 1024;
    
    Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
    System.out.println(result);
}

//授權(quán)會被 shiro 回調(diào)的方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
        PrincipalCollection principals) {
    //1. 從 PrincipalCollection 中來獲取登錄用戶的信息
    Object principal = principals.getPrimaryPrincipal();
    
    //2. 利用登錄的用戶的信息來用戶當(dāng)前用戶的角色或權(quán)限(可能需要查詢數(shù)據(jù)庫)
    Set<String> roles = new HashSet<>();
    roles.add("user");
    if("admin".equals(principal)){
        roles.add("admin");
    }
    
    //3. 創(chuàng)建 SimpleAuthorizationInfo, 并設(shè)置其 reles 屬性.
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
    
    //4. 返回 SimpleAuthorizationInfo 對象. 
    return info;
}

個人筆記總結(jié):

  1. 授權(quán)需要繼承 AuthorizingRealm 類, 并實現(xiàn)其 doGetAuthorizationInfo 方法

  2. AuthorizingRealm 類繼承自 AuthenticatingRealm, 但沒有實現(xiàn) AuthenticatingRealm 中的
    doGetAuthenticationInfo, 所以認(rèn)證和授權(quán)只需要繼承 AuthorizingRealm 就可以了. 同時實現(xiàn)他的兩個抽象方法.

  3. 為什么使用 MD5 鹽值加密:

  4. 如何做到:
    1). 在 doGetAuthenticationInfo 方法返回值創(chuàng)建 SimpleAuthenticationInfo 對象的時候, 需要使用
    SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName) 構(gòu)造器
    2). 使用 ByteSource.Util.bytes() 來計算鹽值.
    3). 鹽值需要唯一: 一般使用隨機(jī)字符串或 user id
    4). 使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); 來計算鹽值加密后的密碼的值.

  5. 如何把一個字符串加密為 MD5

  6. 替換當(dāng)前 Realm 的 credentialsMatcher 屬性. 直接使用 HashedCredentialsMatcher 對象, 并設(shè)置加密算法即可.

密碼的比對:
通過 AuthenticatingRealm 的 credentialsMatcher 屬性來進(jìn)行的密碼的比對!

  1. 獲取當(dāng)前的 Subject. 調(diào)用 SecurityUtils.getSubject();
  2. 測試當(dāng)前的用戶是否已經(jīng)被認(rèn)證. 即是否已經(jīng)登錄. 調(diào)用 Subject 的 isAuthenticated()
  3. 若沒有被認(rèn)證, 則把用戶名和密碼封裝為 UsernamePasswordToken 對象
    1). 創(chuàng)建一個表單頁面
    2). 把請求提交到 SpringMVC 的 Handler
    3). 獲取用戶名和密碼.
  4. 執(zhí)行登錄: 調(diào)用 Subject 的 login(AuthenticationToken) 方法.
  5. 自定義 Realm 的方法, 從數(shù)據(jù)庫中獲取對應(yīng)的記錄, 返回給 Shiro.
    1). 實際上需要繼承 org.apache.shiro.realm.AuthenticatingRealm 類
    2). 實現(xiàn) doGetAuthenticationInfo(AuthenticationToken) 方法.
  6. 由 shiro 完成對密碼的比對.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 說明:本文很多觀點和內(nèi)容來自互聯(lián)網(wǎng)以及各種資料,如果侵犯了您的權(quán)益,請及時聯(lián)系我,我會刪除相關(guān)內(nèi)容。 權(quán)限管理 基...
    寇寇寇先森閱讀 7,754評論 8 76
  • 前言 Spring boot 是什么,網(wǎng)上的很多介紹,這里博客就不多介紹了。如果不明白Spring boot是什么...
    xuezhijian閱讀 18,017評論 13 39
  • 1.簡介 Apache Shiro是Java的一個安全框架。功能強(qiáng)大,使用簡單的Java安全框架,它為開發(fā)人員提供...
    H_Man閱讀 3,255評論 4 47
  • Shiro(代碼) 1.1 簡介 Apache Shiro是Java的一個安全框架。目前,使用Apache Shi...
    ZZS_簡閱讀 526評論 0 0
  • 一、架構(gòu) 要學(xué)習(xí)如何使用Shiro必須先從它的架構(gòu)談起,作為一款安全框架Shiro的設(shè)計相當(dāng)精妙。Shiro的應(yīng)用...
    ITsupuerlady閱讀 3,623評論 4 32

友情鏈接更多精彩內(nèi)容