Shiro 安全权限框架整合学习



Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。


一、将shiro的依赖包增加到项目pom.xml文件  目前使用的版本是 1.3.2

    <!--shiro包-->

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.3.2</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>

二、在web.xml文件增加shiro 的监听配置

<!--shiro配置,要放在mvc前面,如果有sitemesh,放在sitemesh前面-->
<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-application.xml 配置文件中引入shiro-config.xml配置文件

<!--shiro权限管理配置 -->
<import  resource="classpath:shiro-config.xml" />

shiro-config.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx.xsd ">

    <!-- web.xml中shiro的filter对应的bean -->
    <!-- Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
         <property name="loginUrl" value="/login" />
        <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
        <property name="successUrl" value="/"/>
        <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
       <property name="unauthorizedUrl" value="/refuse" />
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter" />
                <entry key="logout" value-ref="logoutFilter" />
            </map>
        </property>
        <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
       <property name="filterChainDefinitions">
            <value>
                <!--开发前端库全部开放
                (虽然在容器中用defaultServlet中处理过,但这里是filter,在servlet之前,还是需要指定这些目录不经验证)-->
                 /favicon.ico = anon
                /lib/** = anon
                /js/** = anon
                /css/** = anon
                /img/** = anon
                <!--退出登录接口-->
               /logout = logout
                <!--admin的url必须角色admin才能访问-->
              /admin/** = roles[admin]
                <!--其它url将全部需要登录后才可以访问-->
               /** = authc
            </value>
        </property>
    </bean>

    <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"/>
    </bean>

    <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
        <!-- 退出登录后跳转的页面,默认将跳回登录页面 -->
<property name="redirectUrl" value="/login" />
    </bean>

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

    <!--
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true"/>
    </bean> -->
    <!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="defaultRealm"/>
        <!-- 注入缓存管理器 -->
<property name="cacheManager" ref="cacheManager"/>
        <!-- 注入session管理器 -->
<property name="sessionManager" ref="sessionManager" />
    </bean>

    <!-- 默认realm,通过帐号密码进行验证登录 -->
<bean id="defaultRealm" class="com.mywind.eemp.utils.shiro.realm.DefaultRealm">
        <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
<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>

    <!-- 凭证匹配器 -->
<bean id="credentialsMatcher"
class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <!--使用md5进行密码加密-->
<property name="hashAlgorithmName" value="md5" />
        <!--加密两次-->
<property name="hashIterations" value="2" />
    </bean>


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

    <!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- session的失效时长,单位毫秒 -->
<property name="globalSessionTimeout" value="6000000"/>
        <!-- 删除失效的session -->
<property name="deleteInvalidSessions" value="true"/>
    </bean>

    <!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="rememberMeCookie" />
    </bean>

    <!-- 记住我cookie -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <!-- rememberMe是cookie的名字 -->
<constructor-arg value="rememberMe" />
        <!-- 记住我cookie生效时间30天 -->
<property name="maxAge" value="2592000" />
    </bean>

    <!-- 开启Shiro注解的Spring配置方式的beans。在lifecycleBeanPostProcessor之后运行 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" />
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>


</beans>

 这里缓存管理使用 了 shiro-ehcache.xml

 <?xml version="1.0" encoding="UTF-8"?>

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!--diskStore:缓存数据持久化的目录 地址  -->
<diskStore path="D:/WorkSpace/JAVA/ehcache" />
    <defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

 四、在shrio-config.xml配置中,自己实现默认的认证授权类。

<bean id="defaultRealm" class="com.mywind.eemp.utils.shiro.realm.DefaultRealm">
DefaultRealm.java 如下:
package com.mywind.eemp.utils.shiro.realm;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.mywind.eemp.beans.entity.SysMenuPermission;
import com.mywind.eemp.beans.entity.SysRole;
import com.mywind.eemp.beans.entity.SysUser;
import com.mywind.eemp.enums.SysUserStatusEnum;
import com.mywind.eemp.service.custom.SysRolesPermissionService;
import com.mywind.eemp.service.custom.SysUserRolesService;
import com.mywind.eemp.service.impl.SysUserServiceImpl;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

/**
 * 实现默认的登录认证,和认证通过后的授权方法
 */
public class DefaultRealm extends AuthorizingRealm {

    @Autowired
private SysUserServiceImpl sysUserService;

    @Autowired
private SysUserRolesService sysUserRolesService;

    @Autowired
private SysRolesPermissionService sysRolesPermissionService;


    //认证后的授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SysUser sysUser = (SysUser) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        Set<String> rolesString = new HashSet<>();
        Set<String> permissionString = new HashSet<>();

        //把用户所有的角色查找出来,转化为Set<String>,String为角色名称,并set到AuthorizationInfo中
List<SysRole> roleList = sysUserRolesService.selectRolesBySysUserId(sysUser.getId());

        for (SysRole sysRole : roleList) {
            rolesString.add(sysRole.getMatchString());
        }
        info.setRoles(rolesString);

        //同时把所有这些角色的权限搜索出来,并set到AuthorizationInfo中
List<SysMenuPermission> permissionList = sysRolesPermissionService.selectPermissionsBySysRoles(roleList);
        for (SysMenuPermission sysMenuPermission : permissionList) {
            permissionString.add(sysMenuPermission.getMatchString());
        }
        info.setStringPermissions(permissionString);

        return info;
    }


    //realm的认证方法,从数据库查询用户信息
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String loginName = (String) authenticationToken.getPrincipal();

        SysUser sysUser = sysUserService.selectOne(
                new EntityWrapper<SysUser>().eq("login_name", loginName)
        );

        if(sysUser == null) {
            throw new UnknownAccountException(); //没找到帐号
}

        if(sysUser.getIsLock().equals(SysUserStatusEnum.LOCK)) {
            throw new LockedAccountException(); //帐号锁定
}



        //第一个参数建议传SysUser对象进去,后面获取权限的时候可以通过对象获取各种信息,不用去数据库再查一次
SimpleAuthenticationInfo simpleAuthenticationInfo =
                new SimpleAuthenticationInfo(
                        sysUser,
                        sysUser.getPassword(),
                        ByteSource.Util.bytes(sysUser.getSalt()),
                        this.getName()
                );
        return simpleAuthenticationInfo;
    }
}



五、在shrio-config.xml配置文件中filter 指定了登录的入口方法

<property name="loginUrl" value="/login" />

 在controller 的login方法,即可以进行登录认证的结果判定。

@Controller
public class LoginController {

    @RequestMapping(value = "/login")
    public String loginPage(HttpServletRequest request, Model model) {
         //如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
        String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
        String error = "";


        //根据shiro返回的异常类路径判断,抛出指定异常信息
        if(exceptionClassName!=null){
            if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
                error = "找不到该用户";
            } else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
                error = "用户名/密码错误";
            } else if(LockedAccountException.class.getName().equals(exceptionClassName)){
                error = "你的帐号被锁定了,请稍后再试。";
            } else if(exceptionClassName != null){
                error = "其他错误:" + exceptionClassName;
            }
        }

        model.addAttribute("error", error);
        //这个login方法当登录成功后不做跳转处理,跳转处理已经由shiro来进行处理
        return "login";
    }
}

六、shiro权限标签在页面的使用:

      首先导入 权限标签库:<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

    标签:hasPermission 表示拥有某个权限  hasRole 表示拥有某个角色 shiro:user标签表示已经登录的用户属性 

        hasAllRoles表示拥有所有相关的角色;hasAllPermissions表示拥有所有相关的权限;hasAnyPermissions表示拥有任意一个相关的权限。

 
<div class="mywp-header clearfix"><div class="mywp-logo"><h1><a href="">logo</a></h1></div><div class="site-menu"><div class="nav"><div class="overTop"></div><div class="overBottom"></div><a href="#" class="active">首页</a><shiro:hasPermission name="monitor"><a href="#">xx</a></shiro:hasPermission><shiro:hasPermission name="powerAnalysis"><a href="#">综合分析</a></shiro:hasPermission><shiro:hasPermission name="reportManager"><a href="#">报表管理</a></shiro:hasPermission><shiro:hasPermission name="generalSearch"><a href="#">故障告警</a></shiro:hasPermission><shiro:hasRole name="admin"><a href="admin/list">系统配置</a></shiro:hasRole></div><div class="action"><shiro:user>
                <a href="#"><span class="mywp-icon icon-admin"></span>您好,<shiro:principal  property="displayName"/></a>
            </shiro:user> 
</div><div class="exitSystem"><a href="#" id="logout_button"><span class="mywp-icon icon-exit"></span></a></div></div></div>

 至此,从引入包到配置xml。到实现默认认证和授权,页面标签使用完整可以使用。

猜你喜欢

转载自zifeng412708.iteye.com/blog/2386174