Shiro详解

功能简介

  1. Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
  2. Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。
  3. Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web环境的;
  4. Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
  5. Web Support:Web 支持,可以非常容易的集成到Web环境;
  6. Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
  7. Concurrency;Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
  8. Testing:提供测试支持;
  9. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问
  10. Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

shiro架构

8d83c79ffaa94444b319f871e00d963e

  1. Subject: 主体,可以是任何可以与应用交互的“用户”;
  2. SecurityManager: 相当于SpringMVC中的DispatcherServlet,是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
  3. Authenticator: 认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
  4. Authrizer: 授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
  5. Realm: 可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;
  6. SessionManager: 如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);
  7. SessionDAO: DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
  8. CacheManager: 缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
  9. Cryptography: 密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。

shiro基本流程

4029bca342554946beca293a1ab40a06

  1. 应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
  2. 我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入

shiro整合spring

导包

导入jar包:

log4j-1.2.15.jar
shiro-all-1.3.2.jar
slf4j-api-1.6.1.jar
slf4j-log4j12-1.6.1.jar

定义ShiroFilter

web.xml文件里配置shiro的过滤器shiroFilter,DelegatingFilterProxy实际上是Filter的一个代理对象,默认情况下,Spring会到IOC容器查找和<filter-name>对应的filter bean,也可以通过targetBeanName的初始化参数来配置filter bean的id:

<filter>
    <filter-name>shiroFilterABC</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>
    <init-param>
    	<param-name>targetBeanName</param-name>
        <param-value>shiroFilterABC</param-value>
    </init-param>
</filter>

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

注册SecurityManager

在spring的配置文件applicationContext.xml里配置SecurityManager:

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
	<property name="cacheManager" ref="cacheManager"/>
    <property name="sessionMode" value="native"/>
    <property name="realm" ref="jdbcRealm"/>
</bean>

注册CacheManager

在spring的配置文件applicationContext.xml里配置CacheManager:

需要加入ehcache的jar包及配置文件

<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <property name="cacheManagerCongfigFile" value="classpath:ehcache.xml"/>
</bean>

注册Realm

在spring的配置文件applicationContext.xml里定义注册一个ShiroRealm.javabean类并实现Realm.java接口:

<bean id="jdbcRealm" class="com.zjm.study.shiro.realms.MyShiroRealm"></bean>

注册lifecycleBeanPostProcessor

在spring的配置文件applicationContext.xml里配置生命周期的bean后置处理器,可以自动的来调用配置在Spring IOC容器中的shiro bean的生命周期方法:

<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>

启动IOC扫描shiro注解

启用IOC容器中使用shiro的注解,但必须在配置了LifecycleBeanPostProcessor之后才能用:

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"></bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
	<property name="securityManager" ref="securityManager"/>
</bean>

配置ShiroFilter

在spring的配置文件applicationContext.xml里配置ShiroFilter,id必须和web.xml文件中配置的DelegatingFilterProxy<filter-name>一致, 若不一致,则会抛出:NoSuchBeanDefinitionException,因为Shiro回来IOC容器中查找和<filter-name>名字对应的filter bean

<bean id="shiroFilterABC" class="org.apache.spring.web.ShiroFilterFactoryBean">
	<property name="securityManager" ref="securityManager"/>		<!--配置securityManager-->
    <property name="loginUrl" value="/login.jsp"/>					<!--触发登录方法-->
    <property name="sucessUrl" value="/list.jsp"/>					<!--触发认证成功-->
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/>	<!--触发没有权限-->
    <!--
		配置哪些页面需要受保护。
		以及访问这些页面需要的权限。
		1).anon可以被匿名访问
		2).authc必须认证(即登录)后才能访问的页面
		3).logout表示登出
	-->
    <property name="filterChainDefinitions">
    	<value>
        	/favicon.ico = anon
            /logo.png = anon
            /shiro.css = anon
            /s/login = anon
            /shiro/logout = logout
            # allow WebStart to pull the jars for the swing app:
            /*.jar = anon
            # everything else requires authentication:
            /** = authc
        </value>
    </property>
</bean>
  • URL匹配规则

    URL权限采取第一次匹配优先的方式,即从头开始使用第一个匹配的url模式对应的拦截链:

    • /bb/** = filter1
    • /bb/aa = filter2
    • /** = filter3

    如果请求的url是“/bb/aa”,因为按照声明顺序进行匹配,那么将使用filter1进行拦截

认证

  1. 先获取subject

    Subject currentUser = SecurityUtils.getSubject();
    
  2. 判定当前的subject是否已经被认证了,即是否已经登录

    if(!currentUser.isAuthenticated())
    
  3. 没有登录就需要创建一个UsernamePasswordToken对象,需要传入username和password,实际开发中的username和password是前端请求传过来的

    UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456")
    
  4. 调用subject的login()方法来执行登录,login()方法实际上最后会调用shiro的doGetAuthenticationInfo(AuthenticationToken)方法,而此方法又会被我们自己定义的Realm实现类来重写

    try {
        currentUser.login(token);
    }
    
  5. 自定义Realm的方法,从数据库中获取对应的记录,返回给Shiro

    1. 实现类需要继承org.apache.shiro.realm.AuthenticatingRealm类
    2. 并重写doGetAuthenticationInfo(AuthenticationToken)方法
  6. 由shiro完成对密码的校验

实现认证Realm

package com.zjm.study.shiro.realms;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.AuthenticatingRealm;

public class MyShiroRealm extends AuthenticatingRealm {
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1. 把AuthenticationToken转换为UsernamePasswordToken
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        
        //2. 从UsernamePasswordToken中来获取username
        String username = upToken.getUsername();
        
        //3. 调用数据库的方法,从数据库中查询username对应的用户记录
        System.out.println("从数据库中获取username:" + username + "所对应的用户信息")
        
        //4. 若用户不存在,则可以抛出UnknowAccountException异常
        if("unknow".equals(username)) {
            throw new UnknowAccountException("用户不存在!");
        }
        
        //5. 根据用户信息的情况,决定是否需要抛出其他的AuthenticationException异常,如用户被锁
        if("monster".equals(username)) {
            throw new LockedAccountException("用户被锁...");
        }
        
        //6. 根据用户的情况,来构建AuthenticationInfo对象并返回,通常使用的实现类为:SimpleAuthenticationInfo
        //以下信息是从数据库中获取的
        //1). principal:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象
        Object principal = username;
        
        //2). credentials:密码,实际此密码由数据库获取并进行盐值加密,而不是前端传过来的密码。再直接由shiro自动比对。
        Object credentials = "123465";
        
        //3). realmName:当前realm对象的name。调用父类的getName()方法即可
        String realmName = getName();
        
        //4). 盐值,唯一性,建议用username来设定
        ByteSource credentialsSalt = ByteSource.Util.bytes(username);
        
        //也进行了盐值加密
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
        return info;
    }
}

密码的比对

密码的比对是由shiro自动完成的,通过自己从数据库中获取到正确密码,然后填入到SimpleAuthenticationInfo对象的构造方法参数里面,此构造方法会自动获取到前端传来的密码(通过token对象的getPassword()方法),然后比对填入的数据库密码进行认证。

而整个过程通过父类AuthenticatingRealm的credentialsMatcher属性来进行密码的比对。

密码的MD5加密

从数据库中获取到了正确密码后不应该明文传值,从token中获取到的前端传来的密码也不应该就这么让它在后端系统中这么明文展示,而应该进行一层加密;且这个密码加密算法要求是不可逆的,即不能通过加密算法来反推出原密码,著名的加密算法有MD5、SHA-1、SHA-2、SHA-3等。

  • 前端密码自动加密:

    之前在注册realm的时候我们不是什么都没配吗?这回我们来加点配置,使我们的前端密码在后端系统中加密传递:

    <bean id="jdbcRealm" class="com.zjm.study.shiro.realms.MyShiroRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            	<property name="hashAlgorithmName" value="MD5"></property>	<!--配置加密算法为MD5-->
                <property name="hashIterations" value="1024"></property>	<!--配置加密次数为1024-->
            </bean>
        </property>
    </bean>
    

    如果这时候我们debug看源代码可以看到加密过程是通过一个SimpleHash对象来完成的。

  • 数据库密码手动盐值加密

    前面我们进行了前端密码的加密操作,但是如果两个用户的密码是一样的,那么出来的加密字符串也是一样。这时候我们要求一样的密码但是加密出来的结果字符串不是一样的,所以我们要“加一点佐料”,即“盐值加密”:

    public Object MD5SaltEncrypt(Object credentials, String username) {
        String hashAlgorithmName = "MD5";
        Object salt = ByteSource.Util.bytes("username");
        int hashIterations = 1024;
        
        Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
        return result;
    }
    

多个Realm进行验证

  • 验证器注入

    配置多个Realm类,并注册到IOC容器中,注册好后将全部的Realm配置进一个验证器Authenticator里面,Realm在里面的形式是list集合,验证器同样配置在spring的配置文件applicationContext.xml里:

    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="realms">
        	<list>
            	<ref bean="jdbcRealm"/>
                <ref bean="secondRealm"/>
                <ref bean="thirdRealm"/>
                <ref bean="fourthRealm"/>
                ······
            </list>
        </property>
    </bean>
    

    除此之外还要给定义好的SecurityManager里面配置anthenticator属性,将原先配置的单个Realm改为验证器注入Realm:

    <property name="authenticator" ref="authenticator"></property>
    

    全部配置完毕后最终效果就是按照验证器里面的集合顺序来依次运行Realm认证

  • 直接SecurityManager里注入list

    将验证器里面的realms属性剪切到SecurityManager里面:

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    	<property name="cacheManager" ref="cacheManager"/>
        <property name="sessionMode" value="native"/>
        <property name="realm" ref="jdbcRealm"/>
        <property name="realms">
        	<list>
            	<ref bean="jdbcRealm"/>
                <ref bean="secondRealm"/>
                <ref bean="thirdRealm"/>
                <ref bean="fourthRealm"/>
                ······
            </list>
        </property>
    </bean>
    

    原理是shiro自动将SecurityManager里的realms赋予到了验证器authenticator里面,然后再读取里面的realms。

认证策略

AuthenticationStrategy接口的实现:

  • FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略,且此实现类是AuthenticationStrategy接口的默认实现;
  • AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,将返回所有Realm身份验证成功的认证信息;
  • AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。
  • ModularRealmAuthenticator:默认是AtLeastOneSuccessfulStrategy策略。

要想修改认证策略只需要在验证器里面Authenticator里面多加个验证策略属性authenticationStrategy即可:

<property name="authenticationStrategy">
	<bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
</property>

授权

  • 授权:也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
  • 主体(Subject):访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访问相应的资源。
  • 资源(Resource):在应用中用户可以访问的URL,比如访问JSP页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
  • 权限(Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资,如:访问用户列表页面查看/新增/修改/删除用户数据(很多时候都是CRUD式权限控制)等。权限代表了用户有没有操作某个资源的权力,即反映在某个资源上的操作允不允许。
  • Shiro支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限,即实例界别的)
  • 角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的确实拥有一组不同的权限。

授权方式

1696840204987

默认过滤器

1696906682675

1696906718286

1696907123103

Permissions

  • 规则:资源标识符:操作:对象实例D即对哪个资源的哪个实例可以进行什么操作.其默认支持通配符权限字符串,:表示资源操作/实例的分割;,表示操作的分割,*表示任意资源操作/实例。
  • 多层次管理:
    • 例如:user:query,user:edit
    • 冒号是一个特殊字符,它用来分隔权限字符串的下一部件:第一部分是权限被操作的领域(打印机),第二部分是被执行的操作。
    • 多个值:每个部件能够保护多个值。因此,除了授予用户user:queyuser:edit权限外,也可以简单地授予他们-个:user:query,edt
    • 还可以用*号代替所有的值,如:user:*,也可以写:*:quey,表示某个用户在所有的领域都有query的权限
  • 实例级访问控制
    • 一这种情况通常会使用三个部件:域、操作、被付诸实施的实例。如:user:edit:manager
    • 也可以使用通配符来定义,如:user:edit:*、user:*:*、user:*:manager
    • 部分省略通配符:缺少的部件意味着用户可以访问所有与之匹配的值,比如:user:edit等价于user:edit:*、user等价于user:*:*
    • 注意:通配符只能从字符串的结尾处省略部件,也就是说user:edit并不等价于user:*:edit

实现授权Realm

  1. 授权需要继承AuthorizingRealm类,并实现其doGetAuthorizationInfo方法
  2. AuthorizingRealm类继承自AuthenticatingRealm,但没有实现AuthenticatingRealm中的doGetAuthenticationInfo,所以认证和授权只需要继承AuthorizingRealm就可以了,同时实现他的两个抽象方法.
package com.zjm.study.shiro.realms;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.realm.AuthenticatingRealm;

public class MyShiroRealm extends AuthenticatingRealm {
   	//用于认证的doGetAuthenticationInfo(上文已实现)
    @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        ······
    }
    
    //用于授权的doGetAuthorizationInfo
    @Override
    protected AuthenticationInfo doGetAuthorizationInfo(PrincipalCollection principals) throws AuthenticationException {
        //1. 从PrincipalCo1 lection中来获取登录用户的信息
        Object principal = principals.getprimaryPrincipal();
        
        //2. 利用登录的用户的信息来识别用户当前用户的角色或权限(可能需要查询数据库)
        Set<String> roles = new Hashset<>();
        roles.add("user");	//授予“user”权限
        if("admin".equals(principal)) {
            roles.add("admin")	//授予“admin”权限
        }
        
        //3. 创建SimpleAuthorizationInfo,并设置其roles属性.
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
        
        //4. 返回SimpleAuthorizationInfo对象.
        return info;
    }
}

shiro权限标签

  • Shiro提供了JSTL标签用于在JSP页面进行权限控制,如根据登绿用户显示相应的页面按钮。

  • guest标签:用户没有身份验证时显示相应信息,即游客访问信息:

    <shiro:guest>
    	欢迎游客访问,<a href="login.jsp">登录</a>
    </shiro:guest>
    
  • user标签:用户已经经过认证/记住我登录后显示相应的信息。

    <shiro:user>
    	欢迎[<shiro:principal/>]登录,<a href="logout">退出</a>
    </shiro:user>
    
  • authenticated标签:用户已经身份验证通过,即Subject.login登绿成功,不是记住我登录的

    <shiro:authenticated>
    	用户[<shiro:principal/>]已身份验证通过
    </shiro:authenticated>
    
  • notAuthenticated标签:用户未进行身份验证,即没有调用Subject.login进行登录,包括记住我自动登录的也属于未进行身份验证。

    <shiro:notAuthenticated>
    	未身份验证(包括记住我)
    </shiro:notAuthenticated>
    
  • pincipal标签:显示用户身份信息,默认调用Subject.getPrincipal()获取,即Primary Principal。

    <shiro:principal property="username"/>
    
  • hasRole标签:如果当前Subject有角色将显示body体内容:

    <shiro:hasRole name="admin">
        用户[<shiro:principal/>]拥有角色admin<br/>
    </shiro:hasRole>
    
  • hasAnyRoles标签:如果当前Subject有任意一个角色(或的关系)将显示body体内容。

    <shiro:hasAnyRoles name="admin,user">
    	用户[<shiro:principal/>]拥有角色admin或user<br/>
    </shiro:hasAnyRoles>
    
  • lacksRole:如果当前Subject没有角色将显示body体内容

    <shiro:lacksRole name="admin">
    	用户[<shiro:principal/>]没有角色admin<br/>
    </shiro:lacksRole>
    
  • hasPermission:如果当前Subject有权限将显示body体内容

    <shiro:hasPermission name="user:create">
    	用户[<shiro:principal/>]拥有权限user:create<br/>
    </shiro:hasPermission>
    
  • lacksPermission:如果当前Subject没有权限将显示body体内容。

    <shiro:lacksPermission name="org:create">
    	用户[<shiro:principal/>]没有权限org:create<br/>
    </shiro:lacksPermission>
    

shiro权限注解

  • @RequiresAuthentication:表示当前Subjecti已经通过login进行了身份验证;即Subject.isAuthenticated()返回true
  • @RequiresUser:表示当前Subject已经身份验证或者通过记住我登录的。
  • @RequiresGuest:表示当前Subject;没有身份验证或通过记住我登录过,即是游客身份。
  • @RequiresRoles(value={"admin","user"),logical=Logical..AND):表示当前Subject需要角色admin和user
  • @RequiresPermissions (value={"user:a","user:b"),logical-=Logical.QR):表示当前Subject需要权限user:auser:b

会话管理

Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如web容器tomcat),不管JavaSE还是JavaEE环境都可以使用,提供了会话管理、会话事件监听、会话存储/持久化、容器无关的集群、失效/过期支持、对Web的透明支持、SSO单点登录的支持等特性。

也就是说我们可以在编写service层的时候就可以直接获取到session

会话相关的API

  • Subiect.getSession():即可获取会话;其等价于Subiect.getSession(true),即如果当前没有创建Session对象会创建一个;Subiect.getSession(false),如果当前没有创建Session则返回null
  • session.getld():获取当前会话的唯一标识
  • session.getHost():获取当前Subject的主机地址
  • session.getTimeout() & session.setTimeout(毫秒):获取/设置当前Sessionl的过期时间
  • session.getStartTimestampo&session.getLastAccessTime():获取会话的启动时间及最后访问时间;如果是JavaSE应用需要自己定期调用session.touch()去更新最后访问时间;如果是Web应用,每次进入ShiroFilter都会自动调用session.touch()来更新最后访问时间。
  • session.touch0&session.stop():更新会话最后访问时间及销毁会话;当Subject.logout()时会自动调用stop方法来销毁会话。如果在web中,调用HttpSession.invalidate()也会自动调用Shiro的Session.stop()方法进行销毁Shiro的会话
  • session.setAttribute(key,val) & session.getAttribute(key)&session.removeAttribute(key):设置/获取/删除会话属性;在整个会话范围内都可以对这些属性进行操作

会话监听器

shiro自动给会话设置的监听器:

1696927433121

SessionDao

1696994515485

  • AbstractSessionDAO:提供了SessionDAO的基础实现如生成会话引D等
  • CachingSessionDAO:提供了对开发者透明的会话缓存的功能,需要设置相应的CacheManager
  • MemorySessionDAO:直接在内存中进行会话维护
  • EnterpriseCacheSessionDAO:提供了缓存功能的会话维护,默认情况下使用MapCache实现,内部使用
  • ConcurrentHashMap:保存缓存的会话。

配置:

<!-- Session ID生成器 -->
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>

<!-- Session DAO 继承EnterpriseCacheSessionDAO -->
<bean id="session" class="com.zjm.study.shiro.realms.MySessionDao">
	<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
    <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>

<!-- 会话管理 -->
<bean id="sessionManager" class="org.apache.shiro.session.mgt.DefaultSessionManager">
	<property name="globalSessionTimeout" value="1800000"/>
    <property name="deleteInvalidSessions" value="true"/>
    <property name="sessionValidationSchedulerEnabled" value="true"/>
    <property name="sessionDAO" ref="sessionDAO"/>
</bean>

实现:

自己创建一个实现类,需要继承EnterpriseCacheSessionDAO,里面有4个方法可以继承过来重写,

会话验证

  • Shiro提供了会话验证调度器,用于定期的验证会话是否已过期,如果过期将停止会话
  • 出于性能考虑,一般情况下都是获取会话时来验证会话是否过期并停止会话的;但是如在web环境中,如果用户不主动退出是不知道会话是否过期的,因此需要定期的检测会话是否过期,Shiro提供了会话验证调度SessionValidationScheduler
  • Shiro也提供了使用Quartzs会话验证调度器:QuartzSessionValidationScheduler

缓存

  • Shiro内部相应的组件(DefaultSecurityManager)会自动检测相应的对象(如Realm)是否实现了CacheManagerAware并自动注入相应的CacheManager
  • Shiro提供了Caching Realm,其实现了CacheManagerAware接口,提供了缓存的一些基础实现,
  • Authenticating RealmAuthorizingRealm也分别提供了对AuthenticationlnfoAuthorizationlnfo信息的缓存。

RememberMe

Shiro提供了记住我(RememberMe)的功能,比如访问如淘宝等一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁,下次访问时无需再登绿即可访问,基本流程如下:

  1. 首先在登录页面选中RememberMe然后登录成功;如果是浏览器登录,一般会把RememberMe的Cookie写到客户端并保存下来;

  2. 关闭浏览器再重新打开;会发现浏览器还是记住你的;

  3. 访问一般的网页服务器端还是知道你是谁,且能证常访问;

  4. 但是比如我们访问淘宝时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。

认证与RememberMe的区别:

  • subject.isAuthenticated():表示用户进行了身份验证登绿的即使有Subiect…login进行了登录;
  • subject.isRemembered():表示用户是通过记住我登绿的此时可能并不是真正的你(如你的朋友使用你的电脑,或者
    你的cookie被窃取)在访问的
  • 两者二选一,即subject.isAuthenticated0==true,则subject.isRemembered()==false;反之一样。

实现:

UsernamePasswordToken token = new UsernamePasswordToken(username);
token.setRememberMe(true);

**配置RememberMe的时间:**只用在securityManager里面配置rememberMeManager的cookie的maxAge属性即可

<!-- 10秒后rememberMe会自动失效 -->
wordToken token = new UsernamePasswordToken(username);
token.setRememberMe(true);

**配置RememberMe的时间:**只用在securityManager里面配置rememberMeManager的cookie的maxAge属性即可

<!-- 10秒后rememberMe会自动失效 -->
<property name="rememberMeManager.cookie.maxAge" value="10"></property>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值