您的位置:首页 > 其它

Shiro入门学习五

2017-11-29 19:32 337 查看

与Web集成

前面的学习都是在JavaSE下,Shiro提供了与Web集成的支持,其通过一ShiroFilter入口来拦截需要安全控制的URL,然后进行相应的控制,ShiroFilter 类似于如 Strut2/SpringMVC 这种 web 框架的前端控制器,其是安全控制的入口点,其负责读取配置(如 ini 配置文件),然后判断 URL 是否需要登录 / 权限等工作。

servlet环境配置

一. shiro1.1及以前版本配置方式

<filter>
<filter-name>iniShiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.IniShiroFilter</filter-class>
<init-param>
<param-name>configPath</param-name>
<param-value>classpath:shiro.ini</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>iniShiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


使用IniShiroFilter作为Shiro安全控制的入口,通过url-pattern指定需要安全的URL;

通过 configPath 指定 ini 配置文件位置,默认是先从 /WEB-INF/shiro.ini 加载,如果没有就默认加载 classpath:shiro.ini;

也可以通过如下方式直接内嵌 ini 配置文件内容到 web.xml。

<init-param>
<param-name>config</param-name>
<param-value>
ini配置文件贴在这
</param-value>
</init-param>


二. shiro1.2及以后版本配置方式

shiro1.2开始引入了Environment/WebEnvironment概念,即由它们的实现提供相应的 SecurityManager 及其相应的依赖。ShiroFilter 会自动找到 Environment 然后获取相应的依赖。与spring配置很像

<!--通过EnvironmentLoaderListener来创建相应的WebEnvironment并自动绑定到 ServletContext,
默认使用 IniWebEnvironment 实现-->
<listener>
<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<!--通过配置修改默认实现及其加载的配置文件位置-->
<context-param>
<param-name>shiroEnviromentClass</param-name>
<param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value>
</context-param>
<context-param>
<param-name>shiroConfigLocations</param-name>
<param-value>classpath:shiro.ini</param-value>
</context-param>
<!--shiroFilter-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


web INI 配置

ini 配置部分和之前的相比将多出对 urls 部分的配置。

[main]
#默认是/login.jsp
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized
perms.unauthorizedUrl=/unauthorized
logout.redirectUrl=/login
[users]
zhang=123,admin
wang=123
[roles]
admin=user:*,menu:*
[urls]
/login=anon
/unauthorized=anon
/static/**=anon
/authenticated=authc
/role=authc,roles[admin]
/permission=authc,perms["user:create"]


authc.loginUrl:登录URL

roles.unauthorizedUrl:角色未授权重定向URL

perms.unauthorizedUrl:权限未授权重定向URL

logout.redirectUrl:登出时重定向URL

anon:org.apache.shiro.web.filter.authc.AnonymousFilter拦截器表示可以匿名访问

authc:org.apache.shiro.web.filter.authc.FormAuthenticationFilter拦截器表示需要身份认证通过后才能访问

roles[admin]:org.apache.shiro.web.filter.authz.RolesAuthorizationFilter拦截器表示需要有admin角色授权才能访问

perms[“user:create”]:org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter拦截器表示需要user:create权限才能访问

url 模式匹配顺序

url 模式匹配顺序是按照在配置中的声明顺序匹配,即从头开始使用第一个匹配的 url 模式对应的拦截器链。

在实际项目中比如支付时如果没有登录将跳转到登录页面,登录成功后再跳回到支付页面;对于这种功能大家可以在登录时把当前请求保存下来,然后登录成功后再重定向到该请求即可。Shiro内置了登录(身份验证)的实现:基于表单的和基于Basic的验证,其通过拦截器实现。

基于Basic的拦截器身份验证

[main]
authcBasic.applicationName=please login
[users]
zhang=123,admin
[urls]
/list=authcBasic,roles[admin]


authcBasic 是 org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter 类型的实例,其用于实现基于 Basic 的身份验证;applicationName 用于弹出的登录框提示信息。

基于表单的拦截器身份验证

[main]
authc.loginUrl=/formfilterlogin
authc.usernameParam=username
authc.passwordParam=password
authc.successUrl=/
authc.failureKeyAttribute=shiroLoginFailure
[urls]
/list=authc,roles[admin]


authc 是 org.apache.shiro.web.filter.authc.FormAuthenticationFilter 类型的实例,其用于实现基于表单的身份验证。usernameParam指定登录表单提交的用户名参数名;passwordParam 指定登录表单提交的密码参数名;successUrl 指定登录成功后重定向的默认地址(默认是 “/”)(如果有上一个地址会自动重定向到该地址);failureKeyAttribute 指定登录失败时的 request 属性 key(默认 shiroLoginFailure);这样可以在登录表单得到该错误 key 显示相应的错误消息;

Shiro jsp标签

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

导入标签库

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>


标签库定义在 shiro jar 包下的 META-INF/shiro.tld 中定义。

<!-- 用户没有登录时显示 -->
<shiro:guest>
欢迎游客访问,<a href="/login.jsp">登录</a>
</shiro:guest>

<!-- 用户通过身份验证/记住我登录时显示 -->
<shiro:user>
欢迎[<shiro:principal/>]登录,<a href="shiro/logout">点击退出</a><br/>
</shiro:user>

<!-- 用户通过身份验证时显示 -->
<shiro:authenticated>
用户[<shiro:principal/>]已身份验证通过<br/>
</shiro:authenticated> 

<!-- 用户通过记住我登录时显示 -->
<shiro:notAuthenticated>
未身份验证(包括记住我)
</shiro:notAuthenticated>

<!-- 拥有admin角色时显示 -->
<shiro:hasRole name="admin">
用户[<shiro:principal/>]拥有角色admin<br/>
</shiro:hasRole>

<!-- 拥有任意一角色时显示 -->
<shiro:hasAnyRoles name="admin,user">
用户[<shiro:principal/>]拥有角色admin或user<br/>
</shiro:hasAnyRoles>

<!-- 没有guest角色时显示 -->
<shiro:lacksRole name="guest">
用户[<shiro:principal/>]没有角色guest<br/>
</shiro:lacksRole>

<!-- 拥有user:create权限时显示 -->
<shiro:hasPermission name="user:create">
用户[<shiro:principal/>]拥有权限user:create<br/>
</shiro:hasPermission>

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

<!--默认调用 Subject.getPrincipal() 获取,即 Primary Principal  -->
<shiro:principal/>
<!-- 相当于 Subject.getPrincipals().oneByType(String.class) -->
<shiro:principal type="java.lang.String"/>
<!-- 相当于 ((User)Subject.getPrincipals()).getUsername() -->
<shiro:principal property="username"/>


会话管理

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

SessionAPI

//获取主体
Subject subject = SecurityUtils.getSubject();
//获取Session
Session session = subject.getSession();
//获取当前会话的唯一标识
session.getId();
//获取当前Subject的主机地址
session.getHost();
//设置/获取当前Session过期时间
session.setTimeout(100);
session.getTimeout();
//获取会话的启动时间及最后访问时间,JavaSE自己定期调用session.touch更新最后访问时间
session.getStartTimestamp();
session.getLastAccessTime();
//更新会话最后访问时间及销毁会话
session.touch();
session.stop();
//设置/获取/删除会话属性及获取属性Key的集合
session.setAttribute("key", "value");
session.getAttribute("");
session.removeAttribute("key");
session.getAttributeKeys();


会话管理器



Shiro提供了三种默认实现:

DefaultSessionManager
:DefaultSecurityManager 使用的默认实现,用于 JavaSE 环境;

ServletContainerSessionManager
:DefaultWebSecurityManager 使用的默认实现,用于 Web 环境,其直接使用 Servlet 容器的会话;

DefaultWebSessionManager
:用于 Web 环境的实现,可以替代 ServletContainerSessionManager,自己维护着会话,直接废弃了 Servlet 容器的会话管理。

INI配置:

[main]
#JavaSE环境下
sessionManager=org.apache.shiro.session.mgt.DefaultSessionManager
securityManager.sessionManager=$sessionManager

#JavaWeb环境下
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie
sessionIdCookie.name=sid
\#sessionIdCookie.domain=
\#sessionIdCookie.path=
sessionIdCookie.maxAge=1800
sessionIdCookie.httpOnly=true
sessionManager.sessionIdCookie=$sessionIdCookie
sessionManager.sessionIdCookieEnabled=true
sessionManager.globalSessionTimeout=1800000
securityManager.sessionManager=$sessionManager
securityManager.sessionManager=$sessionManager


sessionIdCookie 是 sessionManager 创建会话 Cookie 的模板:

sessionIdCookie.name:设置 Cookie 名字,默认为 JSESSIONID;

sessionIdCookie.domain:设置 Cookie 的域名,默认空,即当前访问的域名;

sessionIdCookie.path:设置 Cookie 的路径,默认空,即存储在域名根下;

sessionIdCookie.maxAge:设置 Cookie 的过期时间,秒为单位,默认 - 1 表示关闭浏览器时过期 Cookie;

sessionIdCookie.httpOnly:如果设置为 true,则客户端不会暴露给客户端脚本代码,使用 HttpOnly cookie 有助于减少某些类型的跨站点脚本攻击;此特性需要实现了 Servlet 2.5 MR6 及以上版本的规范的 Servlet容器支持;

sessionManager.sessionIdCookieEnabled:是否启用 / 禁用 Session Id Cookie,默认是启用的;如果禁用后将不会设置 Session Id Cookie,即默认使用了 Servlet 容器的JSESSIONID,且通过 URL 重写(URL 中的 “;JSESSIONID=id” 部分)保存 Session Id;

sessionManager.globalSessionTimeout:设置会话的全局过期时间。

对于使用ServletContainerSessionManager进行会话管理,Session的超时依赖于底层Servlet容器的超时时间,可以在web.xml中配置

<session-config>
<!--30分钟超时-->
<session-timeout>30</session-timeout>
</session-config>


会话监听器

会话监听器用于监听会话的创建、过期及停止事件,有二种方式:

实现SessionListener接口

package com.shiro.listener;
public class MySessionListener1 implements SessionListener {
@Override
public void onStart(Session session) {//会话创建时触发
System.out.println("会话创建:" + session.getId());
}
@Override
public void onExpiration(Session session) {//会话过期时触发
System.out.println("会话过期:" + session.getId());
}
@Override
public void onStop(Session session) {//退出/会话过期时触发
System.out.println("会话停止:" + session.getId());
}
}


如果只想监听某一个事件,可以继承SessionListenerAdapter,重写其中的某一个方法。

public class MySessionListener2 extends SessionListenerAdapter {
@Override
public void onStart(Session session) {
System.out.println("会话创建:" + session.getId());
}
}


在INI配置文件中配置

sessionListener1=com.shiro.listener.MySessionListener1
sessionListener2=com.shiro.listener.MySessionListener2
SessionManager.sessionListeners=$sessionListener1,$sessionListener2


会话存储/持久化

Shiro 提供 SessionDAO 用于会话的 CRUD,即 DAO(Data Access Object)模式实现:



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

使用EhCache缓存进行会话存储

sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
#设置Session缓存名字,默认就是shiro-activeSessionCache
sessionDAO.activeSessionsCacheName=shiro-activeSessionCache
#设置生成会话ID,默认是JavaUuidSessionIdGenerator,使用java.util.UUID生成
sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator
sessionDAO.sessionIdGenerator=$sessionIdGenerator
sessionManager.sessionDAO=$sessionDAO
#缓存管理器
cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager
#设置ehcache的配置文件
cacheManager.cacheManagerConfigFile=classpath:ehcache.xml
#SecurityManager的cacheManager会自动设置到实现了CacheManagerAware接口的相应对象
securityManager.cacheManager=$cacheManager


ehcache.xml

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


org.apache.shiro.session.mgt.eis.AbstractSessionDAO中实现了SessionID的实现。

//生成SessionID的方法
protected Serializable generateSessionId(Session session) {
if (this.sessionIdGenerator == null) {
String msg = "sessionIdGenerator attribute has not been configured.";
throw new IllegalStateException(msg);
}
return this.sessionIdGenerator.generateId(session);
}

//将生成的sessionID放入Session中
protected void assignSessionId(Session session, Serializable sessionId) {
((SimpleSession) session).setId(sessionId);
}


会话验证

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

sessionValidationScheduler=org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
sessionValidationScheduler.interval = 3600000
sessionValidationScheduler.sessionManager=$sessionManager
sessionManager.globalSessionTimeout=1800000
sessionManager.sessionValidationSchedulerEnabled=true
sessionManager.sessionValidationScheduler=$sessionValidationScheduler


sessionValidationScheduler:会话验证调度器,sessionManager 默认就是使用

ExecutorServiceSessionValidationScheduler,其使用 JDK 的

ScheduledExecutorService 进行定期调度并验证会话是否过期;

sessionValidationScheduler.interval:设置调度时间间隔,单位毫秒,默认就是 1 小时;

sessionValidationScheduler.sessionManager:设置会话验证调度器进行会话验证时的会话管理器;

sessionManager.globalSessionTimeout:设置全局会话超时时间,默认 30 分钟,即如果 30分钟内没有访问会话将过期;

sessionManager.sessionValidationSchedulerEnabled:是否开启会话验证器,默认是开启的;

sessionManager.sessionValidationScheduler:设置会话验证调度器,默认就是使用

ExecutorServiceSessionValidationScheduler。

缓存机制

Shiro 提供了类似于 Spring 的 Cache 抽象,即 Shiro 本身不实现 Cache,但是对 Cache 进行了又抽象,方便更换不同的底层 Cache 实现。Shiro提供了Cache和CacheManager接口,还提供了CacheManagerAware用于注入CacheManager,Shiro内部响应的组件(DefaultSecurityManager)会自动检测相应的对象(如Realm、SessionManager、SessionDAO)是否实现了CacheManagerAware并自动注入相应的CacheManager。

Realm缓存

Shiro 提供了 CachingRealm,其实现了 CacheManagerAware 接口,提供了缓存的一些基础实现;另外 AuthenticatingRealm 及 AuthorizingRealm 分别提供了对 AuthenticationInfo 和 AuthorizationInfo 信息的缓存。

INI配置

userRealm=com.shiro.realm.UserRealm
#启用缓存,默认false
userRealm.cachingEnabled=true
#启用身份验证缓存,缓存名为authenticationCache
userRealm.authenticationCachingEnabled=true
userRealm.authenticationCacheName=authenticationCache
#启用授权缓存,缓存名为authorizationCache
userRealm.authorizationCachingEnabled=true
userRealm.authorizationCacheName=authorizationCache
securityManager.realms=$userRealm
cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile=classpath:shiro-ehcache.xml
securityManager.cacheManager=$cacheManager
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: