shiro整合spring

来源:互联网 发布:java中常用io流类 编辑:程序博客网 时间:2024/05/21 19:29

本文是对CSDN开涛大神的shiro教程第十二章——shiro整合spring的一个整理。
Shiro是最近非常流行的安全框架,比spring security要简单一些,轻量一些。但其功能相当强大,不仅提供权限管理、还集成了单点登录、分布式session等等。
下面是shiro和spring的整合流程。
一般来说权限管理系统可以设计5张表,user表,role表,permission表,他们两两之间分别是一对多的关系,即一个用户可以拥有多个角色,一个角色可以拥有多个权限。因此还需建立user_role_relation表和role_permission
表。
SSM整合shiro首先把这五张表的dao层写好。用户量较小时,可以使用spring JDBCTemplate写dao层。
shiro提供了SimpleHash,他是一个加密工具,使用起来非常方便。

    private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();    private String algorithmName = "md5";    private int hashIterations = 2;  public void encryptPassword(User user) {        user.setSalt(randomNumberGenerator.nextBytes().toHex());/*shiro提供的simpleHash,分别赋值加密算法、密码、盐、加密循环次数。并转为16进制返回。 */        String newPassword = new SimpleHash(                algorithmName,                user.getPassword(),                //CredentialsSalt在实体类中由username+salt构成                ByteSource.Util.bytes(user.getCredentialsSalt()),                hashIterations).toHex();        user.setPassword(newPassword);    }

SimpleHash构造函数里有五个参数,分别是算法名称(这里使用的MD5),加密数据,盐,加密循环次数。这样我们在service层插入用户时,就可以使用这个encryptPassword方法将用户密码加密后插入DB了。至于service层无非也是调用dao做一些CURD的操作,不做一一阐述了。重点是下面的Realm

 @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {        /**        *@Author hfismyangel@163.com        *@Description:         * 保存登陆成功后的信息        *@Date: 8:46 2017/8/18           * @param principals        */        String username = (String)principals.getPrimaryPrincipal();        //简单授权信息,保存用户登录状态        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();        authorizationInfo.setRoles(userService.findRoles(username));        authorizationInfo.setStringPermissions(userService.findPermissions(username));        return authorizationInfo;    }    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        /**        *@Author hfismyangel@163.com        *@Description:         * 验证是否能登录        *@Date: 8:47 2017/8/18           * @param token        */        //直接从token里拿用户名        String username = (String)token.getPrincipal();        User user = userService.findByUsername(username);        if(user == null) {            throw new UnknownAccountException();//没找到帐号        }        if(Boolean.TRUE.equals(user.getLocked())) {            throw new LockedAccountException(); //帐号锁定        }        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(                user.getUsername(), //用户名                user.getPassword(), //密码                ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt                getName()  //realm name        );        return authenticationInfo;    }

这个Realm可以理解为对于是否登录/拥有权限做逻辑判断的一层。首先自定义一个类,继承Shiro的AuthorizingRealm父类。重写doGetAuthorizationInfo和doGetAuthenticationInfo方法即可,注意两个方法互为重载方法。一个是用于保存登陆成功后的信息,一个是用于判断是否登录。
注意第二个doGetAuthorizationInfo方法里调用的token.getPrincipal()方法,是直接从token中拿到用户名,而第一个doGetAuthorizationInfo方法里token.getPrincipal()是从PrincipalCollection通过遍历器拿到第一个用户名,PrincipalCollection 是个身份集合,保存各种身份信息,具体实现可以看源码。
最简单的代码配置就到此为止,再配置一下spring-shiro.xml的配置文件,在web.xml里配置一个shiro的过滤器就可以进行测试了。
下面是shiro与srpring整合的配置文件,原文注释写的比较清晰,不作解释了。另外最好根据这个shiro流程图来理解。
shiro工作流程

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:util="http://www.springframework.org/schema/util"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">    <!--dao层、passwordHelper类、与shiro-web无关-->    <!-- 缓存管理器 使用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实现,包含两部分AuthenticationToken和AuthenticationInfo-->    <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生成器,指定使用uuid生成-->    <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,管理session。    shiro的session都是存在缓存中的,所有会有一个sessionDAO的类EnterpriseCacheSessionDAO来做CRUD操作-->    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">        <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>        <property name="sessionIdGenerator" ref="sessionIdGenerator"/>    </bean>    <!-- 会话验证调度器 -->    <!--shiro提供了会话验证调度器,用于定期的验证会话是否已过期,如果过期将停止会话;出于性能考虑,    一般情况下都是获取会话时来验证会话是否过期并停止会话的;但是如在web环境中,如果用户不主动退出是不知道会话是否过期的,    因此需要定期的检测会话是否过期,Shiro提供了会话验证调度器SessionValidationScheduler。-->    <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">        <!--设置调度时间间隔,单位毫秒,默认是1小时-->        <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"/>        <!--存储sessionID的jsessionId-->        <property name="sessionIdCookie" ref="sessionIdCookie"/>    </bean>    <!-- 安全管理器,安全框架的核心组件,统筹realm/sessionManager/cache-->    <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>    <!-- 通过MethodInvokingFactoryBean工厂Bean,可以将指定方法返回值注入成为目标Bean的属性值。     将SecurityUtils的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"/>    <!--==============================================================================================================-->    <!-- 基于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">            <!--        ①[urls]部分的配置,其格式为:url=拦截器[参数],拦截器[参数]。     ②如果当前请求的url匹配[urls]部分的某个url模式,将会执行器配置的拦截器。     ③anon(anonymous)拦截器表示可以匿名访问。     ④authc(authencation)拦截器标识需要身份认证通过后才能访问。     ⑤logout(logout)拦截器表示登出/退出登录,它会清空shiro缓存信息。        ⑥url模式使用 Ant 风格模式:          [1]?匹配一个字符;          [2]*匹配零个或多个字符;          [3]**匹配路径中的零个或多个路径;-->            <value>                /index.jsp = anon                /unauthorized.jsp = anon                /login.jsp = authc                /logout = logout                /** = user            </value>        </property>    </bean></beans>
原创粉丝点击