Java框架之shiro(02)支持特性

web 集成

  • Shiro通过一个ShiroFilter入口来拦截需要安全控制的URL,读取配置(如ini配置文件),然后判断URL是否需要登录/权限等

一、环境准备

<dependency>  
    <groupId>javax.servlet</groupId>  
    <artifactId>javax.servlet-api</artifactId>  
    <version>3.0.1</version>  
    <scope>provided</scope>  
</dependency> 

<dependency>  
    <groupId>org.apache.shiro</groupId>  
    <artifactId>shiro-web</artifactId>  
    <version>1.2.2</version>  
</dependency> 

<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <path>/${project.build.finalName}</path>
    </configuration>
</plugin>

二、ShiroFilter入口

  • Shiro 1.2及以后版本的配置方式
<!--- shiro 1.2 -->
<listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>shiroEnvironmentClass</param-name>
    <param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value>
    <!-- 默认先从/WEB-INF/shiro.ini,如果没有找classpath:shiro.ini -->
</context-param>
<context-param>
    <param-name>shiroConfigLocations</param-name>
    <param-value>classpath:shiro-basicfilterlogin.ini</param-value>
</context-param>
<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配置

  • 相比较于之前的配置多了 urls 这一块的内容
  • [urls]部分的配置格式: url=拦截器[参数],拦截器[参数]
    • 即如果当前请求的url匹配[urls]部分的某个url模式,将会执行其配置的拦截器;
    • anon拦截器表示匿名访问(即不需要登录即可访问);
    • authc拦截器表示需要身份认证通过后才能访问;
    • roles[admin]拦截器表示需要有admin角色授权才能访问;
    • perms[“user:create”]拦截器表示需要有“user:create”权限才能访问
  • main 区域还配置了拦截器的一些属性
  • url部分的写法支持通配符
    • ?:匹配一个字符,如”/admin?”将匹配/admin1,但不匹配/admin或/admin2;
    • *:匹配零个或多个字符串,如/admin*将匹配/admin、/admin123,但不匹配/admin/1;
    • **:匹配路径中的零个或多个路径,如/admin/**将匹配/admin/a或/admin/a/b;
  • 匹配拦截器的顺序是按拦截器的声明顺序,第一个匹配的就生效
[main]  
#默认是/login.jsp  
authc.loginUrl=/login  
roles.unauthorizedUrl=/unauthorized  
perms.unauthorizedUrl=/unauthorized  

[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"]   

1、身份验证

  • 基本的特点就是:如果 URL 配置了拦截器,就是先拦截器再相应的应答的 servlet,不然的话就直接由 servlet 来处理请求了
基于Basic的拦截器身份验证
  • 访问 /role这个 URL 的时候,如果没有验证身份就会弹出一个登录框,登录之后即可访问(实际还要走 roles 拦截器)
  • authcBasic.applicationName 配置就是那个登录框的一个提示信息
[main]  
authcBasic.applicationName=please login  

[urls]  
/role=authcBasic,roles[admin]  
基于表单的拦截器身份验证
  • successUrl指定登录成功后重定向的默认地址(默认是/,如果有上一个地址会自动重定向带该地址);
  • failureKeyAttribute指定登录失败时的request属性key(默认shiroLoginFailure);
[main]  
authc.loginUrl=/formfilterlogin  
authc.usernameParam=username  
authc.passwordParam=password  
authc.successUrl=/  
authc.failureKeyAttribute=shiroLoginFailure  
  
[urls]  
/role=authc,roles[admin]   

2、授权(角色和权限验证)

  • 授权之前先要身份验证
  • 实际上加了授权的拦截器之后,就不用在代码里写 check*** 来检查角色或者权限了
[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]
/logout2=logout
/login=anon
/logout=anon
/unauthorized=anon
/static/**=anon
/authenticated=authc
/role=authc,roles[admin]
/permission=authc,perms["user:create"]

3、登出

  • 除了我们可以在 servlet 里面写代码退出之外,还可以利用 logout 拦截器来实现登出,这样我们可以少些一个 servlet
  • 通过 logout 拦截器的 redirectUrl 属性,我们还可以指定登出后的重定向地址,十分方便
[main]  
logout.redirectUrl=/login  
  
[urls]  
/logout2=logout  

示例源代码:跟我一起学 shiro 作者的源码参考


web集成补充:拦截器机制

一、拦截器介绍

filter继承体系

1、各种 Filter 的介绍

NameableFilter
  • NameableFilter给Filter起个名字,如果没有设置默认就是FilterName,之前ini文件里面配置的 authc等就是filter的名字
OncePerRequestFilter
  • OncePerRequestFilter用于防止多次执行Filter的,也就是说一次请求只会走一次拦截器链;
ShiroFilter
  • shiro的入口,拦截需要安全控制的请求;
AdviceFilter
  • AdviceFilter提供了AOP风格的支持,类似于SpringMVC中的Interceptor
  • 其中主要有三个方法
    • preHandle:进行请求的预处理,返回 true继续执行拦截器链,可以通过它实现权限控制;
    • postHandle:执行完拦截器链之后正常返回后执行;
    • afterCompletion:不管最后有没有异常,afterCompletion都会执行,完成如清理资源功能
PathMatchingFilter
  • PathMatchingFilter提供了基于Ant风格的请求路径匹配功能及拦截器参数解析的功能
    • pathsMatch:用于URL模式和请求路径进行匹配
    • preHandle:调用pathsMatch进行url模式与请求url进行匹配,如果匹配会调用onPreHandle,不匹配直接返回true
    • onPreHandle:如果url模式与请求url匹配,那么会执行onPreHandle,并把该拦截器配置参数传入
AccessControlFilter
  • AccessControlFilter提供了访问控制的基础功能;比如是否允许访问/当访问拒绝时如何处理等
    • isAccessAllowed:即是否允许访问,返回true表示允许;
    • onAccessDenied:表示访问拒绝时是否自己处理,如果返回true表示自己不处理且继续拦截器链执行,返回false表示自己已经处理了(比如重定向到另一个页面)

2、总结

  • 如果我们想进行访问访问的控制就可以继承AccessControlFilter
  • 如果我们要添加一些通用数据我们可以直接继承PathMatchingFilter
  • 以上的几个 Filter 全部是 Shiro 提供的 API 实现,都是最终继承于 servlet 里面的Filter接口

二、拦截器链

  • Shiro通过ProxiedFilterChain对Servlet容器的FilterChain进行了代理,所以先走Shiro自己的Filter体系,然后才是 servlet 的Filter 体系;
  • Shiro内部提供了一个FilterChainResolver的默认实现PathMatchingFilterChainResolver,其根据[urls]中配置和请求的url是否匹配来解析得到拦截器链的;
  • PathMatchingFilterChainResolver内部通过FilterChainManager维护拦截器链,可以通过FilterChainManager进行动态增加url模式与拦截器链的关系,具体实现DefaultFilterChainManager会自动添加DefaultFilter中声明的拦截器
public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);
}
//定制FilterChainResolver或FilterChainManager来完成动态URL匹配的实现
public class MyIniWebEnvironment extends IniWebEnvironment {
    @Override
    protected FilterChainResolver createFilterChainResolver() {
			//1、创建FilterChainResolver
			PathMatchingFilterChainResolver filterChainResolver =
					new PathMatchingFilterChainResolver();
			//2、创建FilterChainManager
			DefaultFilterChainManager filterChainManager = new DefaultFilterChainManager();
			//3、注册Filter
			for(DefaultFilter filter : DefaultFilter.values()) {
				filterChainManager.addFilter(
					filter.name(), (Filter) ClassUtils.newInstance(filter.getFilterClass()));
			}
			//4、注册URL-Filter的映射关系
			filterChainManager.addToChain("/login.jsp", "authc");
			filterChainManager.addToChain("/unauthorized.jsp", "anon");
			filterChainManager.addToChain("/**", "authc");
			filterChainManager.addToChain("/**", "roles", "admin");

			//5、设置Filter的属性
			FormAuthenticationFilter authcFilter =
					 (FormAuthenticationFilter)filterChainManager.getFilter("authc");
			authcFilter.setLoginUrl("/login.jsp");
			RolesAuthorizationFilter rolesFilter =
					  (RolesAuthorizationFilter)filterChainManager.getFilter("roles");
			rolesFilter.setUnauthorizedUrl("/unauthorized.jsp");

			filterChainResolver.setFilterChainManager(filterChainManager);
			return filterChainResolver;
    }
}
<!--web xml 中需要配置环境-->
<context-param>  
	<param-name>shiroEnvironmentClass</param-name> 
	<param-value>com.github.zhangkaitao.shiro.chapter8.web.env.MyIniWebEnvironment</param-value>  
</context-param>   

三、自定义拦截器

  • 通过自定义自己的拦截器可以扩展一些功能
  • 自定义拦截器通常是通过继承shiro已有的 Filter 的实现来完成
  • 自定义拦截器需要在 ini 配置文件中的 filters 或者 main 的区域进行注册

1、自定义表单登录拦截器

  • 通过继承AuthenticatingFilter实现是更好的选择,其提供了很多登录相关的基础代码
public class FormLoginFilter extends PathMatchingFilter {
    private String loginUrl = "/login.jsp";
    private String successUrl = "/";
    @Override
    protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        if(SecurityUtils.getSubject().isAuthenticated()) {
            return true;//已经登录过
        }
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if(isLoginRequest(req)) {
            if("post".equalsIgnoreCase(req.getMethod())) {//form表单提交
                boolean loginSuccess = login(req); //登录
                if(loginSuccess) {
                    return redirectToSuccessUrl(req, resp);
                }
            }
            return true;//继续过滤器链
        } else {//保存当前地址并重定向到登录界面
            saveRequestAndRedirectToLogin(req, resp);
            return false;
        }
    }
    private boolean redirectToSuccessUrl(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        WebUtils.redirectToSavedRequest(req, resp, successUrl);
        return false;
    }
    private void saveRequestAndRedirectToLogin(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        WebUtils.saveRequest(req);
        WebUtils.issueRedirect(req, resp, loginUrl);
    }

    private boolean login(HttpServletRequest req) {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        try {
            SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password));
        } catch (Exception e) {
            req.setAttribute("shiroLoginFailure", e.getClass());
            return false;
        }
        return true;
    }
    private boolean isLoginRequest(HttpServletRequest req) {
        return pathsMatch(loginUrl, WebUtils.getPathWithinApplication(req));
    }
}

2、自定义任意角色授权拦截器

  • 继承AuthorizationFilter实现更好,其提供了授权相关的基础代码
public class AnyRolesFilter extends AccessControlFilter {
    private String unauthorizedUrl = "/unauthorized.jsp";
    private String loginUrl = "/login.jsp";
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        String[] roles = (String[])mappedValue;
        if(roles == null) {
            return true;//如果没有设置角色参数,默认成功
        }
        for(String role : roles) {
            if(getSubject(request, response).hasRole(role)) {
                return true;
            }
        }
        return false;//跳到onAccessDenied处理
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        Subject subject = getSubject(request, response);
        if (subject.getPrincipal() == null) {//表示没有登录,重定向到登录页面
            saveRequest(request);
            WebUtils.issueRedirect(request, response, loginUrl);
        } else {
            if (StringUtils.hasText(unauthorizedUrl)) {//如果有未授权页面跳转过去
                WebUtils.issueRedirect(request, response, unauthorizedUrl);
            } else {//否则返回401未授权状态码
                WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
            }
        }
        return false;
    }
} 

四、默认拦截器

  • Shiro内置了很多默认的拦截器,比如身份验证、授权等相关的
  • 默认拦截器会自动注册,可以直接在ini配置文件中通过拦截器名.属性设置其属性
  • 如果某个拦截器不想使用了可以直接通过如下配置直接禁用:perms.enabled=false
  • shirio中默认拦截器参考

JSP标签

  • Shiro提供了JSTL标签用于在JSP/GSP页面进行权限控制,如根据登录用户显示相应的页面按钮
  • 使用前先导入标签库:<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

一、各种 shiro 标签使用

用maven创建web工程来测试这些 shiro标签的时候需要注意:
  • maven 最重要的 POM 一定要配置好,主要是各种依赖和 tomcat 插件
  • web 的web xml 文件一定要配置好,主要是shiro环境配置和shiroFilter 的配置
  • shiro使用一定要把shiro 的ini配置文件弄好
  • 还有相当重要的一点,我最开始测试的时候地址已输入就报错根本无法进入默认的jsp页面,后来多方查询注释掉了 POM 中的 jsp 和 servlet 的依赖才正常了,原因好像是添加了这两个依赖的话就和 tomcat 插件里的 API 冲突了
<p>
    <shiro:guest>
        欢迎游客访问,<a href="${pageContext.request.contextPath}/login.jsp">登录</a>
    </shiro:guest>
</p>

<p>
    <shiro:user>
        欢迎[<shiro:principal/>]登录,<a href="${pageContext.request.contextPath}/logout">退出</a>
        <%--<shiro:principal type="java.lang.String"/>
        相当于Subject.getPrincipals().oneByType(String.class)--%>
        <%--<shiro:principal property="username"/>
        相当于((User)Subject.getPrincipals()).getUsername()--%>
    </shiro:user>
</p>

<p>
    <shiro:authenticated>
        用户[<shiro:principal/>]已身份验证通过
    </shiro:authenticated>
</p>

<p>
    <shiro:notAuthenticated>
        未身份验证(包括记住我)
    </shiro:notAuthenticated>
</p>

<p>
    <shiro:hasRole name="admin">
        用户[<shiro:principal/>]拥有角色admin<br/>
    </shiro:hasRole>
</p>

<p>
    <shiro:hasAnyRoles name="admin,user">
        用户[<shiro:principal/>]拥有角色admin或user<br/>
    </shiro:hasAnyRoles>
</p>

<p>
    <shiro:lacksRole name="abc">
        用户[<shiro:principal/>]没有角色abc<br/>
    </shiro:lacksRole>
</p>

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

二、使用自定义标签

  • 引入自定义标签库:<%@taglib prefix="stan" tagdir="/WEB-INF/tags" %>

1、标签定义

  • 自定义 hasAllRoles.tag 标签示例
<%@ tag import="org.apache.shiro.SecurityUtils" %>
<%@ tag import="org.apache.shiro.util.StringUtils" %>
<%@ tag import="java.util.Arrays" %>
<%@ tag pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%@ attribute name="name" type="java.lang.String" required="true" description="角色列表" %>
<%@ attribute name="delimiter" type="java.lang.String" required="false" description="角色列表分隔符" %>
<%

    if (!StringUtils.hasText(delimiter)) {
        delimiter = ",";//默认逗号分隔
    }

    if (!StringUtils.hasText(name)) {
%>
<jsp:doBody/>
<%
        return;
    }

    String[] roles = name.split(delimiter);

    if (!SecurityUtils.getSubject().hasAllRoles(Arrays.asList(roles))) {
        return;
    } else {
%>
<jsp:doBody/>
<%
    }
%>

2、标签使用

<h2>自定义的标签</h2>
<P>
    <stan:hasAllRoles name="admin,user">
        用户[<shiro:principal/>]拥有角色admin和user<br/>
    </stan:hasAllRoles>
    <br>
    <stan:hasAllPermissions name="user:create,user:update">
        用户[<shiro:principal/>]拥有权限user:create和user:update<br/>
    </stan:hasAllPermissions>
    <br>
    <stan:hasAnyPermissions name="user:create,abc:update">
        用户[<shiro:principal/>]拥有权限user:create或abc:update<br/>
    </stan:hasAnyPermissions>
</P>

缓存机制

  • shiro 提供了 Cache、CacheManager、CacheManagerAware 接口
public interface Cache<K, V> {  
    //根据Key获取缓存中的值  
    public V get(K key) throws CacheException;  
    //往缓存中放入key-value,返回缓存中之前的值  
    public V put(K key, V value) throws CacheException;   
    //移除缓存中key对应的值,返回该值  
    public V remove(K key) throws CacheException;  
    //清空整个缓存  
    public void clear() throws CacheException;  
    //返回缓存大小  
    public int size();  
    //获取缓存中所有的key  
    public Set<K> keys();  
    //获取缓存中所有的value  
    public Collection<V> values();  
}  

public interface CacheManager {  
    //根据缓存名字获取一个Cache  
    public <K, V> Cache<K, V> getCache(String name) throws CacheException;  
}  

public interface CacheManagerAware {  
    //注入CacheManager  
    void setCacheManager(CacheManager cacheManager);  
} 

一、Realm 缓存

  • Shiro提供了CachingRealm,其实现了CacheManagerAware接口,提供了缓存的一些基础实现
  • AuthenticatingRealm及AuthorizingRealm都继承了CachingRealm,分别提供了对AuthenticationInfo 和AuthorizationInfo信息的缓存
  • 在进行身份验证和授权的时候,都是先从缓存里面取,没有再去查询数据库
  • Realm缓存带来的问题就是如果我们的用户已经完成了认证和授权并且相应的信息被缓存了,那么一旦认证信息发生变化或授权信息发生改变,就可能导致用户认证失败和授权失败,这是需要手动清空缓存,感觉比较麻烦
ini配置文件开启 Realm 缓存
userRealm=com.github.zhangkaitao.shiro.chapter11.realm.UserRealm  
userRealm.credentialsMatcher=$credentialsMatcher  
userRealm.cachingEnabled=true  
userRealm.authenticationCachingEnabled=true  
userRealm.authenticationCacheName=authenticationCache  
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 
测试代码
@Test  
public void testClearCachedAuthenticationInfo() {  
    login(u1.getUsername(), password);  
    userService.changePassword(u1.getId(), password + "1");  
  
    RealmSecurityManager securityManager =  
     (RealmSecurityManager) SecurityUtils.getSecurityManager();  
    UserRealm userRealm = (UserRealm) securityManager.getRealms().iterator().next();  
    userRealm.clearCachedAuthenticationInfo(subject().getPrincipals());  
  
    login(u1.getUsername(), password + "1");  
}  

二、session 缓存

  • securityManager实现了SessionsSecurityManager,其会自动判断SessionManager是否实现了CacheManagerAware接口,如果实现了会把CacheManager设置给它。
  • sessionManager会判断相应的sessionDAO(如继承自CachingSessionDAO)是否实现了CacheManagerAware,如果实现了会把CacheManager设置给它

以上的缓存都需要 ehcache 的支持,ehcache 可以配置多个,使用的时候在 ini 配置文件中指定相应的 ehcache的名称即可


与 spring 集成

  • shiro 的组件都是兼容 javaBean的,所以和Spring集成是非常容易的
  • 直接将 shiro 的 ini 配置文件转化为 spring 的 xml 配置文件,用 spring 来管理 shiro 的组件即可

一、标准应用

<!-- 缓存管理器 使用Ehcache实现 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>

<!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="
com.github.zhangkaitao.shiro.chapter12.credentials.RetryLimitHashedCredentialsMatcher">
    <constructor-arg ref="cacheManager"/>
    <property name="hashAlgorithmName" value="md5"/>
    <property name="hashIterations" value="2"/>
    <property name="storedCredentialsHexEncoded" value="true"/>
</bean>

<!-- Realm实现 -->
<bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter12.realm.UserRealm">
    <property name="userService" ref="userService"/>
    <property name="credentialsMatcher" ref="credentialsMatcher"/>
    <property name="cachingEnabled" value="true"/>
    <property name="authenticationCachingEnabled" value="true"/>
    <property name="authenticationCacheName" value="authenticationCache"/>
    <property name="authorizationCachingEnabled" value="true"/>
    <property name="authorizationCacheName" value="authorizationCache"/>
</bean>
<!-- 会话ID生成器 -->
<bean id="sessionIdGenerator" 
class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
<!-- 会话DAO -->
<bean id="sessionDAO" 
class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
    <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
    <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
<!-- 会话验证调度器 -->
<bean id="sessionValidationScheduler" 
class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
    <property name="sessionValidationInterval" value="1800000"/>
    <property name="sessionManager" ref="sessionManager"/>
</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="sessionValidationScheduler" ref="sessionValidationScheduler"/>
    <property name="sessionDAO" ref="sessionDAO"/>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
    <property name="realms">
        <list><ref bean="userRealm"/></list>
    </property>
    <property name="sessionManager" ref="sessionManager"/>
    <property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" 
value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
    <property name="arguments" ref="securityManager"/>
</bean>
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" 
class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

二、WEB应用

  • spring-shiro xml:cookie模版、会话管理器、安全管理器、shiro 相关的过滤器要特别注意
<!-- 缓存管理器 使用Ehcache实现 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
	<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>

<!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="com.github.zhangkaitao.shiro.chapter12.credentials.RetryLimitHashedCredentialsMatcher">
	<constructor-arg ref="cacheManager"/>
	<property name="hashAlgorithmName" value="md5"/>
	<property name="hashIterations" value="2"/>
	<property name="storedCredentialsHexEncoded" value="true"/>
</bean>

<!-- Realm实现 -->
<bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter12.realm.UserRealm">
	<property name="userService" ref="userService"/>
	<property name="credentialsMatcher" ref="credentialsMatcher"/>
	<property name="cachingEnabled" value="true"/>
	<property name="authenticationCachingEnabled" value="true"/>
	<property name="authenticationCacheName" value="authenticationCache"/>
	<property name="authorizationCachingEnabled" value="true"/>
	<property name="authorizationCacheName" value="authorizationCache"/>
</bean>

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

<!-- 会话Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
	<constructor-arg value="sid"/>
	<property name="httpOnly" value="true"/>
	<property name="maxAge" value="180000"/>
</bean>

<!-- 会话DAO -->
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
	<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
	<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>

<!-- 会话验证调度器 -->
<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
	<property name="sessionValidationInterval" value="1800000"/>
	<property name="sessionManager" ref="sessionManager"/>
</bean>

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

<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
	<property name="realm" ref="userRealm"/>
	<property name="sessionManager" ref="sessionManager"/>
	<property name="cacheManager" ref="cacheManager"/>
</bean>

<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
	<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
	<property name="arguments" ref="securityManager"/>
</bean>

<!-- 基于Form表单的身份验证过滤器 -->
<bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
	<property name="usernameParam" value="username"/>
	<property name="passwordParam" value="password"/>
	<property name="loginUrl" value="/login.jsp"/>
</bean>

<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
	<property name="securityManager" ref="securityManager"/>
	<property name="loginUrl" value="/login.jsp"/>
	<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
	<property name="filters">
		<util:map>
			<entry key="authc" value-ref="formAuthenticationFilter"/>
		</util:map>
	</property>
	<property name="filterChainDefinitions">
		<value>
			/index.jsp = anon
			/unauthorized.jsp = anon
			/login.jsp = authc
			/logout = logout
			/** = user
		</value>
	</property>
</bean>

<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
  • web xml
<!-- Spring配置文件开始  -->
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		classpath:spring-beans.xml,
		classpath:spring-shiro-web.xml
	</param-value>
</context-param>
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring配置文件结束 -->

<!-- shiro 安全过滤器 这里的shiroFilter必须要能够在 spring 的IOC容器里找到-->
<filter>
	<filter-name>shiroFilter</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	<async-supported>true</async-supported>
	<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前端控制器-->
<servlet>
	<servlet-name>spring</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-mvc.xml</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
	<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
	<servlet-name>spring</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>

三、权限注解

  • shiro 的权限注解需要 spring 的 AOP 的支持,还需要开启注解驱动
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
	<property name="securityManager" ref="securityManager"/>
</bean>
<mvc:annotation-driven/>
  • springmvc 中的权限注解使用
@RequiresRoles("admin")  
@RequestMapping("/hello2")  
public String hello2() {  
    return "success";  
}  
  • 验证失败可交由 spring 的异常处理器来
@ExceptionHandler({UnauthorizedException.class})  
@ResponseStatus(HttpStatus.UNAUTHORIZED)  
public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {  
    ModelAndView mv = new ModelAndView();  
    mv.addObject("exception", e);  
    mv.setViewName("unauthorized");  
    return mv;  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值