spring与shrio集成记录

来源:互联网 发布:手机照片搞怪软件 编辑:程序博客网 时间:2024/06/03 21:37

写在前面

shrio断断续续看了有几周了,当初从jeesite项目里面知道有这么一个东东存在,然后网上看到开涛的博客,于是就跟着他的博客挑自己感兴趣的看,都是零零散散的看,虽然整合起来了,还是有一些细节我可能没有注意到,等到用到再补上就好了。

代码

pom.xml shiro部分

      <shiro.version>1.2.2</shiro.version>      <shiro.version>1.2.3</shiro.version>      1.2.2和1.2.3架包还是有点区别的,使用时候一定要注意!    <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-web</artifactId>            <version>${shiro.version}</version>        </dependency>        <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-ehcache</artifactId>            <version>${shiro.version}</version>        </dependency>            <dependency>            <groupId>org.apache.shiro</groupId>            <artifactId>shiro-quartz</artifactId>            <version>${shiro.version}</version>        </dependency>

web.xml shiro 部分

<!--Shiro对Servlet容器的FilterChain进行了代理,先执行Shiro自己的Filter链;2、再执行Servlet容器的Filter链(即原始的Filter)--><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>

spring-shiro.xml 内容

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd"    default-lazy-init="true">    <description>Shiro Configuration</description>    <!-- 【1】定义Shiro安全管理配置 -->    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">        <property name="realm" ref="systemAuthorizingRealm" />        <property name="sessionManager" ref="sessionManager" />        <property name="cacheManager" ref="shiroCacheManager" />    </bean>    <!-- 【1.1】Realm实现 -->    <bean id="systemAuthorizingRealm" class="com.msok.common.utils.SystemAuthorizingRealm">        <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>    <!-- 【1.1.1】凭证匹配器 可以自定义。利用这个将密码进行解密验证 -->    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">        <property name="hashAlgorithmName" value="md5" />        <property name="hashIterations" value="2" />    </bean>    <!-- 【1】安全认证过滤器 -->    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">        <!-- shiro的核心安全接口 -->        <property name="securityManager" ref="securityManager" /><!-- 要求登录时的链接 -->        <property name="loginUrl" value="/msok/web/login" />        <!-- 登陆成功后要跳转的连接 如果首先访问受保护 URL 登录成功,则跳转到实际访问页面 -->        <property name="successUrl" value="/msok/web/success" />        <!--可以在代码里面配置  -->        <property name="unauthorizedUrl" value="/msok/web/unauthorizedUrl" />        <property name="filters">            <map>                <!-- <entry key="cas" value-ref="casFilter"/> -->                <entry key="authc" value-ref="formAuthenticationFilter" />            </map>        </property>        <!-- shiro连接约束配置 -->        <property name="filterChainDefinitions">            <ref bean="shiroFilterChainDefinitions" />        </property>    </bean>    <!-- 加载配置属性文件 -->    <!-- Shiro权限过滤过滤器定义 authc 表示需要认证 anon 表示匿名访问(不需要认证与授权)perms[SECURITY_ACCOUNT_VIEW] 表示用户需要提供值为“SECURITY_ACCOUNT_VIEW”Permission 信息 -->    <!-- user用户拦截器,用户已经身份验证/记住我登录的都可;示例“/**=user” -->    <!-- authc基于表单的拦截器;如“/**=authc”,如果没有登录会跳到相应的登录页面登录;表单提交的用户名参数名( username); passwordParam:表单提交的密码参数名(password); rememberMeParam:表单提交的密码参数名 -->    <!-- logout退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout” -->    <!-- anon 匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon” -->    <!-- 授权相关的 -->    <!-- roles 角色授权拦截器,验证用户是否拥有所有角色;主要属性: loginUrl:登录页面地址(/login.jsp);unauthorizedUrl:未授权后重定向的地址;示例“/admin/**=roles[admin]” perms 权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例“/user/**=perms["user:create"]” -->    <bean name="shiroFilterChainDefinitions" class="java.lang.String">        <constructor-arg>            <value>                /static/** = anon                /msok/web/login = authc                /msok/web/** = user                 /gencodeplatform/gencode/** = rest[student]                <!--/logout = logout                 gencodeplatform/gencode/**=rest[test] -->            </value>        </constructor-arg>    </bean>    <!-- 基于Form表单的身份验证过滤器-->    <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">        <property name="usernameParam" value="username" />        <property name="passwordParam" value="password" />    </bean>    <!-- CAS认证过滤器 <bean id="casFilter" class="org.apache.shiro.cas.CasFilter"> <property name="failureUrl" value="${adminPath}/login"/> </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" />        <!-- 配置了这个那么sessionValidationInterval 就不用配置了 <property name="sessionValidationScheduler" ref="sessionValidationScheduler" /> -->        <property name="sessionValidationInterval" value="1800000" />        <property name="sessionDAO" ref="sessionDAO" />        <property name="sessionIdCookieEnabled" value="true" />        <!-- sessionid 生成规则 -->        <property name="sessionIdCookie" ref="sessionIdCookie" />    </bean>    <!-- 会话验证调度器 -->    <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">        <property name="sessionValidationInterval" value="1800000" />        <property name="sessionManager" ref="sessionManager" />    </bean>    <!-- 会话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>    <!-- 自定义Session存储容器  不是所有的连接都要创建session,所以我们自定义dao    <bean id="sessionDAO" class="com.msok.common.utils.CacheSessionDAO">-->    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">        <property name="sessionIdGenerator" ref="sessionIdGenerator" />        <!-- activeSessionsCache 缓存名称 -->        <property name="activeSessionsCacheName" value="activeSessionsCache" />        <property name="cacheManager" ref="shiroCacheManager" />    </bean>    <!-- 会话ID生成器 可以自定义 -->    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />    <!-- 定义授权缓存管理器 -->    <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">        <property name="cacheManager" ref="cacheManager" />    </bean>    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />    <!-- AOP式方法级权限检查 启用Shiro注解(例如,@RequiresRoles,@RequiresPermissions 等等 -->    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">        <property name="proxyTargetClass" value="true" />    </bean>    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">        <property name="securityManager" ref="securityManager" />    </bean>     <!-- 支持Shiro对Controller的方法级AOP安全控制 end --></beans>

配置说明

shiro-web 也是在web.xml里面配置filter。 它可以在spring拦截之前进行拦截处理。

shrio很多类都有默认的,我们可以继承他们然后重写我们需要的方法,这个非常重要,所以说每一个用到的类我们都要注意下

拦截器

Shiro对Servlet容器的FilterChain进行了代理,先执行Shiro自己的Filter链;2、再执行Servlet容器的Filter链(即原始的Filter)也就是说shiro的拦截器会在spring拦截之前进行拦截,拦截优先级最高。
这里写图片描述
这里写图片描述
配置内容比较多,我们一行一行来解释。
securityManager:安全管理器
loginUrl:
登陆地址,比如我们访问一个url,而这个url是处于shiro拦截的,如果这个时候该用户没有登陆,那么就会跳转到此loginUrl,默认访问方式GET,这个loginUrl 可以是一个具体的login.jsp,也可以是controller里面 某个方法映射的地址 /msok/web/login。但是我们推荐用第二种也就是方法映射的地址。因为有时候用户已经登陆过了,就没有必要再重复进行登陆了,我们可以在controller方法里面进行相关的处理。
successUrl :
是你登陆成功之后要访问的地址,如果你登陆时候 输入的地址不是loginUrl ,而是一个受保护的地址,那么登陆成功之后 就会直接跳转到这个受保护的地址。举个例子 比如loginurl=/login ,successUlr=/index 如果你在浏览器敲 /login进行登陆,那么登陆成功之后肯定是跳转到/index ,可是如果你在浏览器敲/test,那么登陆成功之后访问的就是/test。

unauthorizedUrl :
是该用户没有权限访问这个地址,跳转到的地址。如果我们在controller层增加了注解异常@ExceptionHandler({ UnauthorizedException.class }),那么这个unauthorizedUrl 也可以忽略掉

shiroFilterChainDefinitions:
shiroFilterChainDefinitions里面配置的是一些拦截地址和拦截处理对应的类。
我们需要注意的就是:
/msok/web/login = authc 这个地址必须要和loginUrl=/msok/web/login 一样,而且提交表单地址的页面也是/msok/web/login 不过方法是POST形式

<!-- 加载配置属性文件 -->    <!-- Shiro权限过滤过滤器定义 authc 表示需要认证 anon 表示匿名访问(不需要认证与授权)perms[SECURITY_ACCOUNT_VIEW] 表示用户需要提供值为“SECURITY_ACCOUNT_VIEW”Permission 信息 -->    <!-- user用户拦截器,用户已经身份验证/记住我登录的都可;示例“/**=user” -->    <!-- authc基于表单的拦截器;如“/**=authc”,如果没有登录会跳到相应的登录页面登录;表单提交的用户名参数名( username); passwordParam:表单提交的密码参数名(password); rememberMeParam:表单提交的密码参数名 -->    <!-- logout退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout” -->    <!-- anon 匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon” -->    <!-- 授权相关的 -->    <!-- roles 角色授权拦截器,验证用户是否拥有所有角色;主要属性: loginUrl:登录页面地址(/login.jsp);unauthorizedUrl:未授权后重定向的地址;示例“/admin/**=roles[admin]” perms 权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例“/user/**=perms["user:create"]” -->

formAuthenticationFilter:
登陆时候的表单验证器,我们可以重写。

认证和授权

这是一段配置认证和授权的配置文件片断。
这里写图片描述

我们在页面输入用户名和密码之后肯定要进入后台进行验证,AuthorizingRealm类就是这样的一个入口,提供验证和授权的两个方法

doGetAuthenticationInfo(AuthenticationToken token)=验证
doGetAuthorizationInfo(PrincipalCollection principals)=授权

AuthenticationToken 里面保存的用户前台输入的用户名和密码
token.getPrincipal()等于用户名,token.getCredentials()等于前台输入的密码。

后台密码一般都是进行加密的,这时候就涉及到加密解密了。shiro提供多种加密方式,具体学习地址可以参考开涛博客第五章 编码/加密——《跟我学Shiro》
我们前台传过去的密码一般都是明文,然后我们后台调用数据库查询出来加密的密码,然后利用shiro的credentialsMatcher 密码验证器,将明文按照规定的规则进行加密,再和数据库里面的进行匹配,下面的这个就是验证方法doGetAuthenticationInfo()返回的参数AuthenticationInfo。它有这几个部分组成,(用户名,密码,盐,realmName)对应SimpleAuthenticationInfo里面参数

return new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes(**salt**), getName());

也许对盐salt的概念都比较陌生,这里就又涉及到加密的一些知识点了,常见的MD5或者SHA 加密的后的密码如果比较常见可能被暴力破解掉,
如果我们在进行散列算法生成密码时候加入一些干扰元素盐,这样就不容易被逆向了。
散列规则我们可以这样弄:密码+用户名+盐+散列2次

密码+username+salt+2=md5后密码

用java代码来写就是这样

String password=new SimpleHash("md5", "密码", ByteSource.Util.bytes(salt), 2).toHex();

既然我们验证是这样写的,那么我们在注册时候肯定要将密码及盐保存到数据库里面去,salt我们可以用一个随机数

String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex();

如果为了防止暴力破解,比如密码输入五次就不让继续登陆了,我们可以在重写HashedCredentialsMatcher,也就是配置文件里面的bean为credentialsMatcher的类,然后重写doCredentialsMatch()方法,比如密码认证失败一次就放到缓存里面累加一次。具体可以参考RetryLimitHashedCredentialsMatcher.java代码。

以上说的是验证获取AuthenticationInfo的过程进行验证,shrio提供了authorizationCaching和authenticationCaching 也就是验证和授权的缓存。shiro-spring-1.2.2架包配置文件里面默认是不开启的,所以如果我们要开启就需要手动添加 可以看配置文件截图,shiro-spring-1.2.3架包里面默认是开启的。
我们看这个截图,这是进行密码验证时候首先是查询缓存里面有没有AuthenticationInfo,如果没有那就调用doGetAuthenticationInfo()方法

AuthenticatingRealm.java
这里写图片描述

密码验证成功了 那么就当然进行授权步骤了,授权步骤调用的doGetAuthorizationInfo方法,当然这里面也有缓存机制,原理看下图把
当我们手动调用检查是否有权限的时候 也会先从缓存里面查询

这里写图片描述

这里写图片描述

这里写图片描述

doGetAuthorizationInfo里面主要是进行授权功能的,在这个方法里面,我们需要调用数据库,查询改用户有哪些角色和权限,然后放到SimpleAuthorizationInfo 里面去。

SimpleAuthorizationInfo sai = new SimpleAuthorizationInfo();sai.setStringPermissions(permissionsSet);

以下截图是模拟查询数据库,获取相应按钮角色和权限。
这里写图片描述

会话管理

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

会话管理器有以下几个部分组成,下图ref的部分,也就是
会话监听器,会话监听器用于监听会话创建、过期及停止事件
会话存储/持久化(SessionDAO) ,Shiro提供SessionDAO用于会话的CRUD
会话验证(sessionValidationScheduler),Shiro提供了会话验证调度器,用于定期的验证会话是否已过期,如果过期将停止会话。可以Quartz会话验证调度器

这里写图片描述

我们也可以自定义sessiondao 然后将session放到缓存里面或者存放到数据库里面去。
这里写图片描述

Shiro安全管理器

Shiro安全管理器主要维护 realm 和sessionManager 还有通用的缓存cacheManager
这里写图片描述

shiro注解aop

配置注解这样我们在controller就可以直接在方法上面 增加权限的校验,如果没有直接权限就无法调用此方法
例如 @RequiresPermissions(value = “bb:test”)
这里写图片描述

遇到问题

退出浏览器之后,直接登录页面发现不需要登陆就可以进入,定位半天发现是cookie搞的鬼,注释掉这段就可以了。

<!-- 会话Cookie模板 -->    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">        <constructor-arg value="sid" />        <!-- 设置cookie生效时间,默认是退出浏览器cookie就消失        <property name="httpOnly" value="true" />        <property name="maxAge" value="180000" />         -->    </bean>
0 0
原创粉丝点击